[
  {
    "path": ".dockerignore",
    "content": "cmake-build-debug/\ncmake-build-release/\n.DS_Store\n.idea/\nbuild/\nnode_modules/"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [nicbarker]\n"
  },
  {
    "path": ".github/workflows/cmake-multi-platform.yml",
    "content": "name: CMake on multiple platforms\n\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n\njobs:\n  build:\n    runs-on: ${{ matrix.os }}\n\n    strategy:\n      # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.\n      fail-fast: false\n\n      # Set up a matrix to run the following 3 configurations:\n      # 1. <Windows, Release, latest MSVC compiler toolchain on the default runner image, default generator>\n      # 2. <Linux, Release, latest GCC compiler toolchain on the default runner image, default generator>\n      # 3. <Linux, Release, latest Clang compiler toolchain on the default runner image, default generator>\n      #\n      # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list.\n      matrix:\n        os: [ubuntu-latest, windows-latest]\n        build_type: [Release]\n        c_compiler: [gcc, clang, cl]\n        include:\n          - os: windows-latest\n            c_compiler: cl\n            cpp_compiler: cl\n          - os: ubuntu-latest\n            c_compiler: gcc\n            cpp_compiler: g++\n          - os: ubuntu-latest\n            c_compiler: clang\n            cpp_compiler: clang++\n        exclude:\n          - os: windows-latest\n            c_compiler: gcc\n          - os: windows-latest\n            c_compiler: clang\n          - os: ubuntu-latest\n            c_compiler: cl\n\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Set reusable strings\n      # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.\n      id: strings\n      shell: bash\n      run: |\n        echo \"build-output-dir=${{ github.workspace }}/build\" >> \"$GITHUB_OUTPUT\"\n\n    - name: Cache\n      uses: actions/cache@v4.2.0\n      with:\n        # A list of files, directories, and wildcard patterns to cache and restore\n        path: \"/home/runner/work/clay/clay/build/_deps\"\n        # An explicit key for restoring and saving the cache\n        key: \"_deps\"\n\n    - name: Install Dependencies\n      if: runner.os == 'Linux'\n      run: |\n        DEBIAN_FRONTEND=noninteractive sudo apt-get update -y\n        DEBIAN_FRONTEND=noninteractive sudo apt-get install -y git\n        DEBIAN_FRONTEND=noninteractive sudo apt-get install -y libwayland-dev\n        DEBIAN_FRONTEND=noninteractive sudo apt-get install -y pkg-config\n        DEBIAN_FRONTEND=noninteractive sudo apt-get install -y libxkbcommon-dev\n        DEBIAN_FRONTEND=noninteractive sudo apt-get install -y xorg-dev\n\n\n    - name: Configure CMake\n      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.\n      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type\n      run: >\n        cmake -B ${{ steps.strings.outputs.build-output-dir }}\n        -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}\n        -DCMAKE_C_COMPILER=${{ matrix.c_compiler }}\n        -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}\n        -S ${{ github.workspace }}\n\n    - name: Build\n      # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).\n      run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }}\n\n    - name: Test\n      working-directory: ${{ steps.strings.outputs.build-output-dir }}\n      # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).\n      # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail\n      run: ctest --build-config ${{ matrix.build_type }}\n"
  },
  {
    "path": ".github/workflows/odin-bindings-update.yml",
    "content": "name: Odin Bindings Update\non:\n  push:\n    branches: [main]\njobs:\n  check_changes:\n    runs-on: ubuntu-latest\n    outputs:\n      changed: ${{ steps.check_clay.outputs.changed }}\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 2\n\n      - name: Check if clay.h changed\n        id: check_clay\n        run: |\n          if git diff --name-only HEAD^ HEAD | grep -Fx \"clay.h\"; then\n            echo \"changed=true\" >> $GITHUB_OUTPUT\n          else\n            echo \"changed=false\" >> $GITHUB_OUTPUT\n          fi\n\n  build:\n    needs: check_changes\n    if: needs.check_changes.outputs.changed == 'true'\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest]\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n\n      - name: Install clang (Linux)\n        if: runner.os == 'Linux'\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y clang\n\n      - name: Build libs\n        run: |\n          mkdir -p build\n          mkdir -p artifacts\n          cp clay.h clay.c\n\n          COMMON_FLAGS=\"-DCLAY_IMPLEMENTATION -fno-ident -frandom-seed=clay\"\n\n          if [[ \"$(uname)\" == \"Linux\" ]]; then\n            mkdir -p artifacts/linux\n            mkdir -p artifacts/windows\n            mkdir -p artifacts/wasm\n\n            echo \"Building for Linux...\"\n            clang -c $COMMON_FLAGS -fPIC -ffreestanding -static -target x86_64-unknown-linux-gnu clay.c -o build/linux.o\n            ar rD artifacts/linux/clay.a build/linux.o\n\n            echo \"Building for Windows...\"\n            clang -c $COMMON_FLAGS -ffreestanding -target x86_64-pc-windows-msvc -fuse-ld=llvm-lib clay.c -o artifacts/windows/clay.lib\n\n            echo \"Building for WASM...\"\n            clang -c $COMMON_FLAGS -fPIC -target wasm32 -nostdlib -static clay.c -o artifacts/wasm/clay.o\n          elif [[ \"$(uname)\" == \"Darwin\" ]]; then\n            mkdir -p artifacts/macos\n            mkdir -p artifacts/macos-arm64\n            echo \"Building for macOS (x86_64)...\"\n            clang -c $COMMON_FLAGS -fPIC -target x86_64-apple-macos clay.c -o build/macos.o\n            libtool -static -o artifacts/macos/clay.a build/macos.o\n\n            echo \"Building for macOS (ARM64)...\"\n            clang -c $COMMON_FLAGS -fPIC -target arm64-apple-macos clay.c -o build/macos-arm64.o\n            libtool -static -o artifacts/macos-arm64/clay.a build/macos-arm64.o\n          fi\n          rm -f clay.c build/*.o\n\n      - name: Upload artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: artifacts-${{ matrix.os }}\n          path: artifacts/\n\n  commit:\n    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n\n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n\n      - name: Move artifacts\n        run: |\n          cp -r artifacts-ubuntu-latest/* bindings/odin/clay-odin/\n          cp -r artifacts-macos-latest/* bindings/odin/clay-odin/\n\n      - name: Commit/Push changes\n        run: |\n          git config user.name \"github-actions[bot]\"\n          git config user.email \"github-actions[bot]@users.noreply.github.com\"\n          git add bindings/odin/clay-odin/\n          git commit -m \"[bindings/odin] Update Odin bindings\"\n          git push\n"
  },
  {
    "path": ".gitignore",
    "content": "cmake-build-debug/\ncmake-build-release/\n.DS_Store\n.idea/\nnode_modules/\n*.dSYM\n.vs/"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(clay)\n\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake\")\n\noption(CLAY_INCLUDE_ALL_EXAMPLES \"Build all examples\" ON)\noption(CLAY_INCLUDE_DEMOS \"Build video demo and website\" OFF)\noption(CLAY_INCLUDE_CPP_EXAMPLE \"Build C++ example\" OFF)\noption(CLAY_INCLUDE_RAYLIB_EXAMPLES \"Build raylib examples\" OFF)\noption(CLAY_INCLUDE_SDL2_EXAMPLES \"Build SDL 2 examples\" OFF)\noption(CLAY_INCLUDE_SDL3_EXAMPLES \"Build SDL 3 examples\" OFF)\noption(CLAY_INCLUDE_WIN32_GDI_EXAMPLES \"Build Win32 GDI examples\" OFF)\noption(CLAY_INCLUDE_SOKOL_EXAMPLES \"Build Sokol examples\" OFF)\noption(CLAY_INCLUDE_PLAYDATE_EXAMPLES \"Build Playdate examples\" OFF)\n\nmessage(STATUS \"CLAY_INCLUDE_DEMOS: ${CLAY_INCLUDE_DEMOS}\")\n\nif(APPLE)\n  enable_language(OBJC)\nendif()\n\nif(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_CPP_EXAMPLE)\n  add_subdirectory(\"examples/cpp-project-example\")\nendif()\nif(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_DEMOS)\n  if(NOT MSVC)\n    add_subdirectory(\"examples/clay-official-website\")\n    add_subdirectory(\"examples/terminal-example\")\n  endif()\n  add_subdirectory(\"examples/introducing-clay-video-demo\")\nendif ()\n\nif(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_RAYLIB_EXAMPLES)\n  add_subdirectory(\"examples/raylib-multi-context\")\n  add_subdirectory(\"examples/raylib-sidebar-scrolling-container\")\nendif ()\nif(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL2_EXAMPLES)\n  add_subdirectory(\"examples/SDL2-video-demo\")\nendif ()\nif(NOT MSVC AND (CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL3_EXAMPLES))\n    add_subdirectory(\"examples/SDL3-simple-demo\")\nendif()\nif(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SOKOL_EXAMPLES)\n  add_subdirectory(\"examples/sokol-video-demo\")\n  add_subdirectory(\"examples/sokol-corner-radius\")\nendif()\n\n# Playdate example not included in ALL because users need to install the playdate SDK first which requires a license agreement\nif(CLAY_INCLUDE_PLAYDATE_EXAMPLES)\n  add_subdirectory(\"examples/playdate-project-example\")\nendif()\n\nif(WIN32)   # Build only for Win or Wine\n    if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_WIN32_GDI_EXAMPLES)\n        add_subdirectory(\"examples/win32_gdi\")\n    endif()\nendif()\n\n#  add_subdirectory(\"examples/cairo-pdf-rendering\") Some issue with github actions populating cairo, disable for now\n\n#add_library(${PROJECT_NAME} INTERFACE)\n#target_include_directories(${PROJECT_NAME} INTERFACE .)\n"
  },
  {
    "path": "LICENSE.md",
    "content": "zlib/libpng license\n\nCopyright (c) 2024 Nic Barker\n\nThis software is provided 'as-is', without any express or implied warranty.\nIn no event will the authors be held liable for any damages arising from the\nuse of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n    1. The origin of this software must not be misrepresented; you must not\n    claim that you wrote the original software. If you use this software in a\n    product, an acknowledgment in the product documentation would be\n    appreciated but is not required.\n\n    2. Altered source versions must be plainly marked as such, and must not\n    be misrepresented as being the original software.\n\n    3. This notice may not be removed or altered from any source\n    distribution."
  },
  {
    "path": "README.md",
    "content": "# Clay, A UI Layout Library\n**_Clay_** (short for **C Layout**) is a high performance 2D UI layout library.\n\n### Major Features\n- Microsecond layout performance\n- Flex-box like layout model for complex, responsive layouts including text wrapping, scrolling containers and aspect ratio scaling\n- Single ~4k LOC **clay.h** file with **zero** dependencies (including no standard library)\n- Wasm support: compile with clang to a 15kb uncompressed **.wasm** file for use in the browser\n- Static arena based memory use with no malloc / free, and low total memory overhead (e.g. ~3.5mb for 8192 layout elements).\n- React-like nested declarative syntax\n- Renderer agnostic: outputs a sorted list of rendering primitives that can be easily composited in any 3D engine, and even compiled to HTML (examples provided)\n\nTake a look at the [clay website](https://nicbarker.com/clay) for an example of clay compiled to wasm and running in the browser, or others in the [examples directory](https://github.com/nicbarker/clay/tree/main/examples).\n\nYou can also watch the [introduction video](https://youtu.be/DYWTw19_8r4) for an overview of the motivation behind Clay's development and a short demo of its usage.\n\n<img width=\"1394\" alt=\"A screenshot of a code IDE with lots of visual and textual elements\" src=\"https://github.com/user-attachments/assets/9986149a-ee0f-449a-a83e-64a392267e3d\">\n\n_An example GUI application built with clay_\n\n## Quick Start\n\nDownload or clone clay.h and include it after defining `CLAY_IMPLEMENTATION` in one file.\n\n```C\n// Must be defined in one file, _before_ #include \"clay.h\"\n#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n\nconst Clay_Color COLOR_LIGHT = (Clay_Color) {224, 215, 210, 255};\nconst Clay_Color COLOR_RED = (Clay_Color) {168, 66, 28, 255};\nconst Clay_Color COLOR_ORANGE = (Clay_Color) {225, 138, 50, 255};\n\nvoid HandleClayErrors(Clay_ErrorData errorData) {\n    // See the Clay_ErrorData struct for more information\n    printf(\"%s\", errorData.errorText.chars);\n    switch(errorData.errorType) {\n        // etc\n    }\n}\n\n// Example measure text function\nstatic inline Clay_Dimensions MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, uintptr_t userData) {\n    // Clay_TextElementConfig contains members such as fontId, fontSize, letterSpacing etc\n    // Note: Clay_String->chars is not guaranteed to be null terminated\n    return (Clay_Dimensions) {\n            .width = text.length * config->fontSize, // <- this will only work for monospace fonts, see the renderers/ directory for more advanced text measurement\n            .height = config->fontSize\n    };\n}\n\n// Layout config is just a struct that can be declared statically, or inline\nClay_ElementDeclaration sidebarItemConfig = (Clay_ElementDeclaration) {\n    .layout = {\n        .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50) }\n    },\n    .backgroundColor = COLOR_ORANGE\n};\n\n// Re-useable components are just normal functions\nvoid SidebarItemComponent() {\n    CLAY(id, sidebarItemConfig) {\n        // children go here...\n    }\n}\n\nint main() {\n    // Note: malloc is only used here as an example, any allocator that provides\n    // a pointer to addressable memory of at least totalMemorySize will work\n    uint64_t totalMemorySize = Clay_MinMemorySize();\n    Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));\n\n    // Note: screenWidth and screenHeight will need to come from your environment, Clay doesn't handle window related tasks\n    Clay_Initialize(arena, (Clay_Dimensions) { screenWidth, screenHeight }, (Clay_ErrorHandler) { HandleClayErrors });\n\n    while(renderLoop()) { // Will be different for each renderer / environment\n        // Optional: Update internal layout dimensions to support resizing\n        Clay_SetLayoutDimensions((Clay_Dimensions) { screenWidth, screenHeight });\n        // Optional: Update internal pointer position for handling mouseover / click / touch events - needed for scrolling & debug tools\n        Clay_SetPointerState((Clay_Vector2) { mousePositionX, mousePositionY }, isMouseDown);\n        // Optional: Update internal pointer position for handling mouseover / click / touch events - needed for scrolling and debug tools\n        Clay_UpdateScrollContainers(true, (Clay_Vector2) { mouseWheelX, mouseWheelY }, deltaTime);\n\n        // All clay layouts are declared between Clay_BeginLayout and Clay_EndLayout\n        Clay_BeginLayout();\n\n        // An example of laying out a UI with a fixed width sidebar and flexible width main content\n        CLAY(CLAY_ID(\"OuterContainer\"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }, .backgroundColor = {250,250,255,255} }) {\n            CLAY(CLAY_ID(\"SideBar\"), {\n                .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 },\n                .backgroundColor = COLOR_LIGHT\n            }) {\n                CLAY(CLAY_ID(\"ProfilePictureOuter\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .backgroundColor = COLOR_RED }) {\n                    CLAY(CLAY_ID(\"ProfilePicture\"), {.layout = { .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}, .image = { .imageData = &profilePicture } }) {}\n                    CLAY_TEXT(CLAY_STRING(\"Clay - UI Library\"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255, 255, 255, 255} }));\n                }\n\n                // Standard C code like loops etc work inside components\n                for (int i = 0; i < 5; i++) {\n                    SidebarItemComponent();\n                }\n\n                CLAY(CLAY_ID(\"MainContent\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {}\n            }\n        }\n\n        // All clay layouts are declared between Clay_BeginLayout and Clay_EndLayout\n        Clay_RenderCommandArray renderCommands = Clay_EndLayout();\n\n        // More comprehensive rendering examples can be found in the renderers/ directory\n        for (int i = 0; i < renderCommands.length; i++) {\n            Clay_RenderCommand *renderCommand = &renderCommands.internalArray[i];\n\n            switch (renderCommand->commandType) {\n                case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                    DrawRectangle( renderCommand->boundingBox, renderCommand->renderData.rectangle.backgroundColor);\n                }\n                // ... Implement handling of other command types\n            }\n        }\n    }\n}\n```\n    \nThe above example, rendered correctly will look something like the following:\n\n![Clay Example](https://github.com/user-attachments/assets/1928c6d4-ada9-4a4c-a3d1-44fe9b23b3bd)\n\nIn summary, the general order of steps is:\n\n1. [Clay_SetLayoutDimensions(dimensions)](#clay_setlayoutdimensions)\t\n2. [Clay_SetPointerState(pointerPosition, isPointerDown)](#clay_setpointerstate)\n3. [Clay_UpdateScrollContainers(enableDragScrolling, scrollDelta, deltaTime)](#clay_updatescrollcontainers)\n4. [Clay_BeginLayout()](#clay_beginlayout)\n5. Declare your layout with the provided [Element Macros](#element-macros)\n6. [Clay_EndLayout()](#clay_endlayout)\n7. Render the results using the outputted [Clay_RenderCommandArray](#clay_rendercommandarray)\n\nFor help starting out or to discuss clay, considering joining [the discord server.](https://discord.gg/b4FTWkxdvT)\n\n## Summary\n\n- [High Level Documentation](#high-level-documentation)\n  - [Building UI Hierarchies](#building-ui-hierarchies)\n  - [Configuring Layout and Styling UI Elements](#configuring-layout-and-styling-ui-elements)\n  - [Element IDs](#element-ids)\n  - [Mouse, Touch and Pointer Interactions](#mouse-touch-and-pointer-interactions)\n  - [Scrolling Elements](#scrolling-elements)\n  - [Floating Elements](#floating-elements-absolute-positioning)\n  - [Custom Elements](#laying-out-your-own-custom-elements)\n  - [Retained Mode Rendering](#retained-mode-rendering)\n  - [Visibility Culling](#visibility-culling)\n  - [Preprocessor Directives](#preprocessor-directives)\n  - [Bindings](#bindings-for-non-c)\n  - [Debug Tools](#debug-tools)\n- [API](#api)\n  - [Naming Conventions](#naming-conventions)\n  - [Public Functions](#public-functions)\n    - [Lifecycle](#lifecycle-for-public-functions)\n    - [Clay_MinMemorySize](#clay_minmemorysize)\n    - [Clay_CreateArenaWithCapacityAndMemory](#clay_createarenawithcapacityandmemory)\n    - [Clay_SetMeasureTextFunction](#clay_setmeasuretextfunction)\n    - [Clay_ResetMeasureTextCache](#clay_resetmeasuretextcache)\n    - [Clay_SetMaxElementCount](#clay_setmaxelementcount)\n    - [Clay_SetMaxMeasureTextCacheWordCount](#clay_setmaxmeasuretextcachewordcount)\n    - [Clay_Initialize](#clay_initialize)\n    - [Clay_GetCurrentContext](#clay_getcurrentcontext)\n    - [Clay_SetCurrentContext](#clay_setcurrentcontext)\n    - [Clay_SetLayoutDimensions](#clay_setlayoutdimensions)\n    - [Clay_SetPointerState](#clay_setpointerstate)\n    - [Clay_UpdateScrollContainers](#clay_updatescrollcontainers)\n    - [Clay_BeginLayout](#clay_beginlayout)\n    - [Clay_EndLayout](#clay_endlayout)\n    - [Clay_Hovered](#clay_hovered)\n    - [Clay_OnHover](#clay_onhover)\n    - [Clay_PointerOver](#clay_pointerover)\n    - [Clay_GetScrollContainerData](#clay_getscrollcontainerdata)\n    - [Clay_GetElementData](#clay_getelementdata)\n    - [Clay_GetElementId](#clay_getelementid)\n  - [Element Macros](#element-macros)\n    - [CLAY](#clay)\n    - [CLAY_ID](#clay_id)\n    - [CLAY_IDI](#clay_idi)\n  - [Data Structures & Defs](#data-structures--definitions)\n    - [Clay_String](#clay_string)\n    - [Clay_ElementId](#clay_elementid)\n    - [Clay_RenderCommandArray](#clay_rendercommandarray)\n    - [Clay_RenderCommand](#clay_rendercommand)\n    - [Clay_ScrollContainerData](#clay_scrollcontainerdata)\n    - [Clay_ErrorHandler](#clay_errorhandler)\n    - [Clay_ErrorData](#clay_errordata)\n\n## High Level Documentation\n\n### Building UI Hierarchies\nClay UIs are built using the C macro `CLAY(id, { configuration })`. This macro creates a new empty element in the UI hierarchy, and supports modular customisation of layout, styling and functionality. The `CLAY()` macro can also be _nested_, similar to other declarative UI systems like HTML.\n\nChild elements are added by opening a block: `{}` after calling the `CLAY()` macro (exactly like you would with an `if` statement or `for` loop), and declaring child components inside the braces.\n```C\n// Parent element with 8px of padding\nCLAY(CLAY_ID(\"parent\"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {\n    // Child element 1\n    CLAY_TEXT(CLAY_STRING(\"Hello World\"), CLAY_TEXT_CONFIG({ .fontSize = 16 }));\n    // Child element 2 with red background\n    CLAY((CLAY_ID(\"child\"), { .backgroundColor = COLOR_RED }) {\n        // etc\n    }\n}\n```\n\nHowever, unlike HTML and other declarative DSLs, this macro is just C. As a result, you can use arbitrary C code such as loops, functions and conditions inside your layout declaration code:\n```C\n// Re-usable \"components\" are just functions that declare more UI\nvoid ButtonComponent(Clay_String buttonText) {\n    // Red box button with 8px of padding\n    CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(8) }, .backgroundColor = COLOR_RED }) {\n        CLAY_TEXT(buttonText, textConfig);\n    }\n}\n\n// Parent element\nCLAY(CLAY_ID(\"parent\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n    // Render a bunch of text elements\n    for (int i = 0; i < textArray.length; i++) {\n        CLAY_TEXT(textArray.elements[i], textConfig);\n    }\n    // Only render this element if we're on a mobile screen\n    if (isMobileScreen) {\n        CLAY(0) {\n            // etc\n        }\n    }\n    // Re-usable components\n    ButtonComponent(CLAY_STRING(\"Click me!\"));\n    ButtonComponent(CLAY_STRING(\"No, click me!\"));\n});\n```\n\n### Configuring Layout and Styling UI Elements\nThe layout and style of clay elements is configured with the [Clay_ElementDeclaration](#clay_elementdeclaration) struct passed to the `CLAY()` macro. \n```C\nCLAY(CLAY_ID(\"box\"), { .layout = { .padding = { 8, 8, 8, 8 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n    // Children are 8px inset into parent, and laid out top to bottom\n}\n```\nThis macro isn't magic - all it's doing is wrapping the standard designated initializer syntax. e.g. `(Clay_ElementDeclaration) { .layout = { .padding = { .left = 8, .right = 8 } ...`.\n\nSee the [Clay_ElementDeclaration](#clay_elementdeclaration) API for the full list of options.\n\nA `Clay_ElementDeclaration` struct can be defined in file scope or elsewhere, and reused.\n```C\n// Define a style in the global / file scope\nClay_ElementDeclaration reuseableStyle = (Clay_ElementDeclaration) {\n    .layout = { .padding = { .left = 12 } },\n    .backgroundColor = { 120, 120, 120, 255 },\n    .cornerRadius = { 12, 12, 12, 12 }\n};\n\nCLAY(CLAY_ID(\"box\"), reuseableStyle) {\n    // ...\n}\n```\n\n### Element IDs\n\nThe Clay macro by default accepts an ID as its first argument, which is usually provided by the [CLAY_ID()](#clay_id) convenience macro. Elements can also be created with auto generated IDs, by using the [CLAY_AUTO_ID()](#clay-auto-id) macro.\n\n```C\n// Will always produce the same ID from the same input string\nCLAY(CLAY_ID(\"OuterContainer\"), { ...configuration }) {}\n\n// Generates a unique ID that may not be the same between two layout calls\nCLAY_AUTO_ID({ ...configuration }) {}\n```\n\nElement IDs have two main use cases. Firstly, tagging an element with an ID allows you to query information about the element later, such as its [mouseover state](#clay_pointerover) or dimensions.\n\nSecondly, IDs are visually useful when attempting to read and modify UI code, as well as when using the built-in [debug tools](#debug-tools).\n\nTo avoid having to construct dynamic strings at runtime to differentiate ids in loops, clay provides the [CLAY_IDI(string, index)](#clay_idi) macro to generate different ids from a single input string. Think of IDI as \"**ID** + **I**ndex\"\n```C\n// This is the equivalent of calling CLAY_ID(\"Item0\"), CLAY_ID(\"Item1\") etc\nfor (int index = 0; index < items.length; index++) {\n    CLAY(CLAY_IDI(\"Item\", index), { ..configuration }) {}\n}\n```\n\nThis ID will be forwarded to the final `Clay_RenderCommandArray` for use in retained mode UIs. Using duplicate IDs may cause some functionality to misbehave (i.e. if you're trying to attach a floating container to a specific element with ID that is duplicated, it may not attach to the one you expect)\n\n### Mouse, Touch and Pointer Interactions\n\nClay provides several functions for handling mouse and pointer interactions.\n\nAll pointer interactions depend on the function `void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown)` being called after each mouse position update and before any other clay functions.\n\n**During UI declaration**\n\nThe function `bool Clay_Hovered()` can be called during element construction or in the body of an element, and returns `true` if the mouse / pointer is over the currently open element.\n\n```C\n// An orange button that turns blue when hovered\nCLAY(CLAY_ID(\"Button\"), { .backgroundColor = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE }) {\n    bool buttonHovered = Clay_Hovered();\n    CLAY_TEXT(buttonHovered ? CLAY_STRING(\"Hovered\") : CLAY_STRING(\"Hover me!\"), headerTextConfig);\n}\n```\n\nThe function `void Clay_OnHover()` allows you to attach a function pointer to the currently open element, which will be called if the mouse / pointer is over the element.\n\n```C\nvoid HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData) {\n    ButtonData *buttonData = (ButtonData *)userData;\n    // Pointer state allows you to detect mouse down / hold / release\n    if (pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n        // Do some click handling\n        NavigateTo(buttonData->link);\n    }\n}\n\nButtonData linkButton = (ButtonData) { .link = \"https://github.com/nicbarker/clay\" };\n\n// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries\nCLAY(CLAY_ID(\"Button\"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {\n    Clay_OnHover(HandleButtonInteraction, &linkButton);\n    CLAY_TEXT(CLAY_STRING(\"Button\"), &headerTextConfig);\n}\n```\n\n**Before / After UI declaration**\n\nIf you want to query mouse / pointer overlaps outside layout declarations, you can use the function `bool Clay_PointerOver(Clay_ElementId id)`, which takes an [element id](#element-ids) and returns a bool representing whether the current pointer position is within its bounding box. \n```C\n// Reminder: Clay_SetPointerState must be called before functions that rely on pointer position otherwise it will have no effect\nClay_Vector2 mousePosition = { x, y };\nClay_SetPointerState(mousePosition);\n// ...\n// If profile picture was clicked\nif (mouseButtonDown(0) && Clay_PointerOver(Clay_GetElementId(\"ProfilePicture\"))) {\n    // Handle profile picture clicked\n}\n```\n\nNote that the bounding box queried by `Clay_PointerOver` is from the last frame. This generally shouldn't make a difference except in the case of animations that move at high speed.\nIf this is an issue for you, performing layout twice per frame with the same data will give you the correct interaction the second time.\n\n### Scrolling Elements\n\nElements are configured as scrollable with the `.clip` configuration. Clipping instructs the renderer to not draw any pixels outside the clipped element's boundaries, and by specifying the `.childOffset` field, the clipped element's contents can be shifted around to provide \"scrolling\" behaviour.\n\nYou can either calculate scrolling yourself and simply provide the current offset each frame to `.childOffset`, or alternatively, Clay provides a built in mechanism for tracking and updating scroll container offsets, detailed below.\n\nTo make scroll containers respond to mouse wheel and scroll events, two functions need to be called before `BeginLayout()`:\n```C\nClay_Vector2 mousePosition = { x, y };\n// Reminder: Clay_SetPointerState must be called before Clay_UpdateScrollContainers otherwise it will have no effect\nClay_SetPointerState(mousePosition);\n// Clay_UpdateScrollContainers needs to be called before Clay_BeginLayout for the position to avoid a 1 frame delay\nClay_UpdateScrollContainers(\n    true, // Enable drag scrolling\n    scrollDelta, // Clay_Vector2 scrollwheel / trackpad scroll x and y delta this frame\n    float deltaTime, // Time since last frame in seconds as a float e.g. 8ms is 0.008f\n);\n// ...\n// Clay internally tracks the scroll containers offset, and Clay_GetScrollOffset returns the x,y offset of the currently open element\nCLAY(CLAY_ID(\"ScrollContainer\"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {\n    // Scrolling contents\n}\n// .childOffset can be provided directly if you would prefer to manage scrolling outside of clay\nCLAY(CLAY_ID(\"ScrollContainer\"), { .clip = { .vertical = true, .childOffset = myData.scrollContainer.offset } }) {\n    // Scrolling contents\n}\n```\n\nMore specific details can be found in the docs for [Clay_UpdateScrollContainers](#clay_updatescrollcontainers), [Clay_SetPointerState](#clay_setpointerstate), [Clay_ClipElementConfig](#clay_clipelementconfig) and [Clay_GetScrollOffset](#clay_getscrolloffset).\n\n### Floating Elements (\"Absolute\" Positioning)\n\nAll standard elements in clay are laid out on top of, and _within_ their parent, positioned according to their parent's layout rules, and affect the positioning and sizing of siblings.\n\n**\"Floating\"** is configured with the `CLAY_FLOATING()` macro. Floating elements don't affect the parent they are defined in, or the position of their siblings.\nThey also have a **z-index**, and as a result can intersect and render over the top of other elements.\n\nA classic example use case for floating elements is tooltips and modals.\n\n```C\n// The two text elements will be laid out top to bottom, and the floating container\n// will be attached to \"Outer\"\nCLAY(CLAY_ID(\"Outer\"), { .layout = { .layoutDirection = TOP_TO_BOTTOM } }) {\n    CLAY_TEXT(text, &headerTextConfig);\n    CLAY(CLAY_ID(\"Tooltip\"), { .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } }) {}\n    CLAY_TEXT(text, &headerTextConfig);\n}\n```\n\nMore specific details can be found in the full [Floating API](#clay_floatingelementconfig).\n\n### Laying Out Your Own Custom Elements\n\nClay only supports a simple set of UI element primitives, such as rectangles, text and images. Clay provides a singular API for layout out custom elements:\n```C\n#include \"clay.h\"\n\ntypedef enum {\n    CUSTOM_ELEMENT_TYPE_MODEL,\n    CUSTOM_ELEMENT_TYPE_VIDEO\n} CustomElementType;\n\n// A rough example of how you could handle laying out 3d models in your UI\ntypedef struct {\n    CustomElementType type;\n    union {\n        Model model;\n        Video video;\n        // ...\n    };\n} CustomElementData;\n\nModel myModel = Load3DModel(filePath);\nCustomElement modelElement = (CustomElement) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel }\n\ntypedef struct {\n    void* memory;\n    uintptr_t offset;\n} Arena;\n\n// During init\nArena frameArena = (Arena) { .memory = malloc(1024) };\n\n// Custom elements only take a single pointer, so we need to store the data somewhere\nCustomElementData *modelData = (CustomElementData *)(frameArena.memory + frameArena.offset);\n*modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };\nframeArena.offset += sizeof(CustomElementData);\nCLAY(CLAY_ID(\"3DModelViewer\"), { .custom = { .customData = modelData } }) {}\n\n// Later during your rendering\nswitch (renderCommand->commandType) {\n    // ...\n    case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {\n        // Your extended struct is passed through\n        CustomElementData *customElement = renderCommand->config.customElementConfig->customData;\n        if (!customElement) continue;\n        switch (customElement->type) {\n            case CUSTOM_ELEMENT_TYPE_MODEL: {\n                // Render your 3d model here\n                break;\n            }\n            case CUSTOM_ELEMENT_TYPE_VIDEO: {\n                // Render your video here\n                break;\n            }\n            // ...\n        }\n        break;\n    }\n}\n```\n\nMore specific details can be found in the full [Custom Element API](#clay_customelementconfig).\n\n### Retained Mode Rendering\nClay was originally designed for [Immediate Mode](https://www.youtube.com/watch?v=Z1qyvQsjK5Y) rendering - where the entire UI is redrawn every frame. This may not be possible with your platform, renderer design or performance constraints.\n\nThere are some general techniques that can be used to integrate clay into a retained mode rendering system:\n\n- `Clay_RenderCommand` includes the `uint32_t id` that was used to declare the element. If unique ids are used, these can be mapped to persistent graphics objects across multiple frames / layouts.\n- Render commands are culled automatically to only currently visible elements, and `Clay_RenderCommand` is a small enough struct that you can simply compare the memory of two render commands with matching IDs to determine if the element is \"dirty\" and needs to be re-rendered or updated.\n\nFor a worked example, see the provided [HTML renderer](https://github.com/nicbarker/clay/blob/main/renderers/web/html/clay-html-renderer.html). This renderer converts clay layouts into persistent HTML documents with minimal changes per frame.  \n\n### Visibility Culling\nClay provides a built-in visibility-culling mechanism that is **enabled by default**. It will only output render commands for elements that are visible - that is, **at least one pixel of their bounding box is inside the viewport.**\n\nThis culling mechanism can be disabled via the use of the `#define CLAY_DISABLE_CULLING` directive. See [Preprocessor Directives](#preprocessor-directives) for more information.\n\n### Preprocessor Directives\nClay supports C preprocessor directives to modulate functionality at compile time. These can be set either in code using `#define CLAY_DISABLE_CULLING` or on the command line when compiling using the appropriate compiler specific arguments, e.g. `clang -DCLAY_DISABLE_CULLING main.c ...`\n\nThe supported directives are:\n\n- `CLAY_WASM` - Required when targeting Web Assembly.\n- `CLAY_DLL` - Required when creating a .Dll file.\n\n### Bindings for non C\n\nClay is usable out of the box as a `.h` include in both C99 and C++20 with designated initializer support.\nThere are also supported bindings for other languages, including:\n\n- [Odin Bindings](https://github.com/nicbarker/clay/tree/main/bindings/odin)\n- [Rust Bindings](https://github.com/clay-ui-rs/clay)\n\n### Other implementations\nClay has also been implemented in other languages:\n\n- [`glay`](https://github.com/soypat/glay) - Go line-by-line rewrite with readability as main goal.\n- [`totallygamerjet/clay`](https://github.com/totallygamerjet/clay) - Port using `cxgo`, a C to Go transpiler.\n- [`goclay`](https://github.com/igadmg/goclay) - Go line-by-line rewrite closely matching the reference.\n\n### Debug Tools\n\nClay includes built-in UI debugging tools, similar to the \"inspector\" in browsers such as Chrome or Firefox. These tools are included in `clay.h`, and work by injecting additional render commands into the output [Clay_RenderCommandArray](#clay_rendercommandarray).\n\nAs long as the renderer that you're using works correctly, no additional setup or configuration is required to use the debug tools.\n\nTo enable the debug tools, use the function `Clay_SetDebugModeEnabled(bool enabled)`. This boolean is persistent and does not need to be set every frame.\n\nThe debug tooling by default will render as a panel to the right side of the screen, compressing your layout by its width. The default width is 400 and is currently configurable via the direct mutation of the internal variable `Clay__debugViewWidth`, however this is an internal API and is potentially subject to change.\n\n<img width=\"1506\" alt=\"Screenshot 2024-09-12 at 12 54 03 PM\" src=\"https://github.com/user-attachments/assets/2d122658-3305-4e27-88d6-44f08c0cb4e6\">\n\n_The official Clay website with debug tooling visible_\n\n### Running more than one Clay instance\n\nClay allows you to run more than one instance in a program. To do this, [Clay_Initialize](#clay_initialize) returns a [Clay_Context*](#clay_context) reference. You can activate a specific instance using [Clay_SetCurrentContext](#clay_setcurrentcontext). If [Clay_SetCurrentContext](#clay_setcurrentcontext) is not called, then Clay will default to using the context from the most recently called [Clay_Initialize](#clay_initialize).\n\n**⚠ Important: Do not render instances across different threads simultaneously, as Clay does not currently support proper multi-threading.**\n\n```c++\n// Define separate arenas for the instances.\nClay_Arena arena1, arena2;\n// ... allocate arenas\n\n// Initialize both instances, storing the context for each one.\nClay_Context* instance1 = Clay_Initialize(arena1, layoutDimensions, errorHandler);\nClay_Context* instance2 = Clay_Initialize(arena2, layoutDimensions, errorHandler);\n\n// In the program's render function, activate each instance before executing clay commands and macros.\nClay_SetCurrentContext(instance1);\nClay_BeginLayout();\n// ... declare layout for instance1\nClay_RenderCommandArray renderCommands1 = Clay_EndLayout();\nrender(renderCommands1);\n\n// Switch to the second instance\nClay_SetCurrentContext(instance2);\nClay_BeginLayout();\n// ... declare layout for instance2\nClay_RenderCommandArray renderCommands2 = Clay_EndLayout();\nrender(renderCommands2);\n```\n\n# API\n\n### Naming Conventions\n\n- \"**CAPITAL_LETTERS()**\" are used for macros.\n- \"**Clay__**\" (\"Clay\" followed by **double** underscore) is used for internal functions that are not intended for use and are subject to change.\n- \"**Clay_**\" (\"Clay\" followed by **single** underscore) is used for external functions that can be called by the user.\n\n## Public Functions\n\n### Lifecycle for public functions\n\n**At startup / initialization time, run once**\n`Clay_MinMemorySize` -> `Clay_CreateArenaWithCapacityAndMemory` -> `Clay_Initialize` -> `Clay_SetMeasureTextFunction`\n\n**Each Frame**\n`Clay_SetLayoutDimensions` -> `Clay_SetPointerState` -> `Clay_UpdateScrollContainers` -> `Clay_BeginLayout` -> `CLAY() etc...` -> `Clay_EndLayout`\n\n---\n\n### Clay_MinMemorySize\n\n`uint32_t Clay_MinMemorySize()`\n\nReturns the minimum amount of memory **in bytes** that clay needs to accommodate the current [CLAY_MAX_ELEMENT_COUNT](#preprocessor-directives).\n\n---\n\n### Clay_CreateArenaWithCapacityAndMemory\n\n`Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *offset)`\n\nCreates a `Clay_Arena` struct with the given capacity and base memory pointer, which can be passed to [Clay_Initialize](#clay_initialize).\n\n---\n\n### Clay_SetMeasureTextFunction\n\n`void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig *config, uintptr_t userData), uintptr_t userData)`\n\nTakes a pointer to a function that can be used to measure the `width, height` dimensions of a string. Used by clay during layout to determine [CLAY_TEXT](#clay_text) element sizing and wrapping.\n\n**Note 1: This string is not guaranteed to be null terminated.** Clay saves significant performance overhead by using slices when wrapping text instead of having to clone new null terminated strings. If your renderer does not support **ptr, length** style strings (e.g. Raylib), you will need to clone this to a new C string before rendering.\n\n**Note 2: It is essential that this function is as fast as possible.** For text heavy use-cases this function is called many times, and despite the fact that clay caches text measurements internally, it can easily become the dominant overall layout cost if the provided function is slow. **This is on the hot path!**\n\n---\n\n### Clay_ResetMeasureTextCache\n\n`void Clay_ResetMeasureTextCache(void)`\n\nClay caches measurements from the provided MeasureTextFunction, and this will be sufficient for the majority of use-cases. However, if the measurements can depend on external factors that clay does not know about, like DPI changes, then the cached values may be incorrect. When one of these external factors changes, Clay_ResetMeasureTextCache can be called to force clay to recalculate all string measurements in the next frame.\n\n---\n\n### Clay_SetMaxElementCount\n\n`void Clay_SetMaxElementCount(uint32_t maxElementCount)`\n\nSets the internal maximum element count that will be used in subsequent [Clay_Initialize()](#clay_initialize) and [Clay_MinMemorySize()](#clay_minmemorysize) calls, allowing clay to allocate larger UI hierarchies.\n\n**Note: You will need to reinitialize clay, after calling [Clay_MinMemorySize()](#clay_minmemorysize) to calculate updated memory requirements.**\n\n---\n\n### Clay_SetMaxMeasureTextCacheWordCount\n\n`void Clay_SetMaxMeasureTextCacheWordCount(uint32_t maxMeasureTextCacheWordCount)`\n\nSets the internal text measurement cache size that will be used in subsequent [Clay_Initialize()](#clay_initialize) and [Clay_MinMemorySize()](#clay_minmemorysize) calls, allowing clay to allocate more text. The value represents how many separate words can be stored in the text measurement cache.\n\n**Note: You will need to reinitialize clay, after calling [Clay_MinMemorySize()](#clay_minmemorysize) to calculate updated memory requirements.**\n\n---\n\n### Clay_Initialize\n\n`Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler)`\n\nInitializes the internal memory mapping, sets the internal dimensions for layout, and binds an error handler for clay to use when something goes wrong. Returns a [Clay_Context*](#clay_context) that can optionally be given to [Clay_SetCurrentContext](#clay_setcurrentcontext) to allow running multiple instances of clay in the same program, and sets it as the current context. See [Running more than one Clay instance](#running-more-than-one-clay-instance).\n\nReference: [Clay_Arena](#clay_createarenawithcapacityandmemory), [Clay_ErrorHandler](#clay_errorhandler), [Clay_SetCurrentContext](#clay_setcurrentcontext)\n\n---\n\n### Clay_SetCurrentContext\n\n`void Clay_SetCurrentContext(Clay_Context* context)`\n\nSets the context that subsequent clay commands will operate on. You can get this reference from [Clay_Initialize](#clay_initialize) or [Clay_GetCurrentContext](#clay_getcurrentcontext). See [Running more than one Clay instance](#running-more-than-one-clay-instance).\n\n---\n\n### Clay_GetCurrentContext\n\n`Clay_Context* Clay_GetCurrentContext()`\n\nReturns the context that clay commands are currently operating on, or null if no context has been set. See [Running more than one Clay instance](#running-more-than-one-clay-instance).\n\n---\n\n### Clay_SetLayoutDimensions\n\n`void Clay_SetLayoutDimensions(Clay_Dimensions dimensions)`\n\nSets the internal layout dimensions. Cheap enough to be called every frame with your screen dimensions to automatically respond to window resizing, etc.\n\n---\n\n### Clay_SetPointerState\n\n`void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown)`\n\nSets the internal pointer position and state (i.e. current mouse / touch position) and recalculates overlap info, which is used for mouseover / click calculation (via [Clay_PointerOver](#clay_pointerover) and updating scroll containers with [Clay_UpdateScrollContainers](#clay_updatescrollcontainers). **isPointerDown should represent the current state this frame, e.g. it should be `true` for the entire duration the left mouse button is held down.** Clay has internal handling for detecting click / touch start & end.\n\n---\n\n### Clay_UpdateScrollContainers\n\n`void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime)`\n\nThis function handles scrolling of containers. It responds to both `scrollDelta`, which represents mouse wheel or trackpad scrolling this frame, as well as \"touch scrolling\" on mobile devices, or \"drag scrolling\" with a mouse or similar device.\n\nTouch / drag scrolling only occurs if the `enableDragScrolling` parameter is `true`, **and** [Clay_SetPointerState](#clay_setpointerstate) has been called this frame. As a result, you can simply always call it with `false` as the first argument if you want to disable touch scrolling.\n\n`deltaTime` is the time **in seconds** since the last frame (e.g. 0.016 is **16 milliseconds**), and is used to normalize & smooth scrolling across different refresh rates.\n\n---\n\n### Clay_GetScrollOffset\n\n`Clay_Vector2 Clay_GetScrollOffset()`\n\nReturns the internally stored scroll offset for the currently open element.\n\nGenerally intended for use with [clip elements](#clay_clipelementconfig) and the `.childOffset` field to create scrolling containers.\n\nSee [Scrolling Elements](#scrolling-elements) for more details.\n\n```C\n// Create a horizontally scrolling container\nCLAY(CLAY_ID(\"ScrollContainer\"), {\n    .clip = { .horizontal = true, .childOffset = Clay_GetScrollOffset() }\n})\n```\n\n---\n\n### Clay_BeginLayout\n\n`void Clay_BeginLayout()`\n\nPrepares clay to calculate a new layout. Called each frame / layout **before** any of the [Element Macros](#element-macros).\n\n---\n\n### Clay_EndLayout\n\n`Clay_RenderCommandArray Clay_EndLayout()`\n\nEnds declaration of element macros and calculates the results of the current layout. Renders a [Clay_RenderCommandArray](#clay_rendercommandarray) containing the results of the layout calculation.\n\n---\n\n### Clay_Hovered\n\n`bool Clay_Hovered()`\n\nCalled **during** layout declaration, and returns `true` if the pointer position previously set with `Clay_SetPointerState` is inside the bounding box of the currently open element. Note: this is based on the element's position from the **last** frame.\n\n---\n\n### Clay_OnHover\n\n`void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData), void *userData)`\n\nCalled **during** layout declaration, this function allows you to attach a function pointer to the currently open element that will be called once per layout if the pointer position previously set with `Clay_SetPointerState` is inside the bounding box of the currently open element. See [Clay_PointerData](#clay_pointerdata) for more information on the `pointerData` argument.\n\n```C\nvoid HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData) {\n    ButtonData *buttonData = (ButtonData *)userData;\n    // Pointer state allows you to detect mouse down / hold / release\n    if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n        // Do some click handling\n        NavigateTo(buttonData->link);\n    }\n}\n\nButtonData linkButton = (ButtonData) { .link = \"https://github.com/nicbarker/clay\" };\n\n// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries\nCLAY(CLAY_ID(\"Button\"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {\n    Clay_OnHover(HandleButtonInteraction, &buttonData);\n    CLAY_TEXT(CLAY_STRING(\"Click me!\"), &headerTextConfig);\n}\n```\n\n---\n\n### Clay_PointerOver\n\n`bool Clay_PointerOver(Clay_ElementId id)`\n\nReturns `true` if the pointer position previously set with `Clay_SetPointerState` is inside the bounding box of the layout element with the provided `id`. Note: this is based on the element's position from the **last** frame. If frame-accurate pointer overlap detection is required, perhaps in the case of significant change in UI layout between frames, you can simply run your layout code twice that frame. The second call to `Clay_PointerOver` will be frame-accurate.\n\n### Clay_GetScrollContainerData\n\n`Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id)`\n\nReturns [Clay_ScrollContainerData](#clay_scrollcontainerdata) for the scroll container matching the provided ID. This function allows imperative manipulation of scroll position, allowing you to build things such as scroll bars, buttons that \"jump\" to somewhere in a scroll container, etc.\n\n---\n\n### Clay_GetElementData\n\n`Clay_ElementData Clay_GetElementData(Clay_ElementId id)`\n\nReturns [Clay_ElementData](#clay_elementdata) for the element matching the provided ID.\nUsed to retrieve information about elements such as their final calculated bounding box.\n\n---\n\n### Clay_GetElementId\n\n`Clay_ElementId Clay_GetElementId(Clay_String idString)`\n\nReturns a [Clay_ElementId](#clay_elementid) for the provided id string, used for querying element info such as mouseover state, scroll container data, etc.\n\n## Element Macros\n\n### CLAY()\n**Usage**\n\n`CLAY(...configuration) { ...children }`\n\n**Lifecycle**\n\n`Clay_BeginLayout()` -> `CLAY()` -> `Clay_EndLayout()` \n\n**Notes**\n\n**CLAY** opens a generic empty container, that is configurable and supports nested children.\n**CLAY** requires a parameter, so if you want to create an element without any configuration, use `CLAY(0)`.\n\n**Examples**\n```C\n// Define an element with 16px of x and y padding\nCLAY(CLAY_ID(\"Outer\"), { .layout = { .padding = CLAY_PADDING_ALL(16) } }) {\n    // A nested child element\n    CLAY(CLAY_ID(\"SideBar\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {\n        // Children laid out top to bottom with a 16 px gap between them\n    }\n    // A vertical scrolling container with a colored background\n    CLAY(CLAY_ID(\"ScrollContainer\"), {\n        .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },\n        .backgroundColor = { 200, 200, 100, 255 },\n        .cornerRadius = CLAY_CORNER_RADIUS(10),\n        .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }\n    }) {\n        // child elements\n    }\n}\n```\n\n---\n\n### CLAY_AUTO_ID()\n\nA version of the core [CLAY()](#clay) element creation macro that generates an ID automatically instead of requiring it as the first argument.\n\nNote that under the hood this ID is generated in the same way as [CLAY_ID_LOCAL()](#clay_id_local), which is based on the element's position in the hierarchy, and may chance between layout calls if elements are added / removed from the hierarchy before the element is defined. As a result, for transitions & retained mode backends to work correctly, IDs should be specified.\n\n```C\n// Note that CLAY_AUTO_ID only takes one argument: the configuration\nCLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(16) } }) {\n    // A nested child element\n    CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {\n        // Children laid out top to bottom with a 16 px gap between them\n    }\n    // A vertical scrolling container with a colored background\n    CLAY_AUTO_ID({\n        .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },\n        .backgroundColor = { 200, 200, 100, 255 },\n        .cornerRadius = CLAY_CORNER_RADIUS(10),\n        .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }\n    }) {\n        // child elements\n    }\n}\n```\n\n---\n\n### CLAY_TEXT()\n**Usage**\n\n`CLAY_TEXT(Clay_String textContents, Clay_TextElementConfig *textConfig);`\n\n**Lifecycle**\n\n`Clay_BeginLayout()` -> `CLAY_TEXT()` -> `Clay_EndLayout()`\n\n**Notes**\n\n**TEXT** is a measured, auto-wrapped text element. It uses `Clay_TextElementConfig` to configure text specific options.\n\nNote that `Clay_TextElementConfig` uses `uint32_t fontId`. Font ID to font asset mapping is managed in user code and passed to render commands.\n\n**Struct API (Pseudocode)**\n\n```C\n// CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .member = value })) supports these options\nClay_TextElementConfig {\n    Clay_Color textColor {\n        float r; float g; float b; float a;\n    };\n    uint16_t fontId;\n    uint16_t fontSize;\n    uint16_t letterSpacing;\n    uint16_t lineHeight;\n    Clay_TextElementConfigWrapMode wrapMode {\n        CLAY_TEXT_WRAP_WORDS (default),\n        CLAY_TEXT_WRAP_NEWLINES,\n        CLAY_TEXT_WRAP_NONE,\n    };\n};\n```\n\n**Fields**\n\n**`.textColor`**\n\n`CLAY_TEXT_CONFIG(.textColor = {120, 120, 120, 255})`\n\nUses [Clay_Color](#clay_color). Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.\n\n---\n\n**`.fontId`**\n\n`CLAY_TEXT_CONFIG(.fontId = FONT_ID_LATO)`\n\nIt's up to the user to load fonts and create a mapping from `fontId` to a font that can be measured and rendered.\n\n---\n\n**`.fontSize`**\n\n`CLAY_TEXT_CONFIG(.fontSize = 16)`\n\nFont size is generally thought of as `x pixels tall`, but interpretation is left up to the user & renderer.\n\n---\n\n**`.letterSpacing`**\n\n`CLAY_TEXT_CONFIG(.letterSpacing = 1)`\n\n`.letterSpacing` results in **horizontal** white space between individual rendered characters.\n\n---\n\n**`.lineHeight`**\n\n`CLAY_TEXT_CONFIG(.lineHeight = 20)`\n\n`.lineHeight` - when non zero - forcibly sets the `height` of each wrapped line of text to `.lineheight` pixels tall. Will affect the layout of both parents and siblings. A value of `0` will use the measured height of the font.\n\n---\n\n**`.wrapMode`**\n\n`CLAY_TEXT_CONFIG(.wrapMode = CLAY_TEXT_WRAP_NONE)`\n\n`.wrapMode` specifies under what conditions text should [wrap](https://en.wikipedia.org/wiki/Line_wrap_and_word_wrap).\n\nAvailable options are:\n\n- `CLAY_TEXT_WRAP_WORDS` (default) - Text will wrap on whitespace characters as container width shrinks, preserving whole words.\n- `CLAY_TEXT_WRAP_NEWLINES` -  will only wrap when encountering newline characters.\n- `CLAY_TEXT_WRAP_NONE` - Text will never wrap even if its container is compressed beyond the text measured width.\n\n---\n\n**Examples**\n\n```C\n// Define a font somewhere in your code\nconst uint32_t FONT_ID_LATO = 3;\n// ..\nCLAY_TEXT(CLAY_STRING(\"John Smith\"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_LATO, .fontSize = 24, .textColor = {255, 0, 0, 255} }));\n// Rendering example\nFont fontToUse = LoadedFonts[renderCommand->renderData.text->fontId];\n```\n\n**Rendering**\n\nElement is subject to [culling](#visibility-culling). Otherwise, multiple `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_TEXT` may be created, one for each wrapped line of text.\n\n`Clay_RenderCommand.textContent` will be populated with a `Clay_String` _slice_ of the original string passed in (i.e. wrapping doesn't reallocate, it just returns a `Clay_String` pointing to the start of the new line with a `length`)\n\n---\n\n### CLAY_ID\n\n`Clay_ElementId CLAY_ID(STRING_LITERAL idString)`\n\n**CLAY_ID()** is used to generate and attach a [Clay_ElementId](#clay_elementid) to a layout element during declaration.\n\nNote this macro only works with String literals and won't compile if used with a `char*` variable. To use a heap allocated `char*` string as an ID, use [CLAY_SID](#clay_sid). \n\nTo regenerate the same ID outside of layout declaration when using utility functions such as [Clay_PointerOver](#clay_pointerover), use the [Clay_GetElementId](#clay_getelementid) function.\n\n**Examples**\n\n```C\n// Tag a button with the Id \"Button\"\nCLAY(CLAY_ID(\"Button\"), {\n    .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }\n}) {\n    // ...children\n}\n\n// Later on outside of layout code\nbool buttonIsHovered = Clay_IsPointerOver(Clay_GetElementId(\"Button\"));\nif (buttonIsHovered && leftMouseButtonPressed) {\n    // ... do some click handling\n}\n```\n\n---\n\n### CLAY_SID()\n\n`Clay_ElementId CLAY_SID(Clay_String idString)`\n\nA version of [CLAY_ID](#clay_id) that can be used with heap allocated `char *` data. The underlying `char` data will not be copied internally and should live until at least the next frame.\n\n---\n\n### CLAY_IDI()\n\n`Clay_ElementId CLAY_IDI(STRING_LITERAL idString, int32_t index)`\n\nAn offset version of [CLAY_ID](#clay_id). Generates a [Clay_ElementId](#clay_elementid) string id from the provided `char *label`, combined with the `int index`.\n\nUsed for generating ids for sequential elements (such as in a `for` loop) without having to construct dynamic strings at runtime.\n\nNote this macro only works with String literals and won't compile if used with a `char*` variable. To use a heap allocated `char*` string as an ID, use [CLAY_SIDI](#clay_sidi).\n\n---\n\n### CLAY_SIDI()\n\n`Clay_ElementId CLAY_SIDI(Clay_String idString, int32_t index)`\n\nA version of [CLAY_IDI](#clay_idi) that can be used with heap allocated `char *` data. The underlying `char` data will not be copied internally and should live until at least the next frame.\n\n---\n\n### CLAY_ID_LOCAL()\n\n**Usage**\n\n`Clay_ElementId CLAY_ID_LOCAL(STRING_LITERAL idString)`\n\n**Lifecycle**\n\n`Clay_BeginLayout()` -> `CLAY(` -> `CLAY_ID_LOCAL()` -> `)` -> `Clay_EndLayout()`\n\n**Notes**\n\n**CLAY_ID_LOCAL()** is used to generate and attach a [Clay_ElementId](#clay_elementid) to a layout element during declaration.\n\nUnlike [CLAY_ID](#clay_id) which needs to be globally unique, a local ID is based on the ID of it's parent and only needs to be unique among its siblings.\n\nAs a result, local id is suitable for use in reusable components and loops.\n\nNote this macro only works with String literals and won't compile if used with a `char*` variable. To use a heap allocated `char*` string as an ID, use [CLAY_SID_LOCAL](#clay_sid_local).\n\n**Examples**\n\n```C\nvoid RenderHeaderButton(ButtonData button) {\n    CLAY({\n        .id = CLAY_ID_LOCAL(\"HeaderButton\"),\n        .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }\n    }) {\n        // ...children\n    }\n}\n\nfor (int i = 0; i < headerButtons.length; i++) {\n    RenderHeaderButton(headerButtons.items[i]);\n}\n```\n\n---\n\n### CLAY_SID_LOCAL()\n\n`Clay_ElementId CLAY_SID_LOCAL(Clay_String idString)`\n\nA version of [CLAY_ID_LOCAL](#clay_id_local) that can be used with heap allocated `char *` data. The underlying `char` data will not be copied internally and should live until at least the next frame.\n\n---\n\n### CLAY_IDI_LOCAL()\n\n`Clay_ElementId CLAY_IDI_LOCAL(STRING_LITERAL idString, int32_t index)`\n\nAn offset version of [CLAY_ID_LOCAL](#clay_local_id). Generates a [Clay_ElementId](#clay_elementid) string id from the provided `char *label`, combined with the `int index`.\n\nUsed for generating ids for sequential elements (such as in a `for` loop) without having to construct dynamic strings at runtime.\n\nNote this macro only works with String literals and won't compile if used with a `char*` variable. To use a heap allocated `char*` string as an ID, use [CLAY_SIDI_LOCAL](#clay_sidi_local).\n\n---\n\n### CLAY_SIDI_LOCAL()\n\n`Clay_ElementId CLAY_SIDI_LOCAL(Clay_String idString, int32_t index)`\n\nA version of [CLAY_IDI_LOCAL](#clay_idi_local) that can be used with heap allocated `char *` data. The underlying `char` data will not be copied internally and should live until at least the next frame.\n\n---\n\n## Data Structures & Definitions\n\n### Clay_ElementDeclaration\nThe **Clay_ElementDeclaration** struct is the only argument to the `CLAY()` macro and provides configuration options for layout elements.\n\n```C\ntypedef struct {\n    Clay_ElementId id;\n    Clay_LayoutConfig layout;\n    Clay_Color backgroundColor;\n    Clay_CornerRadius cornerRadius;\n    Clay_AspectRatioElementConfig aspectRatio;\n    Clay_ImageElementConfig image;\n    Clay_FloatingElementConfig floating;\n    Clay_CustomElementConfig custom;\n    Clay_ClipElementConfig clip;\n    Clay_BorderElementConfig border;\n    void *userData;\n} Clay_ElementDeclaration;\n```\n\n**Fields**\n\n**`.layout`** - `Clay_LayoutConfig`\n\n`CLAY(CLAY_ID(\"Element\"), { .layout = { .padding = { 16, 16, 12, 12 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } })`\n\nUses [Clay_LayoutConfig](#clay_layoutconfig). Controls various settings related to _layout_, which can be thought of as \"the size and position of this element and its children\".\n\n---\n\n**`.backgroundColor`** - `Clay_Color`\n\n`CLAY(CLAY_ID(\"Element\"), { .backgroundColor = {120, 120, 120, 255} } })`\n\nUses [Clay_Color](#clay_color). Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.\n\n---\n\n**`.cornerRadius`** - `float`\n\n`CLAY(CLAY_ID(\"Element\"), { .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 } })`\n\nDefines the radius in pixels for the arc of rectangle corners (`0` is square, `rectangle.width / 2` is circular).\n\nNote that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to provide short hand for setting all four corner radii to the same value. e.g. `CLAY_BORDER({ .cornerRadius = CLAY_CORNER_RADIUS(10) })`\n\n---\n\n**`.aspectRatio`** - `Clay_AspectRatioElementConfig`\n\n`CLAY(CLAY_ID(\"Element\"), { .aspectRatio = 1 })`\n\nUses [Clay_AspectRatioElementConfig](#clay_aspectratioelementconfig). Configures the element as an aspect ratio scaling element. Especially useful for rendering images, but can also be used to enforce a fixed width / height ratio of other elements.\n\n---\n\n**`.image`** - `Clay_ImageElementConfig`\n\n`CLAY(CLAY_ID(\"Element\"), { .image = { .imageData = &myImage } })`\n\nUses [Clay_ImageElementConfig](#clay_imageelementconfig). Configures the element as an image element. Causes a render command with type `IMAGE` to be emitted.\n\n---\n\n**`.floating`** - `Clay_FloatingElementConfig`\n\n`CLAY(CLAY_ID(\"Element\"), { .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } })`\n\nUses [Clay_FloatingElementConfig](#clay_floatingelementconfig). Configures the element as an floating element, which allows it to stack \"in front\" and \"on top\" of other elements without affecting sibling or parent size or position. \n\n---\n\n**`.custom`** - `Clay_CustomElementConfig`\n\n`CLAY(CLAY_ID(\"Element\"), { .custom = { .customData = &my3DModel } })`\n\nUses [Clay_CustomElementConfig](#clay_customelementconfig). Configures the element as a custom element, which allows you to pass custom data through to the renderer. Causes a render command with type `CUSTOM` to be emitted.\n\n---\n\n**`.clip`** - `Clay_ClipElementConfig`\n\n`CLAY(CLAY_ID(\"Element\"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } })`\n\nUses [Clay_ClipElementConfig](#clay_scrollelementconfig). Configures the element as a clip element, which causes child elements to be clipped / masked if they overflow, and together with the functions listed in [Scrolling Elements](#scrolling-elements) enables scrolling of child contents.\n\n<img width=\"580\" alt=\"An image demonstrating the concept of clipping which prevents rendering of a child elements pixels if they fall outside the bounds of the parent element.\" src=\"https://github.com/user-attachments/assets/2eb83ff9-e186-4ea4-8a87-d90cbc0838b5\">\n\n---\n\n**`.border`** - `Clay_BorderElementConfig`\n\n`CLAY(CLAY_ID(\"Element\"), { .border = { .width = { .left = 5 }, .color = COLOR_BLUE } })`\n\nUses [Clay_BorderElementConfig](#clay_borderelementconfig). Configures the element as a border element, which instructs the renderer to draw coloured border lines along the perimeter of this element's bounding box. Causes a render command with type `BORDER` to be emitted.\n\n---\n\n**`.userData`** - `void *`\n\n`CLAY(CLAY_ID(\"Element\"), { .userData = &extraData })`\n\nTransparently passes a pointer through to the corresponding [Clay_RenderCommands](#clay_rendercommand)s generated by this element.\n\n---\n\n**Examples**\n\n```C\n// Declare a reusable rectangle config, with a purple color and 10px rounded corners\nClay_RectangleElementConfig rectangleConfig = (Clay_RectangleElementConfig) { .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) };\n// Declare a rectangle element using a reusable config\nCLAY(CLAY_ID(\"Box\"), rectangleConfig) {}\n// Declare a retangle element using an inline config\nCLAY(CLAY_ID(\"BoxInline\"), { .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) })) {\n    // child elements\n}\n// Declare a scrolling container with a colored background\nCLAY(CLAY_ID(\"ScrollingContainer\"), { \n    .backgroundColor = { 200, 200, 100, 255 }, \n    .cornerRadius = CLAY_CORNER_RADIUS(10)\n    .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }\n) {\n    // child elements\n}\n```\n\nElement is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE` will be created, with `renderCommand->elementConfig.rectangleElementConfig` containing a pointer to the element's Clay_RectangleElementConfig.\n\n### Clay_LayoutConfig\n\n**Clay_LayoutConfig** is used for configuring _layout_ options (i.e. options that affect the final position and size of an element, its parents, siblings, and children)\n\n**Struct API (Pseudocode)**\n\n```C\n// CLAY({ .layout = { ...fields } }) supports these options\nClay_LayoutConfig {\n    Clay_LayoutDirection layoutDirection = CLAY_LEFT_TO_RIGHT (default) | CLAY_TOP_TO_BOTTOM;\n    Clay_Padding padding {\n        u16 left; u16 right; u16 top; u16 bottom; \n    };\n    uint16_t childGap;\n    Clay_ChildAlignment childAlignment {\n        .x = CLAY_ALIGN_X_LEFT (default) | CLAY_ALIGN_X_CENTER | CLAY_ALIGN_X_RIGHT;\n        .y = CLAY_ALIGN_Y_TOP (default) | CLAY_ALIGN_Y_CENTER | CLAY_ALIGN_Y_BOTTOM;\n    };\n    Clay_Sizing sizing { // Recommended to use the provided macros here - see #sizing for more in depth explanation\n        .width = CLAY_SIZING_FIT(float min, float max) (default) | CLAY_SIZING_GROW(float min, float max) | CLAY_SIZING_FIXED(float width) | CLAY_SIZING_PERCENT(float percent)\n        .height = CLAY_SIZING_FIT(float min, float max) (default) | CLAY_SIZING_GROW(float min, float max) | CLAY_SIZING_FIXED(float height) | CLAY_SIZING_PERCENT(float percent)\n    }; // See CLAY_SIZING_GROW() etc for more details\n};\n```\n\n**Fields**\n\n**`.layoutDirection`** - `Clay_LayoutDirection`\n\n`CLAY(CLAY_ID(\"Element\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } })`\n\nControls the axis / direction in which child elements are laid out. Available options are `CLAY_LEFT_TO_RIGHT` (default) and `CLAY_TOP_TO_BOTTOM`.\n\n_Did you know that \"left to right\" and \"top to bottom\" both have 13 letters?_\n\n<img width=\"580\" alt=\"Screenshot 2024-08-22 at 11 10 27 AM\" src=\"https://github.com/user-attachments/assets/7008aa47-8826-4338-9257-8bc83f7813ce\">\n\n---\n\n**`.padding`** - `Clay_Padding`\n\n`CLAY(CLAY_ID(\"Element\"), { .layout = { .padding = { .left = 16, .right = 16, .top = 8, .bottom = 8 } } })`\n\nControls white-space \"padding\" around the **outside** of child elements.\n\n<img width=\"486\" alt=\"Screenshot 2024-08-22 at 10 50 49 AM\" src=\"https://github.com/user-attachments/assets/b454fa36-92d5-4b1d-bf8b-e4c25428e9de\">\n\n---\n\n**`.childGap`** - `uint16_t`\n\n`CLAY(CLAY_ID(\"Element\"), { .layout = { .childGap = 16 } })`\n\nControls the white-space **between** child elements as they are laid out. When `.layoutDirection` is `CLAY_LEFT_TO_RIGHT` (default), this will be horizontal space, whereas for `CLAY_TOP_TO_BOTTOM` it will be vertical space.\n\n<img width=\"600\" alt=\"Screenshot 2024-08-22 at 11 05 15 AM\" src=\"https://github.com/user-attachments/assets/fa0dae1f-1936-47f6-a299-634bd7d40d58\">\n\n---\n\n**`.childAlignment`** - `Clay_ChildAlignment`\n\n`CLAY(CLAY_ID(\"Element\"), { .layout = { .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER } } })`\n\nControls the alignment of children relative to the height and width of the parent container. Available options are:\n```C\n.x = CLAY_ALIGN_X_LEFT (default) | CLAY_ALIGN_X_CENTER | CLAY_ALIGN_X_RIGHT;\n.y = CLAY_ALIGN_Y_TOP (default) | CLAY_ALIGN_Y_CENTER | CLAY_ALIGN_Y_BOTTOM;\n```\n\n<img width=\"1030\" alt=\"Screenshot 2024-08-22 at 11 25 16 AM\" src=\"https://github.com/user-attachments/assets/be61b4a7-db4f-447c-b6d6-b2d4a91fc664\">\n\n---\n\n**`.sizing`** - `Clay_Sizing`\n\n`CLAY(CLAY_ID(\"Element\"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_PERCENT(0.5) } } })`\n\nControls how final width and height of element are calculated. The same configurations are available for both the `.width` and `.height` axis. There are several options:\n\n- `CLAY_SIZING_FIT(float min, float max) (default)` - The element will be sized to fit its children (plus padding and gaps), up to `max`. If `max` is left unspecified, it will default to `FLOAT_MAX`. When elements are compressed to fit into a smaller parent, this element will not shrink below `min`.\n\n- `CLAY_SIZING_GROW(float min, float max)` - The element will grow to fill available space in its parent, up to `max`. If `max` is left unspecified, it will default to `FLOAT_MAX`. When elements are compressed to fit into a smaller parent, this element will not shrink below `min`.\n\n- `CLAY_SIZING_FIXED(float fixed)` - The final size will always be exactly the provided `fixed` value. Shorthand for `CLAY_SIZING_FIT(fixed, fixed)`\n\n- `CLAY_SIZING_PERCENT(float percent)` - Final size will be a percentage of parent size, minus padding and child gaps. `percent` is assumed to be a float between `0` and `1`.\n\n<img width=\"1056\" alt=\"Screenshot 2024-08-22 at 2 10 33 PM\" src=\"https://github.com/user-attachments/assets/1236efb1-77dc-44cd-a207-7944e0f5e500\">\n\n<img width=\"1141\" alt=\"Screenshot 2024-08-22 at 2 19 04 PM\" src=\"https://github.com/user-attachments/assets/a26074ff-f155-4d35-9ca4-9278a64aac00\">\n\n\n**Example Usage**\n\n```C\nCLAY(CLAY_ID(\"Button\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16, .childGap = 16) } }) {\n    // Children will be laid out vertically with 16px of padding around and between\n}\n```\n\n---\n\n### Clay_ImageElementConfig\n**Usage**\n\n`CLAY(CLAY_ID(\"Element\"), { .image = { ...image config } }) {}`\n\n**Clay_ImageElementConfig** configures a clay element to render an image as its background.\n\n**Struct API (Pseudocode)**\n\n```C\nClay_ImageElementConfig {\n    void * imageData;\n};\n```\n\n**Fields**\n\n**`.imageData`** - `void *`\n\n`CLAY(CLAY_ID(\"Image\"), { .image = { .imageData = &myImage } }) {}`\n\n`.imageData` is a generic void pointer that can be used to pass through image data to the renderer.\n\n```C\n// Load an image somewhere in your code\nYourImage profilePicture = LoadYourImage(\"profilePicture.png\");\n// Note that when rendering, .imageData will be void* type.\nCLAY(CLAY_ID(\"Image\"), { .image = { .imageData = &profilePicture } }) {}\n```\n\n**Examples**\n\n```C\n// Load an image somewhere in your code\nYourImage profilePicture = LoadYourImage(\"profilePicture.png\");\n// Declare a reusable image config\nClay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture };\n// Declare an image element using a reusable config\nCLAY(CLAY_ID(\"Image\"), { .image = imageConfig }) {}\n// Declare an image element using an inline config\nCLAY(CLAY_ID(\"ImageInline\"), { .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}\n// Rendering example\nYourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;\n```\n\n**Rendering**\n\nElement is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_IMAGE` will be created. The user will need to access `renderCommand->renderData.image->imageData` to retrieve image data referenced during layout creation. It's also up to the user to decide how / if they wish to blend `renderCommand->renderData.image->backgroundColor` with the image.\n\n---\n\n### Clay_AspectRatioElementConfig\n\n**Usage**\n\n`CLAY(CLAY_ID(\"Aspect\"), { .aspectRatio = 16.0 / 9.0 }) {}`\n\n**Clay_AspectRatioElementConfig** configures a clay element to enforce a fixed width / height ratio in its final dimensions. Mostly used for image elements, but can also be used for non image elements.\n\n**Struct API (Pseudocode)**\n\n```C\nClay_AspectRatioElementConfig {\n    float aspectRatio;\n};\n```\n\n**Fields**\n\n**`.aspectRatio`** - `float`\n\n`CLAY(CLAY_ID(\"Aspect\"), { .aspectRatio = { .aspectRatio = 16.0 / 9.0 } }) {}`\n\nor alternatively, as C will automatically pass the value to the first nested struct field:\n\n`CLAY(CLAY_ID(\"Aspect\"), { .aspectRatio = 16.0 / 9.0 }) {}`\n\n**Examples**\n\n```C\n// Load an image somewhere in your code\nYourImage profilePicture = LoadYourImage(\"profilePicture.png\");\n// Declare an image element that will grow along the X axis while maintaining its original aspect ratio\nCLAY(CLAY_ID(\"ProfilePicture\"), {\n    .layout = { .width = CLAY_SIZING_GROW() },\n    .aspectRatio = profilePicture.width / profilePicture.height,\n    .image = { .imageData = &profilePicture },\n}) {}\n```\n\n---\n\n### Clay_ImageElementConfig\n**Usage**\n\n`CLAY(CLAY_ID(\"Image\"), { .image = { ...image config } }) {}`\n\n**Clay_ImageElementConfig** configures a clay element to render an image as its background.\n\n**Struct API (Pseudocode)**\n\n```C\nClay_ImageElementConfig {\n    void * imageData;\n};\n```\n\n**Fields**\n\n**`.imageData`** - `void *`\n\n`CLAY(CLAY_ID(\"Image\"), { .image = { .imageData = &myImage } }) {}`\n\n`.imageData` is a generic void pointer that can be used to pass through image data to the renderer.\n\n```C\n// Load an image somewhere in your code\nYourImage profilePicture = LoadYourImage(\"profilePicture.png\");\n// Note that when rendering, .imageData will be void* type.\nCLAY(CLAY_ID(\"Image\"), { .image = { .imageData = &profilePicture } }) {}\n```\n\nNote: for an image to maintain its original aspect ratio when using dynamic scaling, the [.aspectRatio](#clay_aspectratioelementconfig) config option must be used.\n\n**Examples**\n\n```C\n// Load an image somewhere in your code\nYourImage profilePicture = LoadYourImage(\"profilePicture.png\");\n// Declare a reusable image config\nClay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture };\n// Declare an image element using a reusable config\nCLAY(CLAY_ID(\"Image\"), { .image = imageConfig }) {}\n// Declare an image element using an inline config\nCLAY(CLAY_ID(\"ImageInline\"), { .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}\n// Rendering example\nYourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;\n```\n\n**Rendering**\n\nElement is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_IMAGE` will be created. The user will need to access `renderCommand->renderData.image->imageData` to retrieve image data referenced during layout creation. It's also up to the user to decide how / if they wish to blend `renderCommand->renderData.image->backgroundColor` with the image.\n\n---\n\n### Clay_ClipElementConfig\n\n**Usage**\n\n`CLAY(CLAY_ID(\"ScrollBox\"), { .clip = { ...clip config } }) {}`\n\n**Notes**\n\n`Clay_ClipElementConfig` configures the element as a clipping container, enabling masking of children that extend beyond its boundaries.\n\nNote: In order to process scrolling based on pointer position and mouse wheel or touch interactions, you must call `Clay_SetPointerState()` and `Clay_UpdateScrollContainers()` _before_ calling `BeginLayout`.\n\n**Struct Definition (Pseudocode)**\n\n```C\nClay_ClipElementConfig {\n    bool horizontal;\n    bool vertical;\n};\n```\n\n**Fields**\n\n**`.horizontal`** - `bool`\n\n`CLAY(CLAY_ID(\"HorizontalScroll\"), { .clip = { .horizontal = true } })`\n\nEnables or disables horizontal clipping for this container element.\n\n---\n\n**`.vertical`** - `bool`\n\n`CLAY(LAY_ID(\"VerticalScroll\"), { .clip = { .vertical = true } })`\n\nEnables or disables vertical clipping for this container element.\n\n---\n\n**Rendering**\n\nEnabling clip for an element will result in two additional render commands: \n- `commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START`, which should create a rectangle mask with its `boundingBox` and is **not** subject to [culling](#visibility-culling)\n- `commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END`, which disables the previous rectangle mask and is **not** subject to [culling](#visibility-culling)\n\n**Examples**\n\n```C\nCLAY(CLAY_ID(\"ScrollOuter\"), { .clip = { .vertical = true } }) {\n    // Create child content with a fixed height of 5000\n    CLAY(CLAY_ID(\"ScrollInner\"), { .layout = { .sizing = { .height = CLAY_SIZING_FIXED(5000) } } }) {}\n}\n```\n\n---\n\n### Clay_BorderElementConfig\n\n**Usage**\n\n`CLAY(CLAY_ID(\"Border\"), { .border = { ...border config } }) {}`\n\n**Notes**\n\n`Clay_BorderElementConfig` adds borders to the edges or between the children of elements. It uses Clay_BorderElementConfig to configure border specific options.\n\n**Struct Definition (Pseudocode)**\n\n```C\ntypedef struct Clay_BorderElementConfig\n{\n    Clay_Color color {\n        float r; float g; float b; float a;\n    };\n    Clay_BorderWidth width {\n        uint16_t left;\n        uint16_t right;\n        uint16_t top;\n        uint16_t bottom;\n        uint16_t betweenChildren;  \n    };\n} Clay_BorderElementConfig;\n```\n\n**Fields**\n\n**`.color`** - `Clay_Color`\n\n`CLAY(CLAY_ID(\"Border\"), { .border = { .color = { 255, 0, 0, 255 } } })`\n\nUses [Clay_Color](#clay_color). Specifies the shared color for all borders configured by this element. Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.\n\n---\n\n**`.width`** - `Clay_BorderWidth`\n\n`CLAY(CLAY_ID(\"Border\"), { .border = { .width = { .left = 2, .right = 10 } } })`\n\nIndicates to the renderer that a border of `.color` should be draw at the specified edges of the bounding box, **inset and overlapping the box contents by `.width`**.\n\nThis means that border configuration does not affect layout, as the width of the border doesn't contribute to the total container width or layout position. Border containers with zero padding will be drawn over the top of child elements.\n\nNote:\n\n**`.width.betweenChildren`**\n\n`CLAY(CLAY_ID(\"Border\"), { .border = { .width = { .betweenChildren = 2 } }, .color = COLOR_RED })`\n\nConfigures the width and color of borders to be drawn between children. These borders will be vertical lines if the parent uses `.layoutDirection = CLAY_LEFT_TO_RIGHT` and horizontal lines if the parent uses `CLAY_TOP_TO_BOTTOM`. Unlike `.left, .top` etc, this option **will generate additional rectangle render commands representing the borders between children.** As a result, the renderer does not need to specifically implement rendering for these border elements.\n\n---\n\n**Examples**\n\n```C\n// 300x300 container with a 1px red border around all the edges\nCLAY(CLAY_ID(\"OuterBorder\"), {\n    .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) } },\n    .border = { .width = { 1, 1, 1, 1, 0 }, .color = COLOR_RED }\n}) {\n    // ...\n}\n\n// Container with a 3px yellow bottom border\nCLAY(CLAY_ID(\"OuterBorder\"), {\n    .border = { .width = { .bottom = 3 }, .color = COLOR_YELLOW }\n}) {\n    // ...\n}\n\n// Container with a 5px curved border around the edges, and a 5px blue border between all children laid out top to bottom\nCLAY(CLAY_ID(\"OuterBorder\"), {\n    .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },\n    .border = { .width = { 5, 5, 5, 5, 5 }, .color = COLOR_BLUE }\n}) {\n    // Child\n    // -- 5px blue border will be here --\n    // Child\n    // -- 5px blue border will be here --\n    // Child\n}\n```\n\n**Rendering**\n\nElement is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand` with `commandType = CLAY_RENDER_COMMAND_TYPE_BORDER` representing the container will be created.\nRendering of borders and rounded corners is left up to the user. See the provided [Raylib Renderer](https://github.com/nicbarker/clay/tree/main/renderers/raylib) for examples of how to draw borders using line and curve primitives.\n\n---\n\n### Clay_FloatingElementConfig\n\n**Usage**\n\n`CLAY(CLAY_ID(\"Floating\"), { .floating = { ...floating config } }) {}`\n\n**Notes**\n\n**Floating Elements** defines an element that \"floats\" above other content. Typical use-cases include tooltips and modals.\n\nFloating containers:\n\n- With the default configuration, attach to the top left corner of their \"parent\" \n- Don't affect the width and height of their parent\n- Don't affect the positioning of sibling elements\n- Depending on their z-index can appear above or below other elements, partially or completely occluding them\n- Apart from positioning, function just like standard elements - including expanding to fit their children, etc.\n\nThe easiest mental model to use when thinking about floating containers is that they are a completely separate UI hierarchy, attached to a specific x,y point on their \"parent\".\n\nFloating elements uses `Clay_FloatingElementConfig` to configure specific options.\n\n**Struct Definition (Pseudocode)**\n\n```C\nClay_FloatingElementConfig {\n    Clay_Vector2 offset {\n        float x, float y\n    };\n    Clay_Dimensions expand {\n        float width, float height\n    };\n    uint32_t parentId;\n    int16_t zIndex;\n    Clay_FloatingAttachPoints attachPoint {\n        .element = CLAY_ATTACH_POINT_LEFT_TOP (default) | CLAY_ATTACH_POINT_LEFT_CENTER | CLAY_ATTACH_POINT_LEFT_BOTTOM | CLAY_ATTACH_POINT_CENTER_TOP | CLAY_ATTACH_POINT_CENTER_CENTER | CLAY_ATTACH_POINT_CENTER_BOTTOM | CLAY_ATTACH_POINT_RIGHT_TOP | CLAY_ATTACH_POINT_RIGHT_CENTER | CLAY_ATTACH_POINT_RIGHT_BOTTOM\n        .parent = CLAY_ATTACH_POINT_LEFT_TOP (default) | CLAY_ATTACH_POINT_LEFT_CENTER | CLAY_ATTACH_POINT_LEFT_BOTTOM | CLAY_ATTACH_POINT_CENTER_TOP | CLAY_ATTACH_POINT_CENTER_CENTER | CLAY_ATTACH_POINT_CENTER_BOTTOM | CLAY_ATTACH_POINT_RIGHT_TOP | CLAY_ATTACH_POINT_RIGHT_CENTER | CLAY_ATTACH_POINT_RIGHT_BOTTOM\n    };\n    Clay_FloatingAttachToElement attachTo {\n        CLAY_POINTER_CAPTURE_MODE_CAPTURE (default),\n        CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH\n    };\n    Clay_FloatingAttachToElement attachTo {\n        CLAY_ATTACH_TO_NONE (default),\n        CLAY_ATTACH_TO_PARENT,\n        CLAY_ATTACH_TO_ELEMENT_WITH_ID,\n        CLAY_ATTACH_TO_ROOT,\n    };\n};\n```\n\n**Fields**\n\n**`.offset`** - `Clay_Vector2`\n\n`CLAY(CLAY_ID(\"Floating\"), { .floating = { .offset = { -24, -24 } } })`\n\nUsed to apply a position offset to the floating container _after_ all other layout has been calculated. \n\n---\n\n**`.expand`** - `Clay_Dimensions`\n\n`CLAY(CLAY_ID(\"Floating\"), { .floating = { .expand = { 16, 16 } } })`\n\nUsed to expand the width and height of the floating container _before_ laying out child elements.\n\n---\n\n**`.zIndex`** - `float`\n\n`CLAY(CLAY_ID(\"Floating\"), { .floating = { .zIndex = 1 } })`\n\nAll floating elements (as well as their entire child hierarchies) will be sorted by `.zIndex` order before being converted to render commands. If render commands are drawn in order, elements with higher `.zIndex` values will be drawn on top.\n\n---\n\n**`.parentId`** - `uint32_t`\n\n`CLAY(CLAY_ID(\"Floating\"), { .floating = { .parentId = Clay_GetElementId(\"HeaderButton\").id } })`\n\nBy default, floating containers will \"attach\" to the parent element that they are declared inside. However, there are cases where this limitation could cause significant performance or ergonomics problems. `.parentId` allows you to specify a `CLAY_ID().id` to attach the floating container to. The parent element with the matching id can be declared anywhere in the hierarchy, it doesn't need to be declared before or after the floating container in particular.  \n\nConsider the following case:\n```C\n// Load an image somewhere in your code\nCLAY(CLAY_IDI(\"SidebarButton\", 1), { }) {\n    // .. some button contents\n    if (tooltip.attachedButtonIndex == 1) {\n        CLAY(CLAY_ID(\"OptionTooltip\"), { /* floating config... */ })\n    }\n}\nCLAY(CLAY_IDI(\"SidebarButton\", 2), { }) {\n    // .. some button contents\n    if (tooltip.attachedButtonIndex == 2) {\n        CLAY(CLAY_ID(\"OptionTooltip\"), { /* floating config... */ })\n    }\n}\nCLAY(CLAY_IDI(\"SidebarButton\", 3), { }) {\n    // .. some button contents\n    if (tooltip.attachedButtonIndex == 3) {\n        CLAY(CLAY_ID(\"OptionTooltip\"), { /* floating config... */ })\n    }\n}\nCLAY(CLAY_IDI(\"SidebarButton\", 4), { }) {\n    // .. some button contents\n    if (tooltip.attachedButtonIndex == 4) {\n        CLAY(CLAY_ID(\"OptionTooltip\"), { /* floating config... */ })\n    }\n}\nCLAY(CLAY_IDI(\"SidebarButton\", 5), { }) {\n    // .. some button contents\n    if (tooltip.attachedButtonIndex == 5) {\n        CLAY(CLAY_ID(\"OptionTooltip\"), { /* floating config... */ })\n    }\n}\n```\n\nThe definition of the above UI is significantly polluted by the need to conditionally render floating tooltips as a child of many possible elements. The alternative, using `parentId`, looks like this:\n\n```C\n// Load an image somewhere in your code\nCLAY(CLAY_IDI(\"SidebarButton\", 1), { }) {\n    // .. some button contents\n}\nCLAY(CLAY_IDI(\"SidebarButton\", 2), { }) {\n    // .. some button contents\n}\nCLAY(CLAY_IDI(\"SidebarButton\", 3), { }) {\n    // .. some button contents\n}\nCLAY(CLAY_IDI(\"SidebarButton\", 4), { }) {\n    // .. some button contents\n}\nCLAY(CLAY_IDI(\"SidebarButton\", 5), { }) {\n    // .. some button contents\n}\n\n// Any other point in the hierarchy\nCLAY(CLAY_ID(\"OptionTooltip\"), { .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_ID, .parentId = CLAY_IDI(\"SidebarButton\", tooltip.attachedButtonIndex).id }) {\n    // Tooltip contents...\n}\n```\n\n---\n\n**`.attachment`** - `Clay_FloatingAttachPoints`\n\n`CLAY(CLAY_ID(\"Floating\"), { .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } }) {}`\n\nIn terms of positioning the floating container, `.attachment` specifies \n\n- The point on the floating container (`.element`)\n- The point on the parent element that it \"attaches\" to (`.parent`)\n\n![Screenshot 2024-08-23 at 11 47 21 AM](https://github.com/user-attachments/assets/b8c6dfaa-c1b1-41a4-be55-013473e4a6ce)\n\nYou can mentally visualise this as finding a point on the floating container, then finding a point on the parent, and lining them up over the top of one another.\n\nFor example:\n\n\"Attach the LEFT_CENTER of the floating container to the RIGHT_TOP of the parent\"\n\n`CLAY(CLAY_ID(\"Floating\"), { .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } });`\n\n![Screenshot 2024-08-23 at 11 53 24 AM](https://github.com/user-attachments/assets/ebe75e0d-1904-46b0-982d-418f929d1516)\n\n**`.pointerCaptureMode`** - `Clay_PointerCaptureMode`\n\n`CLAY({ .floating = { .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_CAPTURE } })`\n\nControls whether pointer events like hover and click should pass through to content underneath this floating element, or whether the pointer should be \"captured\" by this floating element. Defaults to `CLAY_POINTER_CAPTURE_MODE_CAPTURE`. \n\n**Examples**\n\n```C\n// Horizontal container with three option buttons\nCLAY(CLAY_ID(\"OptionsList\"), { .layout = { childGap = 16 } }) {\n    CLAY(CLAY_IDI(\"Option\", 1), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {\n        CLAY_TEXT(CLAY_STRING(\"Option 1\"), CLAY_TEXT_CONFIG());\n    }\n    CLAY(CLAY_IDI(\"Option\", 2), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {\n        CLAY_TEXT(CLAY_STRING(\"Option 2\"), CLAY_TEXT_CONFIG());\n        // Floating tooltip will attach above the \"Option 2\" container and not affect widths or positions of other elements\n        CLAY(CLAY_ID(\"OptionTooltip\"), { .floating = { .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_TOP } } }) {\n            CLAY_TEXT(CLAY_STRING(\"Most popular!\"), CLAY_TEXT_CONFIG());\n        }\n    }\n    CLAY(CLAY_IDI(\"Option\", 3), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {\n        CLAY_TEXT(CLAY_STRING(\"Option 3\"), CLAY_TEXT_CONFIG());\n    }\n}\n\n// Floating containers can also be declared elsewhere in a layout, to avoid branching or polluting other UI\nfor (int i = 0; i < 1000; i++) {\n    CLAY(CLAY_IDI(\"Option\", i + 1), { }) {\n        // ...\n    }\n}\n// Note the use of \"parentId\".\n// Floating tooltip will attach above the \"Option 2\" container and not affect widths or positions of other elements\nCLAY(CLAY_ID(\"OptionTooltip\"), { .floating = { .parentId = CLAY_IDI(\"Option\", 2).id, .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_TOP_CENTER } } }) {\n    CLAY_TEXT(CLAY_STRING(\"Most popular!\"), CLAY_TEXT_CONFIG());\n}\n```\n\nWhen using `.parentId`, the floating container can be declared anywhere after `BeginLayout` and before `EndLayout`. The target element matching the `.parentId` doesn't need to exist when `Clay_FloatingElementConfig` is used.\n\n**Rendering**\n\n`Clay_FloatingElementConfig` will not generate any specific render commands.\n\n---\n\n### Clay_CustomElementConfig\n\n**Usage**\n\n`CLAY(CLAY_ID(\"Custom\"), { .custom = { .customData = &something } }) {}`\n\n**Notes**\n\n**Clay_CustomElementConfig** allows the user to pass custom data to the renderer. \n\n**Struct Definition (Pseudocode)** \n\n```C\ntypedef struct\n{\n    void * customData;\n} Clay_CustomElementConfig;\n```\n\n**Fields**\n\n`.customData` - `void *`\n\n`CLAY({ .custom = { .customData = &myCustomData } })`\n\n`.customData` is a generic void pointer that can be used to pass through custom data to the renderer.\n\n**Examples**\n```C\n#include \"clay.h\"\n\ntypedef enum {\n    CUSTOM_ELEMENT_TYPE_MODEL,\n    CUSTOM_ELEMENT_TYPE_VIDEO\n} CustomElementType;\n\n// A rough example of how you could handle laying out 3d models in your UI\ntypedef struct {\n    CustomElementType type;\n    union {\n        Model model;\n        Video video;\n        // ...\n    };\n} CustomElementData;\n\nModel myModel = Load3DModel(filePath);\nCustomElement modelElement = (CustomElement) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel }\n\ntypedef struct {\n    void* memory;\n    uintptr_t offset;\n} Arena;\n\n// During init\nArena frameArena = (Arena) { .memory = malloc(1024) };\n\n// ...\nCLAY(0) {\n    // Custom elements only take a single pointer, so we need to store the data somewhere\n    CustomElementData *modelData = (CustomElementData *)(frameArena.memory + frameArena.offset);\n    *modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };\n    frameArena.offset += sizeof(CustomElementData);\n    CLAY(CLAY_ID(\"3DModelViewer\"), { .custom = { .customData = modelData } }) {}\n}\n\n// Later during your rendering\nswitch (renderCommand->commandType) {\n    // ...\n    case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {\n        // Your extended struct is passed through\n        CustomElementData *customElement = renderCommand->config.customElementConfig->customData;\n        if (!customElement) continue;\n        switch (customElement->type) {\n            case CUSTOM_ELEMENT_TYPE_MODEL: {\n                // Render your 3d model here\n                break;\n            }\n            case CUSTOM_ELEMENT_TYPE_VIDEO: {\n                // Render your video here\n                break;\n            }\n            // ...\n        }\n        break;\n    }\n}\n```\n\n**Rendering**\n\nElement is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand` with `commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM` will be created.\n\n### Clay_Color\n\n```C\ntypedef struct {\n    float r, g, b, a;\n} Clay_Color;\n```\n\n`Clay_Color` is an RGBA color struct used in Clay's declarations and rendering. By convention the channels are represented as 0-255, but this is left up to the renderer.\nNote: when using the debug tools, their internal colors are represented as 0-255.\n\n### Clay_String\n\n```C\ntypedef struct {\n    bool isStaticallyAllocated;\n    int32_t length;\n    const char *chars;\n} Clay_String;\n```\n\n`Clay_String` is a string container that clay uses internally to represent all strings.\n\n**Fields**\n\n**`.isStaticallyAllocated`** - `bool`\n\nWhether or not the string is statically allocated, or in other words, whether\nor not it lives for the entire lifetime of the program.\n\n---\n\n**`.length`** - `int32_t`\n\nThe number of characters in the string, _not including an optional null terminator._\n\n---\n\n**`.chars`** - `const char *`\n\nA pointer to the contents of the string. This data is not guaranteed to be null terminated, so if you are passing it to code that expects standard null terminated C strings, you will need to copy the data and append a null terminator.\n\n---\n\n### Clay_ElementId\n\n```C\ntypedef struct {\n    uint32_t id;\n    uint32_t offset;\n    uint32_t baseId;\n    Clay_String stringId;\n} Clay_ElementId;\n```\n\nReturned by [CLAY_ID](#clay_id) and [CLAY_IDI](#clay_idi), this struct contains a hash id, as well as the source string that was used to generate it.\n\n**Fields**\n\n**`.id`** - `uint32_t`\n\nA unique ID derived from the string passed to [CLAY_ID](#clay_id) or [CLAY_IDI](#clay_idi).\n\n---\n\n**`.offset`** - `uint32_t`\n\nIf this id was generated using [CLAY_IDI](#clay_idi), `.offset` is the value passed as the second argument. For [CLAY_ID](#clay_id), this will always be `0`.\n\n---\n\n**`.baseId`** - `uint32_t`\n\nIf this id was generated using [CLAY_IDI](#clay_idi), `.baseId` is the hash of the base string passed, **before it is additionally hashed with `.offset`**. For [CLAY_ID](#clay_id), this will always be the same as `.id`.\n\n---\n\n**`.stringId`** - `Clay_String`\n\nStores the original string that was passed in when [CLAY_ID](#clay_id) or [CLAY_IDI](#clay_idi) were called.\n\n---\n\n### Clay_RenderCommandArray\n\n```C\ntypedef struct\n{\n\tuint32_t capacity;\n\tuint32_t length;\n\tClay_RenderCommand *internalArray;\n} Clay_RenderCommandArray;\n```\n\nReturned by [Clay_EndLayout](#clay_endlayout), this array contains the [Clay_RenderCommand](#clay_rendercommand)s representing the calculated layout.\n\n**Fields**\n\n**`.capacity`** - `uint32_t`\n\nRepresents the total capacity of the allocated memory in `.internalArray`.\n\n---\n\n**`.length`** - `uint32_t`\n\nRepresents the total number of `Clay_RenderCommand` elements stored consecutively at the address `.internalArray`.\n\n---\n\n**`.internalArray`** - `Clay_RenderCommand`\n\nAn array of [Clay_RenderCommand](#clay_rendercommand)s representing the calculated layout. If there was at least one render command, this array will contain elements from `.internalArray[0]` to `.internalArray[.length - 1]`.\n\n---\n\n### Clay_RenderCommand\n\n```C\ntypedef struct {\n    Clay_BoundingBox boundingBox;\n    Clay_RenderData renderData;\n    uintptr_t userData;\n    uint32_t id;\n    int16_t zIndex;\n    Clay_RenderCommandType commandType;\n} Clay_RenderCommand;\n```\n\n**Fields**\n\n**`.commandType`** - `Clay_RenderCommandType`\n\nAn enum indicating how this render command should be handled. Possible values include:\n\n- `CLAY_RENDER_COMMAND_TYPE_NONE` - Should be ignored by the renderer, and never emitted by clay under normal conditions.\n- `CLAY_RENDER_COMMAND_TYPE_RECTANGLE` - A rectangle should be drawn, configured with `.config.rectangleElementConfig`\n- `CLAY_RENDER_COMMAND_TYPE_BORDER` - A border should be drawn, configured with `.config.borderElementConfig`\n- `CLAY_RENDER_COMMAND_TYPE_TEXT` - Text should be drawn, configured with `.config.textElementConfig`\n- `CLAY_RENDER_COMMAND_TYPE_IMAGE` - An image should be drawn, configured with `.config.imageElementConfig`\n- `CLAY_RENDER_COMMAND_TYPE_SCISSOR_START` - Named after [glScissor](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glScissor.xhtml), this indicates that the renderer should begin culling any subsequent pixels that are drawn outside the `.boundingBox` of this render command.\n- `CLAY_RENDER_COMMAND_TYPE_SCISSOR_END` - Only ever appears after a matching `CLAY_RENDER_COMMAND_TYPE_SCISSOR_START` command, and indicates that the scissor has ended.\n- `CLAY_RENDER_COMMAND_TYPE_CUSTOM` - A custom render command controlled by the user, configured with `.config.customElementConfig`\n\n---\n\n**`.boundingBox`** - `Clay_BoundingBox`\n\n```C\ntypedef struct {\n    float x, y, width, height;\n} Clay_BoundingBox;\n```\n\nA rectangle representing the bounding box of this render command, with `.x` and `.y` representing the top left corner of the element.\n\n---\n\n**`.id`** - `uint32_t`\n\nThe id that was originally used with the element macro that created this render command. See [CLAY_ID](#clay_id) for details.\n\n---\n\n**`.zIndex`** - `int16_t`\n\nThe z index of the element, based on what was passed to the root floating configuration that this element is a child of.\nHigher z indexes should be rendered _on top_ of lower z indexes.\n\n---\n\n**`.renderData`** - `Clay_RenderData`\n\n```C\ntypedef union {\n    Clay_RectangleRenderData rectangle;\n    Clay_TextRenderData text;\n    Clay_ImageRenderData image;\n    Clay_CustomRenderData custom;\n    Clay_BorderRenderData border;\n} Clay_RenderData;\n```\n\nA C union containing various structs, with the type dependent on `.commandType`. Possible values include:\n\n- `config.rectangle` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE`.\n- `config.text` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_TEXT`. See [Clay_Text](#clay_text) for details.\n- `config.image` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE`. See [Clay_Image](#clay_imageelementconfig) for details.\n- `config.border` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_BORDER`. See [Clay_Border](#clay_borderelementconfig) for details.\n- `config.custom` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM`. See [Clay_Custom](#clay_customelementconfig) for details.\n\n**Union Structs**\n\n```C\ntypedef struct {\n    Clay_StringSlice stringContents;\n    Clay_Color textColor;\n    uint16_t fontId;\n    uint16_t fontSize;\n    uint16_t letterSpacing;\n    uint16_t lineHeight;\n} Clay_TextRenderData;\n```\n\n```C\ntypedef struct {\n    Clay_Color backgroundColor;\n    Clay_CornerRadius cornerRadius;\n} Clay_RectangleRenderData;\n```\n\n```C\ntypedef struct {\n    Clay_Color backgroundColor;\n    Clay_CornerRadius cornerRadius;\n    void* imageData;\n} Clay_ImageRenderData;\n```\n\n```C\ntypedef struct {\n    Clay_Color backgroundColor;\n    Clay_CornerRadius cornerRadius;\n    void* customData;\n} Clay_CustomRenderData;\n```\n\n```C\ntypedef struct {\n    Clay_Color color;\n    Clay_CornerRadius cornerRadius;\n    Clay_BorderWidth width;\n} Clay_BorderRenderData;\n```\n\n```C\ntypedef union {\n    Clay_RectangleRenderData rectangle;\n    Clay_TextRenderData text;\n    Clay_ImageRenderData image;\n    Clay_CustomRenderData custom;\n    Clay_BorderRenderData border;\n} Clay_RenderData;\n```\n\n### Clay_ScrollContainerData\n\n```C\n// Data representing the current internal state of a scrolling element.\ntypedef struct {\n    // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout.\n    // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling.\n    Clay_Vector2 *scrollPosition;\n    // The bounding box of the scroll element.\n    Clay_Dimensions scrollContainerDimensions;\n    // The outer dimensions of the inner scroll container content, including the padding of the parent scroll container.\n    Clay_Dimensions contentDimensions;\n    // The config that was originally passed to the scroll element.\n    Clay_ClipElementConfig config;\n    // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned.\n    bool found;\n} Clay_ScrollContainerData;\n```\n\n**Fields**\n\n**`.scrollPosition`** - `Clay_Vector2 *`\n\nA pointer to the internal scroll position of this scroll container. Mutating it will result in elements inside the scroll container shifting up / down (`.y`) or left / right (`.x`).\n\n---\n\n**`.scrollContainerDimensions`** - `Clay_Dimensions`\n\n```C\ntypedef struct {\n    float width, height;\n} Clay_Dimensions;\n```\n\nDimensions representing the outer width and height of the scroll container itself.\n\n---\n\n**`.contentDimensions`** - `Clay_Dimensions`\n\n```C\ntypedef struct {\n    float width, height;\n} Clay_Dimensions;\n```\n\nDimensions representing the inner width and height of the content _inside_ the scroll container. Scrolling is only possible when the `contentDimensions` are larger in at least one dimension than the `scrollContainerDimensions`.\n\n---\n\n**`.config`** - `Clay_ClipElementConfig`\n\nThe [Clay_ClipElementConfig](#clay_scroll) for the matching scroll container element.\n\n---\n\n### Clay_ElementData\n\n```C\n// Bounding box and other data for a specific UI element.\ntypedef struct {\n    // The rectangle that encloses this UI element, with the position relative to the root of the layout.\n    Clay_BoundingBox boundingBox;\n    // Indicates whether an actual Element matched the provided ID or if the default struct was returned.\n    bool found;\n} Clay_ElementData;\n```\n\n**Fields**\n\n**`.boundingBox`** - `Clay_BoundingBox`\n\n```C\ntypedef struct {\n    float x, y, width, height;\n} Clay_BoundingBox;\n```\n\nA rectangle representing the bounding box of this render command, with `.x` and `.y` representing the top left corner of the element.\n\n---\n\n**`.found`** - `bool`\n\nA boolean representing whether or not the ID passed to [Clay_GetElementData](#clay_getelementdata) matched a valid element or not. In the case that `.found` is `false`, `.boundingBox` will be the default value (zeroed).\n\n---\n\n### Clay_PointerData\n\n```C\ntypedef struct\n{\n    Clay_Vector2 position;\n    Clay_PointerDataInteractionState state;\n} Clay_PointerData;\n```\n\n**Fields**\n\n**`.position`** - `Clay_Vector2`\n\nA Vector2 containing the current x,y coordinates of the mouse pointer, which were originally passed into [Clay_SetPointerState()](#clay_setpointerstate).\n\n---\n\n**`.state`** - `Clay_PointerDataInteractionState`\n\n```C\ntypedef enum\n{\n    CLAY_POINTER_DATA_PRESSED_THIS_FRAME,\n    CLAY_POINTER_DATA_PRESSED,\n    CLAY_POINTER_DATA_RELEASED_THIS_FRAME,\n    CLAY_POINTER_DATA_RELEASED,\n} Clay_PointerDataInteractionState;\n```\n\nAn enum value representing the current \"state\" of the pointer interaction. As an example, consider the case where a user is on a desktop computer, moves the mouse pointer over a button, clicks and holds the left mouse button for a short time, then releases it:\n\n- While the mouse pointer is over (\"hovering\") the button, but no mouse button has been pressed: `CLAY_POINTER_DATA_RELEASED`\n- First frame that the user presses the left mouse button: `CLAY_POINTER_DATA_PRESSED_THIS_FRAME`\n- All subsequent frames where the user is still holding the left mouse button: `CLAY_POINTER_DATA_PRESSED`\n- The single frame where the left mouse button goes from pressed -> released: `CLAY_POINTER_DATA_RELEASED_THIS_FRAME`\n- All subsequent frames while the mouse pointer is still over the button: `CLAY_POINTER_DATA_RELEASED`\n\n---\n\n### Clay_ErrorHandler\n\n```C\ntypedef struct\n{\n    void (*errorHandlerFunction)(Clay_ErrorData errorText);\n    uintptr_t userData;\n} Clay_ErrorHandler;\n```\n\n**Fields**\n\n**`.errorHandlerFunction`** - `void (Clay_ErrorData errorText) {}`\n\nA function pointer to an error handler function, which takes `Clay_ErrorData` as an argument. This function will be called whenever Clay encounters an internal error.\n\n---\n\n**`.userData`** - `uintptr_t`\n\nA generic pointer to extra userdata that is transparently passed through from `Clay_Initialize` to Clay's error handler callback. Defaults to NULL.\n\n---\n\n### Clay_ErrorData\n\n```C\ntypedef struct\n{\n    Clay_ErrorType errorType;\n    Clay_String errorText;\n    uintptr_t userData;\n} Clay_ErrorData;\n```\n\n**Fields**\n\n**`.errorType`** - `Clay_ErrorType`\n\n```C\ntypedef enum {\n    CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,\n    CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,\n    CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,\n    CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED,\n    CLAY_ERROR_TYPE_DUPLICATE_ID,\n    CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND,\n    CLAY_ERROR_TYPE_INTERNAL_ERROR,\n} Clay_ErrorType;\n```\n\nAn enum representing the type of error Clay encountered. It's up to the user to handle on a case by case basis, but as some general guidance:\n\n- `CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED` - The user is attempting to use `CLAY_TEXT` and either forgot to call [Clay_SetMeasureTextFunction](#clay_setmeasuretextfunction) or accidentally passed a null function pointer.\n- `CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED` - Clay was initialized with an Arena that was too small for the configured [Clay_SetMaxElementCount](#clay_setmaxelementcount). Try using [Clay_MinMemorySize()](#clay_minmemorysize) to get the exact number of bytes required by the current configuration.\n- `CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED` - The declared UI hierarchy has too many elements for the configured max element count. Use [Clay_SetMaxElementCount](#clay_setmaxelementcount) to increase the max, then call [Clay_MinMemorySize()](#clay_minmemorysize) again and reinitialize clay's memory with the required size.\n- `CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED` - The declared UI hierarchy has too much text for the configured text measure cache size. Use [Clay_SetMaxMeasureTextCacheWordCount](#clay_setmeasuretextcachesize) to increase the max, then call [Clay_MinMemorySize()](#clay_minmemorysize) again and reinitialize clay's memory with the required size.\n- `CLAY_ERROR_TYPE_DUPLICATE_ID` - Two elements in Clays UI Hierarchy have been declared with exactly the same ID. Set a breakpoint in your error handler function for a stack trace back to exactly where this occured.\n- `CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND` - A `CLAY_FLOATING` element was declared with the `.parentId` property, but no element with that ID was found. Set a breakpoint in your error handler function for a stack trace back to exactly where this occured.\n- `CLAY_ERROR_TYPE_INTERNAL_ERROR` - Clay has encountered an internal logic or memory error. Please report this as a bug with a stack trace to help us fix these!\n\n---\n\n**`.errorText`** - `Clay_String`\n\nA [Clay_String](#clay_string) that provides a human readable description of the error. May change in future and should not be relied on to detect error types.\n\n---\n\n**`.userData`** - `uintptr_t`\n\nA generic pointer to extra userdata that is transparently passed through from `Clay_Initialize` to Clay's error handler callback. Defaults to NULL.\n\n---\n"
  },
  {
    "path": "bindings/cpp/README.md",
    "content": "https://github.com/TimothyHoytBSME/ClayMan"
  },
  {
    "path": "bindings/csharp/README",
    "content": "https://github.com/Orcolom/clay-cs\n"
  },
  {
    "path": "bindings/rust/README",
    "content": "https://github.com/clay-ui-rs/clay\nhttps://crates.io/crates/clay-layout"
  },
  {
    "path": "bindings/zig/README",
    "content": "https://codeberg.org/Zettexe/clay-zig\nhttps://github.com/johan0A/clay-zig-bindings\n"
  },
  {
    "path": "clay.h",
    "content": "// VERSION: 0.14\n\n/*\n    NOTE: In order to use this library you must define\n    the following macro in exactly one file, _before_ including clay.h:\n\n    #define CLAY_IMPLEMENTATION\n    #include \"clay.h\"\n\n    See the examples folder for details.\n*/\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <stddef.h>\n\n// SIMD includes on supported platforms\n#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))\n#include <emmintrin.h>\n#elif !defined(CLAY_DISABLE_SIMD) && defined(__aarch64__)\n#include <arm_neon.h>\n#endif\n\n// -----------------------------------------\n// HEADER DECLARATIONS ---------------------\n// -----------------------------------------\n\n#ifndef CLAY_HEADER\n#define CLAY_HEADER\n\n#if !( \\\n    (defined(__cplusplus) && __cplusplus >= 202002L) || \\\n    (defined(__STDC__) && __STDC__ == 1 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \\\n    defined(_MSC_VER) || \\\n    defined(__OBJC__) \\\n)\n#error \"Clay requires C99, C++20, or MSVC\"\n#endif\n\n#ifdef CLAY_WASM\n#define CLAY_WASM_EXPORT(name) __attribute__((export_name(name)))\n#else\n#define CLAY_WASM_EXPORT(null)\n#endif\n\n#ifdef CLAY_DLL\n#define CLAY_DLL_EXPORT __declspec(dllexport) __stdcall\n#else\n#define CLAY_DLL_EXPORT\n#endif\n\n// Public Macro API ------------------------\n\n#define CLAY__MAX(x, y) (((x) > (y)) ? (x) : (y))\n#define CLAY__MIN(x, y) (((x) < (y)) ? (x) : (y))\n\n#define CLAY_TEXT_CONFIG(...) Clay__StoreTextElementConfig(CLAY__CONFIG_WRAPPER(Clay_TextElementConfig, __VA_ARGS__))\n\n#define CLAY_BORDER_OUTSIDE(widthValue) {widthValue, widthValue, widthValue, widthValue, 0}\n\n#define CLAY_BORDER_ALL(widthValue) {widthValue, widthValue, widthValue, widthValue, widthValue}\n\n#define CLAY_CORNER_RADIUS(radius) (CLAY__INIT(Clay_CornerRadius) { radius, radius, radius, radius })\n\n#define CLAY_PADDING_ALL(padding) CLAY__CONFIG_WRAPPER(Clay_Padding, { padding, padding, padding, padding })\n\n#define CLAY_SIZING_FIT(...) (CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { __VA_ARGS__ } }, .type = CLAY__SIZING_TYPE_FIT })\n\n#define CLAY_SIZING_GROW(...) (CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { __VA_ARGS__ } }, .type = CLAY__SIZING_TYPE_GROW })\n\n#define CLAY_SIZING_FIXED(fixedSize) (CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { fixedSize, fixedSize } }, .type = CLAY__SIZING_TYPE_FIXED })\n\n#define CLAY_SIZING_PERCENT(percentOfParent) (CLAY__INIT(Clay_SizingAxis) { .size = { .percent = (percentOfParent) }, .type = CLAY__SIZING_TYPE_PERCENT })\n\n// Note: If a compile error led you here, you might be trying to use CLAY_ID with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SID instead.\n#define CLAY_ID(label) CLAY_SID(CLAY_STRING(label))\n\n#define CLAY_SID(label) Clay__HashString(label, 0)\n\n// Note: If a compile error led you here, you might be trying to use CLAY_IDI with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SIDI instead.\n#define CLAY_IDI(label, index) CLAY_SIDI(CLAY_STRING(label), index)\n\n#define CLAY_SIDI(label, index) Clay__HashStringWithOffset(label, index, 0)\n\n// Note: If a compile error led you here, you might be trying to use CLAY_ID_LOCAL with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SID_LOCAL instead.\n#define CLAY_ID_LOCAL(label) CLAY_SID_LOCAL(CLAY_STRING(label))\n\n#define CLAY_SID_LOCAL(label) Clay__HashString(label, Clay__GetParentElementId())\n\n// Note: If a compile error led you here, you might be trying to use CLAY_IDI_LOCAL with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SIDI_LOCAL instead.\n#define CLAY_IDI_LOCAL(label, index) CLAY_SIDI_LOCAL(CLAY_STRING(label), index)\n\n#define CLAY_SIDI_LOCAL(label, index) Clay__HashStringWithOffset(label, index, Clay__GetParentElementId())\n\n#define CLAY__STRING_LENGTH(s) ((sizeof(s) / sizeof((s)[0])) - sizeof((s)[0]))\n\n#define CLAY__ENSURE_STRING_LITERAL(x) (\"\" x \"\")\n\n// Note: If an error led you here, it's because CLAY_STRING can only be used with string literals, i.e. CLAY_STRING(\"SomeString\") and not CLAY_STRING(yourString)\n#define CLAY_STRING(string) (CLAY__INIT(Clay_String) { .isStaticallyAllocated = true, .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) })\n\n#define CLAY_STRING_CONST(string) { .isStaticallyAllocated = true, .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) }\n\nstatic uint8_t CLAY__ELEMENT_DEFINITION_LATCH;\n\n// GCC marks the above CLAY__ELEMENT_DEFINITION_LATCH as an unused variable for files that include clay.h but don't declare any layout\n// This is to suppress that warning\nstatic inline void Clay__SuppressUnusedLatchDefinitionVariableWarning(void) { (void) CLAY__ELEMENT_DEFINITION_LATCH; }\n\n// Publicly visible layout element macros -----------------------------------------------------\n\n/* This macro looks scary on the surface, but is actually quite simple.\n  It turns a macro call like this:\n\n  CLAY({\n    .id = CLAY_ID(\"Container\"),\n    .backgroundColor = { 255, 200, 200, 255 }\n  }) {\n      ...children declared here\n  }\n\n  Into calls like this:\n\n  Clay__OpenElement();\n  Clay__ConfigureOpenElement((Clay_ElementDeclaration) {\n    .id = CLAY_ID(\"Container\"),\n    .backgroundColor = { 255, 200, 200, 255 }\n  });\n  ...children declared here\n  Clay__CloseElement();\n\n  The for loop will only ever run a single iteration, putting Clay__CloseElement() in the increment of the loop\n  means that it will run after the body - where the children are declared. It just exists to make sure you don't forget\n  to call Clay_CloseElement().\n*/\n#define CLAY_AUTO_ID(...)                                                                                                                                   \\\n    for (                                                                                                                                                   \\\n        CLAY__ELEMENT_DEFINITION_LATCH = (Clay__OpenElement(), Clay__ConfigureOpenElement(CLAY__CONFIG_WRAPPER(Clay_ElementDeclaration, __VA_ARGS__)), 0);  \\\n        CLAY__ELEMENT_DEFINITION_LATCH < 1;                                                                                                                 \\\n        CLAY__ELEMENT_DEFINITION_LATCH=1, Clay__CloseElement()                                                                                              \\\n    )\n\n#define CLAY(id, ...)                                                                                                                                               \\\n    for (                                                                                                                                                           \\\n        CLAY__ELEMENT_DEFINITION_LATCH = (Clay__OpenElementWithId(id), Clay__ConfigureOpenElement(CLAY__CONFIG_WRAPPER(Clay_ElementDeclaration, __VA_ARGS__)), 0);  \\\n        CLAY__ELEMENT_DEFINITION_LATCH < 1;                                                                                                                         \\\n        CLAY__ELEMENT_DEFINITION_LATCH=1, Clay__CloseElement()                                                                                                      \\\n    )\n\n// These macros exist to allow the CLAY() macro to be called both with an inline struct definition, such as\n// CLAY({ .id = something... });\n// As well as by passing a predefined declaration struct\n// Clay_ElementDeclaration declarationStruct = ...\n// CLAY(declarationStruct);\n#define CLAY__WRAPPER_TYPE(type) Clay__##type##Wrapper\n#define CLAY__WRAPPER_STRUCT(type) typedef struct { type wrapped; } CLAY__WRAPPER_TYPE(type)\n#define CLAY__CONFIG_WRAPPER(type, ...) (CLAY__INIT(CLAY__WRAPPER_TYPE(type)) { __VA_ARGS__ }).wrapped\n\n#define CLAY_TEXT(text, textConfig) Clay__OpenTextElement(text, textConfig)\n\n#ifdef __cplusplus\n\n#define CLAY__INIT(type) type\n\n#define CLAY_PACKED_ENUM enum : uint8_t\n\n#define CLAY__DEFAULT_STRUCT {}\n\n#else\n\n#define CLAY__INIT(type) (type)\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#define CLAY_PACKED_ENUM __pragma(pack(push, 1)) enum __pragma(pack(pop))\n#else\n#define CLAY_PACKED_ENUM enum __attribute__((__packed__))\n#endif\n\n#if __STDC_VERSION__ >= 202311L\n#define CLAY__DEFAULT_STRUCT {}\n#else\n#define CLAY__DEFAULT_STRUCT {0}\n#endif\n\n#endif // __cplusplus\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// Utility Structs -------------------------\n\n// Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string,\n// but it is also used to represent slices.\ntypedef struct Clay_String {\n    // Set this boolean to true if the char* data underlying this string will live for the entire lifetime of the program.\n    // This will automatically be set for strings created with CLAY_STRING, as the macro requires a string literal.\n    bool isStaticallyAllocated;\n    int32_t length;\n    // The underlying character memory. Note: this will not be copied and will not extend the lifetime of the underlying memory.\n    const char *chars;\n} Clay_String;\n\n// Clay_StringSlice is used to represent non owning string slices, and includes\n// a baseChars field which points to the string this slice is derived from.\ntypedef struct Clay_StringSlice {\n    int32_t length;\n    const char *chars;\n    const char *baseChars; // The source string / char* that this slice was derived from\n} Clay_StringSlice;\n\ntypedef struct Clay_Context Clay_Context;\n\n// Clay_Arena is a memory arena structure that is used by clay to manage its internal allocations.\n// Rather than creating it by hand, it's easier to use Clay_CreateArenaWithCapacityAndMemory()\ntypedef struct Clay_Arena {\n    uintptr_t nextAllocation;\n    size_t capacity;\n    char *memory;\n} Clay_Arena;\n\ntypedef struct Clay_Dimensions {\n    float width, height;\n} Clay_Dimensions;\n\ntypedef struct Clay_Vector2 {\n    float x, y;\n} Clay_Vector2;\n\n// Internally clay conventionally represents colors as 0-255, but interpretation is up to the renderer.\ntypedef struct Clay_Color {\n    float r, g, b, a;\n} Clay_Color;\n\ntypedef struct Clay_BoundingBox {\n    float x, y, width, height;\n} Clay_BoundingBox;\n\n// Primarily created via the CLAY_ID(), CLAY_IDI(), CLAY_ID_LOCAL() and CLAY_IDI_LOCAL() macros.\n// Represents a hashed string ID used for identifying and finding specific clay UI elements, required\n// by functions such as Clay_PointerOver() and Clay_GetElementData().\ntypedef struct Clay_ElementId {\n    uint32_t id; // The resulting hash generated from the other fields.\n    uint32_t offset; // A numerical offset applied after computing the hash from stringId.\n    uint32_t baseId; // A base hash value to start from, for example the parent element ID is used when calculating CLAY_ID_LOCAL().\n    Clay_String stringId; // The string id to hash.\n} Clay_ElementId;\n\n// A sized array of Clay_ElementId.\ntypedef struct\n{\n    int32_t capacity;\n    int32_t length;\n    Clay_ElementId *internalArray;\n} Clay_ElementIdArray;\n\n// Controls the \"radius\", or corner rounding of elements, including rectangles, borders and images.\n// The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.\ntypedef struct Clay_CornerRadius {\n    float topLeft;\n    float topRight;\n    float bottomLeft;\n    float bottomRight;\n} Clay_CornerRadius;\n\n// Element Configs ---------------------------\n\n// Controls the direction in which child elements will be automatically laid out.\ntypedef CLAY_PACKED_ENUM {\n    // (Default) Lays out child elements from left to right with increasing x.\n    CLAY_LEFT_TO_RIGHT,\n    // Lays out child elements from top to bottom with increasing y.\n    CLAY_TOP_TO_BOTTOM,\n} Clay_LayoutDirection;\n\n// Controls the alignment along the x axis (horizontal) of child elements.\ntypedef CLAY_PACKED_ENUM {\n    // (Default) Aligns child elements to the left hand side of this element, offset by padding.width.left\n    CLAY_ALIGN_X_LEFT,\n    // Aligns child elements to the right hand side of this element, offset by padding.width.right\n    CLAY_ALIGN_X_RIGHT,\n    // Aligns child elements horizontally to the center of this element\n    CLAY_ALIGN_X_CENTER,\n} Clay_LayoutAlignmentX;\n\n// Controls the alignment along the y axis (vertical) of child elements.\ntypedef CLAY_PACKED_ENUM {\n    // (Default) Aligns child elements to the top of this element, offset by padding.width.top\n    CLAY_ALIGN_Y_TOP,\n    // Aligns child elements to the bottom of this element, offset by padding.width.bottom\n    CLAY_ALIGN_Y_BOTTOM,\n    // Aligns child elements vertically to the center of this element\n    CLAY_ALIGN_Y_CENTER,\n} Clay_LayoutAlignmentY;\n\n// Controls how the element takes up space inside its parent container.\ntypedef CLAY_PACKED_ENUM {\n    // (default) Wraps tightly to the size of the element's contents.\n    CLAY__SIZING_TYPE_FIT,\n    // Expands along this axis to fill available space in the parent element, sharing it with other GROW elements.\n    CLAY__SIZING_TYPE_GROW,\n    // Expects 0-1 range. Clamps the axis size to a percent of the parent container's axis size minus padding and child gaps.\n    CLAY__SIZING_TYPE_PERCENT,\n    // Clamps the axis size to an exact size in pixels.\n    CLAY__SIZING_TYPE_FIXED,\n} Clay__SizingType;\n\n// Controls how child elements are aligned on each axis.\ntypedef struct Clay_ChildAlignment {\n    Clay_LayoutAlignmentX x; // Controls alignment of children along the x axis.\n    Clay_LayoutAlignmentY y; // Controls alignment of children along the y axis.\n} Clay_ChildAlignment;\n\n// Controls the minimum and maximum size in pixels that this element is allowed to grow or shrink to,\n// overriding sizing types such as FIT or GROW.\ntypedef struct Clay_SizingMinMax {\n    float min; // The smallest final size of the element on this axis will be this value in pixels.\n    float max; // The largest final size of the element on this axis will be this value in pixels.\n} Clay_SizingMinMax;\n\n// Controls the sizing of this element along one axis inside its parent container.\ntypedef struct Clay_SizingAxis {\n    union {\n        Clay_SizingMinMax minMax; // Controls the minimum and maximum size in pixels that this element is allowed to grow or shrink to, overriding sizing types such as FIT or GROW.\n        float percent; // Expects 0-1 range. Clamps the axis size to a percent of the parent container's axis size minus padding and child gaps.\n    } size;\n    Clay__SizingType type; // Controls how the element takes up space inside its parent container.\n} Clay_SizingAxis;\n\n// Controls the sizing of this element along one axis inside its parent container.\ntypedef struct Clay_Sizing {\n    Clay_SizingAxis width; // Controls the width sizing of the element, along the x axis.\n    Clay_SizingAxis height;  // Controls the height sizing of the element, along the y axis.\n} Clay_Sizing;\n\n// Controls \"padding\" in pixels, which is a gap between the bounding box of this element and where its children\n// will be placed.\ntypedef struct Clay_Padding {\n    uint16_t left;\n    uint16_t right;\n    uint16_t top;\n    uint16_t bottom;\n} Clay_Padding;\n\nCLAY__WRAPPER_STRUCT(Clay_Padding);\n\n// Controls various settings that affect the size and position of an element, as well as the sizes and positions\n// of any child elements.\ntypedef struct Clay_LayoutConfig {\n    Clay_Sizing sizing; // Controls the sizing of this element inside it's parent container, including FIT, GROW, PERCENT and FIXED sizing.\n    Clay_Padding padding; // Controls \"padding\" in pixels, which is a gap between the bounding box of this element and where its children will be placed.\n    uint16_t childGap; // Controls the gap in pixels between child elements along the layout axis (horizontal gap for LEFT_TO_RIGHT, vertical gap for TOP_TO_BOTTOM).\n    Clay_ChildAlignment childAlignment; // Controls how child elements are aligned on each axis.\n    Clay_LayoutDirection layoutDirection; // Controls the direction in which child elements will be automatically laid out.\n} Clay_LayoutConfig;\n\nCLAY__WRAPPER_STRUCT(Clay_LayoutConfig);\n\nextern Clay_LayoutConfig CLAY_LAYOUT_DEFAULT;\n\n// Controls how text \"wraps\", that is how it is broken into multiple lines when there is insufficient horizontal space.\ntypedef CLAY_PACKED_ENUM {\n    // (default) breaks on whitespace characters.\n    CLAY_TEXT_WRAP_WORDS,\n    // Don't break on space characters, only on newlines.\n    CLAY_TEXT_WRAP_NEWLINES,\n    // Disable text wrapping entirely.\n    CLAY_TEXT_WRAP_NONE,\n} Clay_TextElementConfigWrapMode;\n\n// Controls how wrapped lines of text are horizontally aligned within the outer text bounding box.\ntypedef CLAY_PACKED_ENUM {\n    // (default) Horizontally aligns wrapped lines of text to the left hand side of their bounding box.\n    CLAY_TEXT_ALIGN_LEFT,\n    // Horizontally aligns wrapped lines of text to the center of their bounding box.\n    CLAY_TEXT_ALIGN_CENTER,\n    // Horizontally aligns wrapped lines of text to the right hand side of their bounding box.\n    CLAY_TEXT_ALIGN_RIGHT,\n} Clay_TextAlignment;\n\n// Controls various functionality related to text elements.\ntypedef struct Clay_TextElementConfig {\n    // A pointer that will be transparently passed through to the resulting render command.\n    void *userData;\n    // The RGBA color of the font to render, conventionally specified as 0-255.\n    Clay_Color textColor;\n    // An integer transparently passed to Clay_MeasureText to identify the font to use.\n    // The debug view will pass fontId = 0 for its internal text.\n    uint16_t fontId;\n    // Controls the size of the font. Handled by the function provided to Clay_MeasureText.\n    uint16_t fontSize;\n    // Controls extra horizontal spacing between characters. Handled by the function provided to Clay_MeasureText.\n    uint16_t letterSpacing;\n    // Controls additional vertical space between wrapped lines of text.\n    uint16_t lineHeight;\n    // Controls how text \"wraps\", that is how it is broken into multiple lines when there is insufficient horizontal space.\n    // CLAY_TEXT_WRAP_WORDS (default) breaks on whitespace characters.\n    // CLAY_TEXT_WRAP_NEWLINES doesn't break on space characters, only on newlines.\n    // CLAY_TEXT_WRAP_NONE disables wrapping entirely.\n    Clay_TextElementConfigWrapMode wrapMode;\n    // Controls how wrapped lines of text are horizontally aligned within the outer text bounding box.\n    // CLAY_TEXT_ALIGN_LEFT (default) - Horizontally aligns wrapped lines of text to the left hand side of their bounding box.\n    // CLAY_TEXT_ALIGN_CENTER - Horizontally aligns wrapped lines of text to the center of their bounding box.\n    // CLAY_TEXT_ALIGN_RIGHT - Horizontally aligns wrapped lines of text to the right hand side of their bounding box.\n    Clay_TextAlignment textAlignment;\n} Clay_TextElementConfig;\n\nCLAY__WRAPPER_STRUCT(Clay_TextElementConfig);\n\n// Aspect Ratio --------------------------------\n\n// Controls various settings related to aspect ratio scaling element.\ntypedef struct Clay_AspectRatioElementConfig {\n    float aspectRatio; // A float representing the target \"Aspect ratio\" for an element, which is its final width divided by its final height.\n} Clay_AspectRatioElementConfig;\n\nCLAY__WRAPPER_STRUCT(Clay_AspectRatioElementConfig);\n\n// Image --------------------------------\n\n// Controls various settings related to image elements.\ntypedef struct Clay_ImageElementConfig {\n    void* imageData; // A transparent pointer used to pass image data through to the renderer.\n} Clay_ImageElementConfig;\n\nCLAY__WRAPPER_STRUCT(Clay_ImageElementConfig);\n\n// Floating -----------------------------\n\n// Controls where a floating element is offset relative to its parent element.\n// Note: see https://github.com/user-attachments/assets/b8c6dfaa-c1b1-41a4-be55-013473e4a6ce for a visual explanation.\ntypedef CLAY_PACKED_ENUM {\n    CLAY_ATTACH_POINT_LEFT_TOP,\n    CLAY_ATTACH_POINT_LEFT_CENTER,\n    CLAY_ATTACH_POINT_LEFT_BOTTOM,\n    CLAY_ATTACH_POINT_CENTER_TOP,\n    CLAY_ATTACH_POINT_CENTER_CENTER,\n    CLAY_ATTACH_POINT_CENTER_BOTTOM,\n    CLAY_ATTACH_POINT_RIGHT_TOP,\n    CLAY_ATTACH_POINT_RIGHT_CENTER,\n    CLAY_ATTACH_POINT_RIGHT_BOTTOM,\n} Clay_FloatingAttachPointType;\n\n// Controls where a floating element is offset relative to its parent element.\ntypedef struct Clay_FloatingAttachPoints {\n    Clay_FloatingAttachPointType element; // Controls the origin point on a floating element that attaches to its parent.\n    Clay_FloatingAttachPointType parent; // Controls the origin point on the parent element that the floating element attaches to.\n} Clay_FloatingAttachPoints;\n\n// Controls how mouse pointer events like hover and click are captured or passed through to elements underneath a floating element.\ntypedef CLAY_PACKED_ENUM {\n    // (default) \"Capture\" the pointer event and don't allow events like hover and click to pass through to elements underneath.\n    CLAY_POINTER_CAPTURE_MODE_CAPTURE,\n    //    CLAY_POINTER_CAPTURE_MODE_PARENT, TODO pass pointer through to attached parent\n\n    // Transparently pass through pointer events like hover and click to elements underneath the floating element.\n    CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,\n} Clay_PointerCaptureMode;\n\n// Controls which element a floating element is \"attached\" to (i.e. relative offset from).\ntypedef CLAY_PACKED_ENUM {\n    // (default) Disables floating for this element.\n    CLAY_ATTACH_TO_NONE,\n    // Attaches this floating element to its parent, positioned based on the .attachPoints and .offset fields.\n    CLAY_ATTACH_TO_PARENT,\n    // Attaches this floating element to an element with a specific ID, specified with the .parentId field. positioned based on the .attachPoints and .offset fields.\n    CLAY_ATTACH_TO_ELEMENT_WITH_ID,\n    // Attaches this floating element to the root of the layout, which combined with the .offset field provides functionality similar to \"absolute positioning\".\n    CLAY_ATTACH_TO_ROOT,\n} Clay_FloatingAttachToElement;\n\n// Controls whether or not a floating element is clipped to the same clipping rectangle as the element it's attached to.\ntypedef CLAY_PACKED_ENUM {\n    // (default) - The floating element does not inherit clipping.\n    CLAY_CLIP_TO_NONE,\n    // The floating element is clipped to the same clipping rectangle as the element it's attached to.\n    CLAY_CLIP_TO_ATTACHED_PARENT\n} Clay_FloatingClipToElement;\n\n// Controls various settings related to \"floating\" elements, which are elements that \"float\" above other elements, potentially overlapping their boundaries,\n// and not affecting the layout of sibling or parent elements.\ntypedef struct Clay_FloatingElementConfig {\n    // Offsets this floating element by the provided x,y coordinates from its attachPoints.\n    Clay_Vector2 offset;\n    // Expands the boundaries of the outer floating element without affecting its children.\n    Clay_Dimensions expand;\n    // When used in conjunction with .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, attaches this floating element to the element in the hierarchy with the provided ID.\n    // Hint: attach the ID to the other element with .id = CLAY_ID(\"yourId\"), and specify the id the same way, with .parentId = CLAY_ID(\"yourId\").id\n    uint32_t parentId;\n    // Controls the z index of this floating element and all its children. Floating elements are sorted in ascending z order before output.\n    // zIndex is also passed to the renderer for all elements contained within this floating element.\n    int16_t zIndex;\n    // Controls how mouse pointer events like hover and click are captured or passed through to elements underneath / behind a floating element.\n    // Enum is of the form CLAY_ATTACH_POINT_foo_bar. See Clay_FloatingAttachPoints for more details.\n    // Note: see <img src=\"https://github.com/user-attachments/assets/b8c6dfaa-c1b1-41a4-be55-013473e4a6ce />\n    // and <img src=\"https://github.com/user-attachments/assets/ebe75e0d-1904-46b0-982d-418f929d1516 /> for a visual explanation.\n    Clay_FloatingAttachPoints attachPoints;\n    // Controls how mouse pointer events like hover and click are captured or passed through to elements underneath a floating element.\n    // CLAY_POINTER_CAPTURE_MODE_CAPTURE (default) - \"Capture\" the pointer event and don't allow events like hover and click to pass through to elements underneath.\n    // CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH - Transparently pass through pointer events like hover and click to elements underneath the floating element.\n    Clay_PointerCaptureMode pointerCaptureMode;\n    // Controls which element a floating element is \"attached\" to (i.e. relative offset from).\n    // CLAY_ATTACH_TO_NONE (default) - Disables floating for this element.\n    // CLAY_ATTACH_TO_PARENT - Attaches this floating element to its parent, positioned based on the .attachPoints and .offset fields.\n    // CLAY_ATTACH_TO_ELEMENT_WITH_ID - Attaches this floating element to an element with a specific ID, specified with the .parentId field. positioned based on the .attachPoints and .offset fields.\n    // CLAY_ATTACH_TO_ROOT - Attaches this floating element to the root of the layout, which combined with the .offset field provides functionality similar to \"absolute positioning\".\n    Clay_FloatingAttachToElement attachTo;\n    // Controls whether or not a floating element is clipped to the same clipping rectangle as the element it's attached to.\n    // CLAY_CLIP_TO_NONE (default) - The floating element does not inherit clipping.\n    // CLAY_CLIP_TO_ATTACHED_PARENT - The floating element is clipped to the same clipping rectangle as the element it's attached to.\n    Clay_FloatingClipToElement clipTo;\n} Clay_FloatingElementConfig;\n\nCLAY__WRAPPER_STRUCT(Clay_FloatingElementConfig);\n\n// Custom -----------------------------\n\n// Controls various settings related to custom elements.\ntypedef struct Clay_CustomElementConfig {\n    // A transparent pointer through which you can pass custom data to the renderer.\n    // Generates CUSTOM render commands.\n    void* customData;\n} Clay_CustomElementConfig;\n\nCLAY__WRAPPER_STRUCT(Clay_CustomElementConfig);\n\n// Scroll -----------------------------\n\n// Controls the axis on which an element switches to \"scrolling\", which clips the contents and allows scrolling in that direction.\ntypedef struct Clay_ClipElementConfig {\n    bool horizontal; // Clip overflowing elements on the X axis.\n    bool vertical; // Clip overflowing elements on the Y axis.\n    Clay_Vector2 childOffset; // Offsets the x,y positions of all child elements. Used primarily for scrolling containers.\n} Clay_ClipElementConfig;\n\nCLAY__WRAPPER_STRUCT(Clay_ClipElementConfig);\n\n// Border -----------------------------\n\n// Controls the widths of individual element borders.\ntypedef struct Clay_BorderWidth {\n    uint16_t left;\n    uint16_t right;\n    uint16_t top;\n    uint16_t bottom;\n    // Creates borders between each child element, depending on the .layoutDirection.\n    // e.g. for LEFT_TO_RIGHT, borders will be vertical lines, and for TOP_TO_BOTTOM borders will be horizontal lines.\n    // .betweenChildren borders will result in individual RECTANGLE render commands being generated.\n    uint16_t betweenChildren;\n} Clay_BorderWidth;\n\n// Controls settings related to element borders.\ntypedef struct Clay_BorderElementConfig {\n    Clay_Color color; // Controls the color of all borders with width > 0. Conventionally represented as 0-255, but interpretation is up to the renderer.\n    Clay_BorderWidth width; // Controls the widths of individual borders. At least one of these should be > 0 for a BORDER render command to be generated.\n} Clay_BorderElementConfig;\n\nCLAY__WRAPPER_STRUCT(Clay_BorderElementConfig);\n\n// Render Command Data -----------------------------\n\n// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT\ntypedef struct Clay_TextRenderData {\n    // A string slice containing the text to be rendered.\n    // Note: this is not guaranteed to be null terminated.\n    Clay_StringSlice stringContents;\n    // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.\n    Clay_Color textColor;\n    // An integer representing the font to use to render this text, transparently passed through from the text declaration.\n    uint16_t fontId;\n    uint16_t fontSize;\n    // Specifies the extra whitespace gap in pixels between each character.\n    uint16_t letterSpacing;\n    // The height of the bounding box for this line of text.\n    uint16_t lineHeight;\n} Clay_TextRenderData;\n\n// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE\ntypedef struct Clay_RectangleRenderData {\n    // The solid background color to fill this rectangle with. Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.\n    Clay_Color backgroundColor;\n    // Controls the \"radius\", or corner rounding of elements, including rectangles, borders and images.\n    // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.\n    Clay_CornerRadius cornerRadius;\n} Clay_RectangleRenderData;\n\n// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE\ntypedef struct Clay_ImageRenderData {\n    // The tint color for this image. Note that the default value is 0,0,0,0 and should likely be interpreted\n    // as \"untinted\".\n    // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.\n    Clay_Color backgroundColor;\n    // Controls the \"radius\", or corner rounding of this image.\n    // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.\n    Clay_CornerRadius cornerRadius;\n    // A pointer transparently passed through from the original element definition, typically used to represent image data.\n    void* imageData;\n} Clay_ImageRenderData;\n\n// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM\ntypedef struct Clay_CustomRenderData {\n    // Passed through from .backgroundColor in the original element declaration.\n    // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.\n    Clay_Color backgroundColor;\n    // Controls the \"radius\", or corner rounding of this custom element.\n    // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.\n    Clay_CornerRadius cornerRadius;\n    // A pointer transparently passed through from the original element definition.\n    void* customData;\n} Clay_CustomRenderData;\n\n// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_START || commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_END\ntypedef struct Clay_ScrollRenderData {\n    bool horizontal;\n    bool vertical;\n} Clay_ClipRenderData;\n\n// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_BORDER\ntypedef struct Clay_BorderRenderData {\n    // Controls a shared color for all this element's borders.\n    // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.\n    Clay_Color color;\n    // Specifies the \"radius\", or corner rounding of this border element.\n    // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.\n    Clay_CornerRadius cornerRadius;\n    // Controls individual border side widths.\n    Clay_BorderWidth width;\n} Clay_BorderRenderData;\n\n// A struct union containing data specific to this command's .commandType\ntypedef union Clay_RenderData {\n    // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE\n    Clay_RectangleRenderData rectangle;\n    // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT\n    Clay_TextRenderData text;\n    // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE\n    Clay_ImageRenderData image;\n    // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM\n    Clay_CustomRenderData custom;\n    // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_BORDER\n    Clay_BorderRenderData border;\n    // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_START|END\n    Clay_ClipRenderData clip;\n} Clay_RenderData;\n\n// Miscellaneous Structs & Enums ---------------------------------\n\n// Data representing the current internal state of a scrolling element.\ntypedef struct Clay_ScrollContainerData {\n    // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout.\n    // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling.\n    Clay_Vector2 *scrollPosition;\n    // The bounding box of the scroll element.\n    Clay_Dimensions scrollContainerDimensions;\n    // The outer dimensions of the inner scroll container content, including the padding of the parent scroll container.\n    Clay_Dimensions contentDimensions;\n    // The config that was originally passed to the clip element.\n    Clay_ClipElementConfig config;\n    // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned.\n    bool found;\n} Clay_ScrollContainerData;\n\n// Bounding box and other data for a specific UI element.\ntypedef struct Clay_ElementData {\n    // The rectangle that encloses this UI element, with the position relative to the root of the layout.\n    Clay_BoundingBox boundingBox;\n    // Indicates whether an actual Element matched the provided ID or if the default struct was returned.\n    bool found;\n} Clay_ElementData;\n\n// Used by renderers to determine specific handling for each render command.\ntypedef CLAY_PACKED_ENUM {\n    // This command type should be skipped.\n    CLAY_RENDER_COMMAND_TYPE_NONE,\n    // The renderer should draw a solid color rectangle.\n    CLAY_RENDER_COMMAND_TYPE_RECTANGLE,\n    // The renderer should draw a colored border inset into the bounding box.\n    CLAY_RENDER_COMMAND_TYPE_BORDER,\n    // The renderer should draw text.\n    CLAY_RENDER_COMMAND_TYPE_TEXT,\n    // The renderer should draw an image.\n    CLAY_RENDER_COMMAND_TYPE_IMAGE,\n    // The renderer should begin clipping all future draw commands, only rendering content that falls within the provided boundingBox.\n    CLAY_RENDER_COMMAND_TYPE_SCISSOR_START,\n    // The renderer should finish any previously active clipping, and begin rendering elements in full again.\n    CLAY_RENDER_COMMAND_TYPE_SCISSOR_END,\n    // The renderer should provide a custom implementation for handling this render command based on its .customData\n    CLAY_RENDER_COMMAND_TYPE_CUSTOM,\n} Clay_RenderCommandType;\n\ntypedef struct Clay_RenderCommand {\n    // A rectangular box that fully encloses this UI element, with the position relative to the root of the layout.\n    Clay_BoundingBox boundingBox;\n    // A struct union containing data specific to this command's commandType.\n    Clay_RenderData renderData;\n    // A pointer transparently passed through from the original element declaration.\n    void *userData;\n    // The id of this element, transparently passed through from the original element declaration.\n    uint32_t id;\n    // The z order required for drawing this command correctly.\n    // Note: the render command array is already sorted in ascending order, and will produce correct results if drawn in naive order.\n    // This field is intended for use in batching renderers for improved performance.\n    int16_t zIndex;\n    // Specifies how to handle rendering of this command.\n    // CLAY_RENDER_COMMAND_TYPE_RECTANGLE - The renderer should draw a solid color rectangle.\n    // CLAY_RENDER_COMMAND_TYPE_BORDER - The renderer should draw a colored border inset into the bounding box.\n    // CLAY_RENDER_COMMAND_TYPE_TEXT - The renderer should draw text.\n    // CLAY_RENDER_COMMAND_TYPE_IMAGE - The renderer should draw an image.\n    // CLAY_RENDER_COMMAND_TYPE_SCISSOR_START - The renderer should begin clipping all future draw commands, only rendering content that falls within the provided boundingBox.\n    // CLAY_RENDER_COMMAND_TYPE_SCISSOR_END - The renderer should finish any previously active clipping, and begin rendering elements in full again.\n    // CLAY_RENDER_COMMAND_TYPE_CUSTOM - The renderer should provide a custom implementation for handling this render command based on its .customData\n    Clay_RenderCommandType commandType;\n} Clay_RenderCommand;\n\n// A sized array of render commands.\ntypedef struct Clay_RenderCommandArray {\n    // The underlying max capacity of the array, not necessarily all initialized.\n    int32_t capacity;\n    // The number of initialized elements in this array. Used for loops and iteration.\n    int32_t length;\n    // A pointer to the first element in the internal array.\n    Clay_RenderCommand* internalArray;\n} Clay_RenderCommandArray;\n\n// Represents the current state of interaction with clay this frame.\ntypedef CLAY_PACKED_ENUM {\n    // A left mouse click, or touch occurred this frame.\n    CLAY_POINTER_DATA_PRESSED_THIS_FRAME,\n    // The left mouse button click or touch happened at some point in the past, and is still currently held down this frame.\n    CLAY_POINTER_DATA_PRESSED,\n    // The left mouse button click or touch was released this frame.\n    CLAY_POINTER_DATA_RELEASED_THIS_FRAME,\n    // The left mouse button click or touch is not currently down / was released at some point in the past.\n    CLAY_POINTER_DATA_RELEASED,\n} Clay_PointerDataInteractionState;\n\n// Information on the current state of pointer interactions this frame.\ntypedef struct Clay_PointerData {\n    // The position of the mouse / touch / pointer relative to the root of the layout.\n    Clay_Vector2 position;\n    // Represents the current state of interaction with clay this frame.\n    // CLAY_POINTER_DATA_PRESSED_THIS_FRAME - A left mouse click, or touch occurred this frame.\n    // CLAY_POINTER_DATA_PRESSED - The left mouse button click or touch happened at some point in the past, and is still currently held down this frame.\n    // CLAY_POINTER_DATA_RELEASED_THIS_FRAME - The left mouse button click or touch was released this frame.\n    // CLAY_POINTER_DATA_RELEASED - The left mouse button click or touch is not currently down / was released at some point in the past.\n    Clay_PointerDataInteractionState state;\n} Clay_PointerData;\n\ntypedef struct Clay_ElementDeclaration {\n    // Controls various settings that affect the size and position of an element, as well as the sizes and positions of any child elements.\n    Clay_LayoutConfig layout;\n    // Controls the background color of the resulting element.\n    // By convention specified as 0-255, but interpretation is up to the renderer.\n    // If no other config is specified, .backgroundColor will generate a RECTANGLE render command, otherwise it will be passed as a property to IMAGE or CUSTOM render commands.\n    Clay_Color backgroundColor;\n    // Controls the \"radius\", or corner rounding of elements, including rectangles, borders and images.\n    Clay_CornerRadius cornerRadius;\n    // Controls settings related to aspect ratio scaling.\n    Clay_AspectRatioElementConfig aspectRatio;\n    // Controls settings related to image elements.\n    Clay_ImageElementConfig image;\n    // Controls whether and how an element \"floats\", which means it layers over the top of other elements in z order, and doesn't affect the position and size of siblings or parent elements.\n    // Note: in order to activate floating, .floating.attachTo must be set to something other than the default value.\n    Clay_FloatingElementConfig floating;\n    // Used to create CUSTOM render commands, usually to render element types not supported by Clay.\n    Clay_CustomElementConfig custom;\n    // Controls whether an element should clip its contents, as well as providing child x,y offset configuration for scrolling.\n    Clay_ClipElementConfig clip;\n    // Controls settings related to element borders, and will generate BORDER render commands.\n    Clay_BorderElementConfig border;\n    // A pointer that will be transparently passed through to resulting render commands.\n    void *userData;\n} Clay_ElementDeclaration;\n\nCLAY__WRAPPER_STRUCT(Clay_ElementDeclaration);\n\n// Represents the type of error clay encountered while computing layout.\ntypedef CLAY_PACKED_ENUM {\n    // A text measurement function wasn't provided using Clay_SetMeasureTextFunction(), or the provided function was null.\n    CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,\n    // Clay attempted to allocate its internal data structures but ran out of space.\n    // The arena passed to Clay_Initialize was created with a capacity smaller than that required by Clay_MinMemorySize().\n    CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,\n    // Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxElementCount().\n    CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,\n    // Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxMeasureTextCacheWordCount().\n    CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED,\n    // Two elements were declared with exactly the same ID within one layout.\n    CLAY_ERROR_TYPE_DUPLICATE_ID,\n    // A floating element was declared using CLAY_ATTACH_TO_ELEMENT_ID and either an invalid .parentId was provided or no element with the provided .parentId was found.\n    CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND,\n    // An element was declared that using CLAY_SIZING_PERCENT but the percentage value was over 1. Percentage values are expected to be in the 0-1 range.\n    CLAY_ERROR_TYPE_PERCENTAGE_OVER_1,\n    // Clay encountered an internal error. It would be wonderful if you could report this so we can fix it!\n    CLAY_ERROR_TYPE_INTERNAL_ERROR,\n    // Clay__OpenElement was called more times than Clay__CloseElement, so there were still remaining open elements when the layout ended.\n    CLAY_ERROR_TYPE_UNBALANCED_OPEN_CLOSE,\n} Clay_ErrorType;\n\n// Data to identify the error that clay has encountered.\ntypedef struct Clay_ErrorData {\n    // Represents the type of error clay encountered while computing layout.\n    // CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED - A text measurement function wasn't provided using Clay_SetMeasureTextFunction(), or the provided function was null.\n    // CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED - Clay attempted to allocate its internal data structures but ran out of space. The arena passed to Clay_Initialize was created with a capacity smaller than that required by Clay_MinMemorySize().\n    // CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED - Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxElementCount().\n    // CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED - Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxMeasureTextCacheWordCount().\n    // CLAY_ERROR_TYPE_DUPLICATE_ID - Two elements were declared with exactly the same ID within one layout.\n    // CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND - A floating element was declared using CLAY_ATTACH_TO_ELEMENT_ID and either an invalid .parentId was provided or no element with the provided .parentId was found.\n    // CLAY_ERROR_TYPE_PERCENTAGE_OVER_1 - An element was declared that using CLAY_SIZING_PERCENT but the percentage value was over 1. Percentage values are expected to be in the 0-1 range.\n    // CLAY_ERROR_TYPE_INTERNAL_ERROR - Clay encountered an internal error. It would be wonderful if you could report this so we can fix it!\n    Clay_ErrorType errorType;\n    // A string containing human-readable error text that explains the error in more detail.\n    Clay_String errorText;\n    // A transparent pointer passed through from when the error handler was first provided.\n    void *userData;\n} Clay_ErrorData;\n\n// A wrapper struct around Clay's error handler function.\ntypedef struct {\n    // A user provided function to call when Clay encounters an error during layout.\n    void (*errorHandlerFunction)(Clay_ErrorData errorText);\n    // A pointer that will be transparently passed through to the error handler when it is called.\n    void *userData;\n} Clay_ErrorHandler;\n\n// Function Forward Declarations ---------------------------------\n\n// Public API functions ------------------------------------------\n\n// Returns the size, in bytes, of the minimum amount of memory Clay requires to operate at its current settings.\nCLAY_DLL_EXPORT uint32_t Clay_MinMemorySize(void);\n// Creates an arena for clay to use for its internal allocations, given a certain capacity in bytes and a pointer to an allocation of at least that size.\n// Intended to be used with Clay_MinMemorySize in the following way:\n// uint32_t minMemoryRequired = Clay_MinMemorySize();\n// Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(minMemoryRequired, malloc(minMemoryRequired));\nCLAY_DLL_EXPORT Clay_Arena Clay_CreateArenaWithCapacityAndMemory(size_t capacity, void *memory);\n// Sets the state of the \"pointer\" (i.e. the mouse or touch) in Clay's internal data. Used for detecting and responding to mouse events in the debug view,\n// as well as for Clay_Hovered() and scroll element handling.\nCLAY_DLL_EXPORT void Clay_SetPointerState(Clay_Vector2 position, bool pointerDown);\n// Initialize Clay's internal arena and setup required data before layout can begin. Only needs to be called once.\n// - arena can be created using Clay_CreateArenaWithCapacityAndMemory()\n// - layoutDimensions are the initial bounding dimensions of the layout (i.e. the screen width and height for a full screen layout)\n// - errorHandler is used by Clay to inform you if something has gone wrong in configuration or layout.\nCLAY_DLL_EXPORT Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler);\n// Returns the Context that clay is currently using. Used when using multiple instances of clay simultaneously.\nCLAY_DLL_EXPORT Clay_Context* Clay_GetCurrentContext(void);\n// Sets the context that clay will use to compute the layout.\n// Used to restore a context saved from Clay_GetCurrentContext when using multiple instances of clay simultaneously.\nCLAY_DLL_EXPORT void Clay_SetCurrentContext(Clay_Context* context);\n// Updates the state of Clay's internal scroll data, updating scroll content positions if scrollDelta is non zero, and progressing momentum scrolling.\n// - enableDragScrolling when set to true will enable mobile device like \"touch drag\" scroll of scroll containers, including momentum scrolling after the touch has ended.\n// - scrollDelta is the amount to scroll this frame on each axis in pixels.\n// - deltaTime is the time in seconds since the last \"frame\" (scroll update)\nCLAY_DLL_EXPORT void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime);\n// Returns the internally stored scroll offset for the currently open element.\n// Generally intended for use with clip elements to create scrolling containers.\nCLAY_DLL_EXPORT Clay_Vector2 Clay_GetScrollOffset(void);\n// Updates the layout dimensions in response to the window or outer container being resized.\nCLAY_DLL_EXPORT void Clay_SetLayoutDimensions(Clay_Dimensions dimensions);\n// Called before starting any layout declarations.\nCLAY_DLL_EXPORT void Clay_BeginLayout(void);\n// Called when all layout declarations are finished.\n// Computes the layout and generates and returns the array of render commands to draw.\nCLAY_DLL_EXPORT Clay_RenderCommandArray Clay_EndLayout(void);\n// Calculates a hash ID from the given idString.\n// Generally only used for dynamic strings when CLAY_ID(\"stringLiteral\") can't be used.\nCLAY_DLL_EXPORT Clay_ElementId Clay_GetElementId(Clay_String idString);\n// Calculates a hash ID from the given idString and index.\n// - index is used to avoid constructing dynamic ID strings in loops.\n// Generally only used for dynamic strings when CLAY_IDI(\"stringLiteral\", index) can't be used.\nCLAY_DLL_EXPORT Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index);\n// Returns layout data such as the final calculated bounding box for an element with a given ID.\n// The returned Clay_ElementData contains a `found` bool that will be true if an element with the provided ID was found.\n// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.\nCLAY_DLL_EXPORT Clay_ElementData Clay_GetElementData(Clay_ElementId id);\n// Returns true if the pointer position provided by Clay_SetPointerState is within the current element's bounding box.\n// Works during element declaration, e.g. CLAY({ .backgroundColor = Clay_Hovered() ? BLUE : RED });\nCLAY_DLL_EXPORT bool Clay_Hovered(void);\n// Bind a callback that will be called when the pointer position provided by Clay_SetPointerState is within the current element's bounding box.\n// - onHoverFunction is a function pointer to a user defined function.\n// - userData is a pointer that will be transparently passed through when the onHoverFunction is called.\nCLAY_DLL_EXPORT void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData), void *userData);\n// An imperative function that returns true if the pointer position provided by Clay_SetPointerState is within the element with the provided ID's bounding box.\n// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.\nCLAY_DLL_EXPORT bool Clay_PointerOver(Clay_ElementId elementId);\n// Returns the array of element IDs that the pointer is currently over.\nCLAY_DLL_EXPORT Clay_ElementIdArray Clay_GetPointerOverIds(void);\n// Returns data representing the state of the scrolling element with the provided ID.\n// The returned Clay_ScrollContainerData contains a `found` bool that will be true if a scroll element was found with the provided ID.\n// An imperative function that returns true if the pointer position provided by Clay_SetPointerState is within the element with the provided ID's bounding box.\n// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.\nCLAY_DLL_EXPORT Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id);\n// Binds a callback function that Clay will call to determine the dimensions of a given string slice.\n// - measureTextFunction is a user provided function that adheres to the interface Clay_Dimensions (Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);\n// - userData is a pointer that will be transparently passed through when the measureTextFunction is called.\nCLAY_DLL_EXPORT void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData), void *userData);\n// Experimental - Used in cases where Clay needs to integrate with a system that manages its own scrolling containers externally.\n// Please reach out if you plan to use this function, as it may be subject to change.\nCLAY_DLL_EXPORT void Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId, void *userData), void *userData);\n// A bounds-checked \"get\" function for the Clay_RenderCommandArray returned from Clay_EndLayout().\nCLAY_DLL_EXPORT Clay_RenderCommand * Clay_RenderCommandArray_Get(Clay_RenderCommandArray* array, int32_t index);\n// Enables and disables Clay's internal debug tools.\n// This state is retained and does not need to be set each frame.\nCLAY_DLL_EXPORT void Clay_SetDebugModeEnabled(bool enabled);\n// Returns true if Clay's internal debug tools are currently enabled.\nCLAY_DLL_EXPORT bool Clay_IsDebugModeEnabled(void);\n// Enables and disables visibility culling. By default, Clay will not generate render commands for elements whose bounding box is entirely outside the screen.\nCLAY_DLL_EXPORT void Clay_SetCullingEnabled(bool enabled);\n// Returns the maximum number of UI elements supported by Clay's current configuration.\nCLAY_DLL_EXPORT int32_t Clay_GetMaxElementCount(void);\n// Modifies the maximum number of UI elements supported by Clay's current configuration.\n// This may require reallocating additional memory, and re-calling Clay_Initialize();\nCLAY_DLL_EXPORT void Clay_SetMaxElementCount(int32_t maxElementCount);\n// Returns the maximum number of measured \"words\" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.\nCLAY_DLL_EXPORT int32_t Clay_GetMaxMeasureTextCacheWordCount(void);\n// Modifies the maximum number of measured \"words\" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.\n// This may require reallocating additional memory, and re-calling Clay_Initialize();\nCLAY_DLL_EXPORT void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount);\n// Resets Clay's internal text measurement cache. Useful if font mappings have changed or fonts have been reloaded.\nCLAY_DLL_EXPORT void Clay_ResetMeasureTextCache(void);\n\n// Internal API functions required by macros ----------------------\n\nCLAY_DLL_EXPORT void Clay__OpenElement(void);\nCLAY_DLL_EXPORT void Clay__OpenElementWithId(Clay_ElementId elementId);\nCLAY_DLL_EXPORT void Clay__ConfigureOpenElement(const Clay_ElementDeclaration config);\nCLAY_DLL_EXPORT void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *config);\nCLAY_DLL_EXPORT void Clay__CloseElement(void);\nCLAY_DLL_EXPORT Clay_ElementId Clay__HashString(Clay_String key, uint32_t seed);\nCLAY_DLL_EXPORT Clay_ElementId Clay__HashStringWithOffset(Clay_String key, uint32_t offset, uint32_t seed);\nCLAY_DLL_EXPORT void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig);\nCLAY_DLL_EXPORT Clay_TextElementConfig *Clay__StoreTextElementConfig(Clay_TextElementConfig config);\nCLAY_DLL_EXPORT uint32_t Clay__GetParentElementId(void);\n\nextern Clay_Color Clay__debugViewHighlightColor;\nextern uint32_t Clay__debugViewWidth;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // CLAY_HEADER\n\n// -----------------------------------------\n// IMPLEMENTATION --------------------------\n// -----------------------------------------\n#ifdef CLAY_IMPLEMENTATION\n#undef CLAY_IMPLEMENTATION\n\n#ifndef CLAY__NULL\n#define CLAY__NULL 0\n#endif\n\n#ifndef CLAY__MAXFLOAT\n#define CLAY__MAXFLOAT 3.40282346638528859812e+38F\n#endif\n\nClay_LayoutConfig CLAY_LAYOUT_DEFAULT = CLAY__DEFAULT_STRUCT;\n\nClay_Color Clay__Color_DEFAULT = CLAY__DEFAULT_STRUCT;\nClay_CornerRadius Clay__CornerRadius_DEFAULT = CLAY__DEFAULT_STRUCT;\nClay_BorderWidth Clay__BorderWidth_DEFAULT = CLAY__DEFAULT_STRUCT;\n\n// The below functions define array bounds checking and convenience functions for a provided type.\n#define CLAY__ARRAY_DEFINE_FUNCTIONS(typeName, arrayName)                                                       \\\n                                                                                                                \\\ntypedef struct                                                                                                  \\\n{                                                                                                               \\\n    int32_t length;                                                                                             \\\n    typeName *internalArray;                                                                                    \\\n} arrayName##Slice;                                                                                             \\\n                                                                                                                \\\ntypeName typeName##_DEFAULT = CLAY__DEFAULT_STRUCT;                                                             \\\n                                                                                                                \\\narrayName arrayName##_Allocate_Arena(int32_t capacity, Clay_Arena *arena) {                                     \\\n    return CLAY__INIT(arrayName){.capacity = capacity, .length = 0,                                             \\\n        .internalArray = (typeName *)Clay__Array_Allocate_Arena(capacity, sizeof(typeName), arena)};            \\\n}                                                                                                               \\\n                                                                                                                \\\ntypeName *arrayName##_Get(arrayName *array, int32_t index) {                                                    \\\n    return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &typeName##_DEFAULT;   \\\n}                                                                                                               \\\n                                                                                                                \\\ntypeName arrayName##_GetValue(arrayName *array, int32_t index) {                                                \\\n    return Clay__Array_RangeCheck(index, array->length) ? array->internalArray[index] : typeName##_DEFAULT;     \\\n}                                                                                                               \\\n                                                                                                                \\\ntypeName *arrayName##_Add(arrayName *array, typeName item) {                                                    \\\n    if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) {                                         \\\n        array->internalArray[array->length++] = item;                                                           \\\n        return &array->internalArray[array->length - 1];                                                        \\\n    }                                                                                                           \\\n    return &typeName##_DEFAULT;                                                                                 \\\n}                                                                                                               \\\n                                                                                                                \\\ntypeName *arrayName##Slice_Get(arrayName##Slice *slice, int32_t index) {                                        \\\n    return Clay__Array_RangeCheck(index, slice->length) ? &slice->internalArray[index] : &typeName##_DEFAULT;   \\\n}                                                                                                               \\\n                                                                                                                \\\ntypeName arrayName##_RemoveSwapback(arrayName *array, int32_t index) {                                          \\\n\tif (Clay__Array_RangeCheck(index, array->length)) {                                                         \\\n\t\tarray->length--;                                                                                        \\\n\t\ttypeName removed = array->internalArray[index];                                                         \\\n\t\tarray->internalArray[index] = array->internalArray[array->length];                                      \\\n\t\treturn removed;                                                                                         \\\n\t}                                                                                                           \\\n\treturn typeName##_DEFAULT;                                                                                  \\\n}                                                                                                               \\\n                                                                                                                \\\nvoid arrayName##_Set(arrayName *array, int32_t index, typeName value) {                                         \\\n\tif (Clay__Array_RangeCheck(index, array->capacity)) {                                                       \\\n\t\tarray->internalArray[index] = value;                                                                    \\\n\t\tarray->length = index < array->length ? array->length : index + 1;                                      \\\n\t}                                                                                                           \\\n}                                                                                                               \\\n\n#define CLAY__ARRAY_DEFINE(typeName, arrayName)     \\\ntypedef struct                                      \\\n{                                                   \\\n    int32_t capacity;                               \\\n    int32_t length;                                 \\\n    typeName *internalArray;                        \\\n} arrayName;                                        \\\n                                                    \\\nCLAY__ARRAY_DEFINE_FUNCTIONS(typeName, arrayName)   \\\n\nClay_Context *Clay__currentContext;\nint32_t Clay__defaultMaxElementCount = 8192;\nint32_t Clay__defaultMaxMeasureTextWordCacheCount = 16384;\n\nvoid Clay__ErrorHandlerFunctionDefault(Clay_ErrorData errorText) {\n    (void) errorText;\n}\n\nClay_String CLAY__SPACECHAR = { .length = 1, .chars = \" \" };\nClay_String CLAY__STRING_DEFAULT = { .length = 0, .chars = NULL };\n\ntypedef struct {\n    bool maxElementsExceeded;\n    bool maxRenderCommandsExceeded;\n    bool maxTextMeasureCacheExceeded;\n    bool textMeasurementFunctionNotSet;\n} Clay_BooleanWarnings;\n\ntypedef struct {\n    Clay_String baseMessage;\n    Clay_String dynamicMessage;\n} Clay__Warning;\n\nClay__Warning CLAY__WARNING_DEFAULT = CLAY__DEFAULT_STRUCT;\n\ntypedef struct {\n    int32_t capacity;\n    int32_t length;\n    Clay__Warning *internalArray;\n} Clay__WarningArray;\n\ntypedef struct {\n    Clay_Color backgroundColor;\n    Clay_CornerRadius cornerRadius;\n    void* userData;\n} Clay_SharedElementConfig;\n\nCLAY__WRAPPER_STRUCT(Clay_SharedElementConfig);\n\nClay__WarningArray Clay__WarningArray_Allocate_Arena(int32_t capacity, Clay_Arena *arena);\nClay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning item);\nvoid* Clay__Array_Allocate_Arena(int32_t capacity, uint32_t itemSize, Clay_Arena *arena);\nbool Clay__Array_RangeCheck(int32_t index, int32_t length);\nbool Clay__Array_AddCapacityCheck(int32_t length, int32_t capacity);\n\nCLAY__ARRAY_DEFINE(bool, Clay__boolArray)\nCLAY__ARRAY_DEFINE(int32_t, Clay__int32_tArray)\nCLAY__ARRAY_DEFINE(char, Clay__charArray)\nCLAY__ARRAY_DEFINE_FUNCTIONS(Clay_ElementId, Clay_ElementIdArray)\nCLAY__ARRAY_DEFINE(Clay_LayoutConfig, Clay__LayoutConfigArray)\nCLAY__ARRAY_DEFINE(Clay_TextElementConfig, Clay__TextElementConfigArray)\nCLAY__ARRAY_DEFINE(Clay_AspectRatioElementConfig, Clay__AspectRatioElementConfigArray)\nCLAY__ARRAY_DEFINE(Clay_ImageElementConfig, Clay__ImageElementConfigArray)\nCLAY__ARRAY_DEFINE(Clay_FloatingElementConfig, Clay__FloatingElementConfigArray)\nCLAY__ARRAY_DEFINE(Clay_CustomElementConfig, Clay__CustomElementConfigArray)\nCLAY__ARRAY_DEFINE(Clay_ClipElementConfig, Clay__ClipElementConfigArray)\nCLAY__ARRAY_DEFINE(Clay_BorderElementConfig, Clay__BorderElementConfigArray)\nCLAY__ARRAY_DEFINE(Clay_String, Clay__StringArray)\nCLAY__ARRAY_DEFINE(Clay_SharedElementConfig, Clay__SharedElementConfigArray)\nCLAY__ARRAY_DEFINE_FUNCTIONS(Clay_RenderCommand, Clay_RenderCommandArray)\n\ntypedef CLAY_PACKED_ENUM {\n    CLAY__ELEMENT_CONFIG_TYPE_NONE,\n    CLAY__ELEMENT_CONFIG_TYPE_BORDER,\n    CLAY__ELEMENT_CONFIG_TYPE_FLOATING,\n    CLAY__ELEMENT_CONFIG_TYPE_CLIP,\n    CLAY__ELEMENT_CONFIG_TYPE_ASPECT,\n    CLAY__ELEMENT_CONFIG_TYPE_IMAGE,\n    CLAY__ELEMENT_CONFIG_TYPE_TEXT,\n    CLAY__ELEMENT_CONFIG_TYPE_CUSTOM,\n    CLAY__ELEMENT_CONFIG_TYPE_SHARED,\n} Clay__ElementConfigType;\n\ntypedef union {\n    Clay_TextElementConfig *textElementConfig;\n    Clay_AspectRatioElementConfig *aspectRatioElementConfig;\n    Clay_ImageElementConfig *imageElementConfig;\n    Clay_FloatingElementConfig *floatingElementConfig;\n    Clay_CustomElementConfig *customElementConfig;\n    Clay_ClipElementConfig *clipElementConfig;\n    Clay_BorderElementConfig *borderElementConfig;\n    Clay_SharedElementConfig *sharedElementConfig;\n} Clay_ElementConfigUnion;\n\ntypedef struct {\n    Clay__ElementConfigType type;\n    Clay_ElementConfigUnion config;\n} Clay_ElementConfig;\n\nCLAY__ARRAY_DEFINE(Clay_ElementConfig, Clay__ElementConfigArray)\n\ntypedef struct {\n    Clay_Dimensions dimensions;\n    Clay_String line;\n} Clay__WrappedTextLine;\n\nCLAY__ARRAY_DEFINE(Clay__WrappedTextLine, Clay__WrappedTextLineArray)\n\ntypedef struct {\n    Clay_String text;\n    Clay_Dimensions preferredDimensions;\n    int32_t elementIndex;\n    Clay__WrappedTextLineArraySlice wrappedLines;\n} Clay__TextElementData;\n\nCLAY__ARRAY_DEFINE(Clay__TextElementData, Clay__TextElementDataArray)\n\ntypedef struct {\n    int32_t *elements;\n    uint16_t length;\n} Clay__LayoutElementChildren;\n\ntypedef struct {\n    union {\n        Clay__LayoutElementChildren children;\n        Clay__TextElementData *textElementData;\n    } childrenOrTextContent;\n    Clay_Dimensions dimensions;\n    Clay_Dimensions minDimensions;\n    Clay_LayoutConfig *layoutConfig;\n    Clay__ElementConfigArraySlice elementConfigs;\n    uint32_t id;\n    uint16_t floatingChildrenCount;\n} Clay_LayoutElement;\n\nCLAY__ARRAY_DEFINE(Clay_LayoutElement, Clay_LayoutElementArray)\n\ntypedef struct {\n    Clay_LayoutElement *layoutElement;\n    Clay_BoundingBox boundingBox;\n    Clay_Dimensions contentSize;\n    Clay_Vector2 scrollOrigin;\n    Clay_Vector2 pointerOrigin;\n    Clay_Vector2 scrollMomentum;\n    Clay_Vector2 scrollPosition;\n    Clay_Vector2 previousDelta;\n    float momentumTime;\n    uint32_t elementId;\n    bool openThisFrame;\n    bool pointerScrollActive;\n} Clay__ScrollContainerDataInternal;\n\nCLAY__ARRAY_DEFINE(Clay__ScrollContainerDataInternal, Clay__ScrollContainerDataInternalArray)\n\ntypedef struct {\n    bool collision;\n    bool collapsed;\n} Clay__DebugElementData;\n\nCLAY__ARRAY_DEFINE(Clay__DebugElementData, Clay__DebugElementDataArray)\n\ntypedef struct { // todo get this struct into a single cache line\n    Clay_BoundingBox boundingBox;\n    Clay_ElementId elementId;\n    Clay_LayoutElement* layoutElement;\n    void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData);\n    void *hoverFunctionUserData;\n    int32_t nextIndex;\n    uint32_t generation;\n    Clay__DebugElementData *debugData;\n} Clay_LayoutElementHashMapItem;\n\nCLAY__ARRAY_DEFINE(Clay_LayoutElementHashMapItem, Clay__LayoutElementHashMapItemArray)\n\ntypedef struct {\n    int32_t startOffset;\n    int32_t length;\n    float width;\n    int32_t next;\n} Clay__MeasuredWord;\n\nCLAY__ARRAY_DEFINE(Clay__MeasuredWord, Clay__MeasuredWordArray)\n\ntypedef struct {\n    Clay_Dimensions unwrappedDimensions;\n    int32_t measuredWordsStartIndex;\n    float minWidth;\n    bool containsNewlines;\n    // Hash map data\n    uint32_t id;\n    int32_t nextIndex;\n    uint32_t generation;\n} Clay__MeasureTextCacheItem;\n\nCLAY__ARRAY_DEFINE(Clay__MeasureTextCacheItem, Clay__MeasureTextCacheItemArray)\n\ntypedef struct {\n    Clay_LayoutElement *layoutElement;\n    Clay_Vector2 position;\n    Clay_Vector2 nextChildOffset;\n} Clay__LayoutElementTreeNode;\n\nCLAY__ARRAY_DEFINE(Clay__LayoutElementTreeNode, Clay__LayoutElementTreeNodeArray)\n\ntypedef struct {\n    int32_t layoutElementIndex;\n    uint32_t parentId; // This can be zero in the case of the root layout tree\n    uint32_t clipElementId; // This can be zero if there is no clip element\n    int16_t zIndex;\n    Clay_Vector2 pointerOffset; // Only used when scroll containers are managed externally\n} Clay__LayoutElementTreeRoot;\n\nCLAY__ARRAY_DEFINE(Clay__LayoutElementTreeRoot, Clay__LayoutElementTreeRootArray)\n\nstruct Clay_Context {\n    int32_t maxElementCount;\n    int32_t maxMeasureTextCacheWordCount;\n    bool warningsEnabled;\n    Clay_ErrorHandler errorHandler;\n    Clay_BooleanWarnings booleanWarnings;\n    Clay__WarningArray warnings;\n\n    Clay_PointerData pointerInfo;\n    Clay_Dimensions layoutDimensions;\n    Clay_ElementId dynamicElementIndexBaseHash;\n    uint32_t dynamicElementIndex;\n    bool debugModeEnabled;\n    bool disableCulling;\n    bool externalScrollHandlingEnabled;\n    uint32_t debugSelectedElementId;\n    uint32_t generation;\n    uintptr_t arenaResetOffset;\n    void *measureTextUserData;\n    void *queryScrollOffsetUserData;\n    Clay_Arena internalArena;\n    // Layout Elements / Render Commands\n    Clay_LayoutElementArray layoutElements;\n    Clay_RenderCommandArray renderCommands;\n    Clay__int32_tArray openLayoutElementStack;\n    Clay__int32_tArray layoutElementChildren;\n    Clay__int32_tArray layoutElementChildrenBuffer;\n    Clay__TextElementDataArray textElementData;\n    Clay__int32_tArray aspectRatioElementIndexes;\n    Clay__int32_tArray reusableElementIndexBuffer;\n    Clay__int32_tArray layoutElementClipElementIds;\n    // Configs\n    Clay__LayoutConfigArray layoutConfigs;\n    Clay__ElementConfigArray elementConfigs;\n    Clay__TextElementConfigArray textElementConfigs;\n    Clay__AspectRatioElementConfigArray aspectRatioElementConfigs;\n    Clay__ImageElementConfigArray imageElementConfigs;\n    Clay__FloatingElementConfigArray floatingElementConfigs;\n    Clay__ClipElementConfigArray clipElementConfigs;\n    Clay__CustomElementConfigArray customElementConfigs;\n    Clay__BorderElementConfigArray borderElementConfigs;\n    Clay__SharedElementConfigArray sharedElementConfigs;\n    // Misc Data Structures\n    Clay__StringArray layoutElementIdStrings;\n    Clay__WrappedTextLineArray wrappedTextLines;\n    Clay__LayoutElementTreeNodeArray layoutElementTreeNodeArray1;\n    Clay__LayoutElementTreeRootArray layoutElementTreeRoots;\n    Clay__LayoutElementHashMapItemArray layoutElementsHashMapInternal;\n    Clay__int32_tArray layoutElementsHashMap;\n    Clay__MeasureTextCacheItemArray measureTextHashMapInternal;\n    Clay__int32_tArray measureTextHashMapInternalFreeList;\n    Clay__int32_tArray measureTextHashMap;\n    Clay__MeasuredWordArray measuredWords;\n    Clay__int32_tArray measuredWordsFreeList;\n    Clay__int32_tArray openClipElementStack;\n    Clay_ElementIdArray pointerOverIds;\n    Clay__ScrollContainerDataInternalArray scrollContainerDatas;\n    Clay__boolArray treeNodeVisited;\n    Clay__charArray dynamicStringData;\n    Clay__DebugElementDataArray debugElementData;\n};\n\nClay_Context* Clay__Context_Allocate_Arena(Clay_Arena *arena) {\n    size_t totalSizeBytes = sizeof(Clay_Context);\n    if (totalSizeBytes > arena->capacity)\n    {\n        return NULL;\n    }\n    arena->nextAllocation += totalSizeBytes;\n    return (Clay_Context*)(arena->memory);\n}\n\nClay_String Clay__WriteStringToCharBuffer(Clay__charArray *buffer, Clay_String string) {\n    for (int32_t i = 0; i < string.length; i++) {\n        buffer->internalArray[buffer->length + i] = string.chars[i];\n    }\n    buffer->length += string.length;\n    return CLAY__INIT(Clay_String) { .length = string.length, .chars = (const char *)(buffer->internalArray + buffer->length - string.length) };\n}\n\n#ifdef CLAY_WASM\n    __attribute__((import_module(\"clay\"), import_name(\"measureTextFunction\"))) Clay_Dimensions Clay__MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);\n    __attribute__((import_module(\"clay\"), import_name(\"queryScrollOffsetFunction\"))) Clay_Vector2 Clay__QueryScrollOffset(uint32_t elementId, void *userData);\n#else\n    Clay_Dimensions (*Clay__MeasureText)(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);\n    Clay_Vector2 (*Clay__QueryScrollOffset)(uint32_t elementId, void *userData);\n#endif\n\nClay_LayoutElement* Clay__GetOpenLayoutElement(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    return Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1));\n}\n\nuint32_t Clay__GetParentElementId(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    return Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2))->id;\n}\n\nClay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config) {  return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY_LAYOUT_DEFAULT : Clay__LayoutConfigArray_Add(&Clay_GetCurrentContext()->layoutConfigs, config); }\nClay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config) {  return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_TextElementConfig_DEFAULT : Clay__TextElementConfigArray_Add(&Clay_GetCurrentContext()->textElementConfigs, config); }\nClay_AspectRatioElementConfig * Clay__StoreAspectRatioElementConfig(Clay_AspectRatioElementConfig config) {  return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_AspectRatioElementConfig_DEFAULT : Clay__AspectRatioElementConfigArray_Add(&Clay_GetCurrentContext()->aspectRatioElementConfigs, config); }\nClay_ImageElementConfig * Clay__StoreImageElementConfig(Clay_ImageElementConfig config) {  return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_ImageElementConfig_DEFAULT : Clay__ImageElementConfigArray_Add(&Clay_GetCurrentContext()->imageElementConfigs, config); }\nClay_FloatingElementConfig * Clay__StoreFloatingElementConfig(Clay_FloatingElementConfig config) {  return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_FloatingElementConfig_DEFAULT : Clay__FloatingElementConfigArray_Add(&Clay_GetCurrentContext()->floatingElementConfigs, config); }\nClay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConfig config) {  return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_CustomElementConfig_DEFAULT : Clay__CustomElementConfigArray_Add(&Clay_GetCurrentContext()->customElementConfigs, config); }\nClay_ClipElementConfig * Clay__StoreClipElementConfig(Clay_ClipElementConfig config) {  return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_ClipElementConfig_DEFAULT : Clay__ClipElementConfigArray_Add(&Clay_GetCurrentContext()->clipElementConfigs, config); }\nClay_BorderElementConfig * Clay__StoreBorderElementConfig(Clay_BorderElementConfig config) {  return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_BorderElementConfig_DEFAULT : Clay__BorderElementConfigArray_Add(&Clay_GetCurrentContext()->borderElementConfigs, config); }\nClay_SharedElementConfig * Clay__StoreSharedElementConfig(Clay_SharedElementConfig config) {  return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_SharedElementConfig_DEFAULT : Clay__SharedElementConfigArray_Add(&Clay_GetCurrentContext()->sharedElementConfigs, config); }\n\nClay_ElementConfig Clay__AttachElementConfig(Clay_ElementConfigUnion config, Clay__ElementConfigType type) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->booleanWarnings.maxElementsExceeded) {\n        return CLAY__INIT(Clay_ElementConfig) CLAY__DEFAULT_STRUCT;\n    }\n    Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();\n    openLayoutElement->elementConfigs.length++;\n    return *Clay__ElementConfigArray_Add(&context->elementConfigs, CLAY__INIT(Clay_ElementConfig) { .type = type, .config = config });\n}\n\nClay_ElementConfigUnion Clay__FindElementConfigWithType(Clay_LayoutElement *element, Clay__ElementConfigType type) {\n    for (int32_t i = 0; i < element->elementConfigs.length; i++) {\n        Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&element->elementConfigs, i);\n        if (config->type == type) {\n            return config->config;\n        }\n    }\n    return CLAY__INIT(Clay_ElementConfigUnion) { NULL };\n}\n\nClay_ElementId Clay__HashNumber(const uint32_t offset, const uint32_t seed) {\n    uint32_t hash = seed;\n    hash += (offset + 48);\n    hash += (hash << 10);\n    hash ^= (hash >> 6);\n\n    hash += (hash << 3);\n    hash ^= (hash >> 11);\n    hash += (hash << 15);\n    return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = seed, .stringId = CLAY__STRING_DEFAULT }; // Reserve the hash result of zero as \"null id\"\n}\n\nClay_ElementId Clay__HashString(Clay_String key, const uint32_t seed) {\n    uint32_t hash = seed;\n\n    for (int32_t i = 0; i < key.length; i++) {\n        hash += key.chars[i];\n        hash += (hash << 10);\n        hash ^= (hash >> 6);\n    }\n\n    hash += (hash << 3);\n    hash ^= (hash >> 11);\n    hash += (hash << 15);\n    return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = 0, .baseId = hash + 1, .stringId = key }; // Reserve the hash result of zero as \"null id\"\n}\n\nClay_ElementId Clay__HashStringWithOffset(Clay_String key, const uint32_t offset, const uint32_t seed) {\n    uint32_t hash = 0;\n    uint32_t base = seed;\n\n    for (int32_t i = 0; i < key.length; i++) {\n        base += key.chars[i];\n        base += (base << 10);\n        base ^= (base >> 6);\n    }\n    hash = base;\n    hash += offset;\n    hash += (hash << 10);\n    hash ^= (hash >> 6);\n\n    hash += (hash << 3);\n    base += (base << 3);\n    hash ^= (hash >> 11);\n    base ^= (base >> 11);\n    hash += (hash << 15);\n    base += (base << 15);\n    return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = base + 1, .stringId = key }; // Reserve the hash result of zero as \"null id\"\n}\n\n#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))\nstatic inline __m128i Clay__SIMDRotateLeft(__m128i x, int r) {\n    return _mm_or_si128(_mm_slli_epi64(x, r), _mm_srli_epi64(x, 64 - r));\n}\n\nstatic inline void Clay__SIMDARXMix(__m128i* a, __m128i* b) {\n    *a = _mm_add_epi64(*a, *b);\n    *b = _mm_xor_si128(Clay__SIMDRotateLeft(*b, 17), *a);\n}\n\nuint64_t Clay__HashData(const uint8_t* data, size_t length) {\n    // Pinched these constants from the BLAKE implementation\n    __m128i v0 = _mm_set1_epi64x(0x6a09e667f3bcc908ULL);\n    __m128i v1 = _mm_set1_epi64x(0xbb67ae8584caa73bULL);\n    __m128i v2 = _mm_set1_epi64x(0x3c6ef372fe94f82bULL);\n    __m128i v3 = _mm_set1_epi64x(0xa54ff53a5f1d36f1ULL);\n\n    uint8_t overflowBuffer[16] = { 0 };  // Temporary buffer for small inputs\n\n    while (length > 0) {\n        __m128i msg;\n        if (length >= 16) {\n            msg = _mm_loadu_si128((const __m128i*)data);\n            data += 16;\n            length -= 16;\n        }\n        else {\n            for (size_t i = 0; i < length; i++) {\n                overflowBuffer[i] = data[i];\n            }\n            msg = _mm_loadu_si128((const __m128i*)overflowBuffer);\n            length = 0;\n        }\n\n        v0 = _mm_xor_si128(v0, msg);\n        Clay__SIMDARXMix(&v0, &v1);\n        Clay__SIMDARXMix(&v2, &v3);\n\n        v0 = _mm_add_epi64(v0, v2);\n        v1 = _mm_add_epi64(v1, v3);\n    }\n\n    Clay__SIMDARXMix(&v0, &v1);\n    Clay__SIMDARXMix(&v2, &v3);\n    v0 = _mm_add_epi64(v0, v2);\n    v1 = _mm_add_epi64(v1, v3);\n    v0 = _mm_add_epi64(v0, v1);\n\n    uint64_t result[2];\n    _mm_storeu_si128((__m128i*)result, v0);\n\n    return result[0] ^ result[1];\n}\n#elif !defined(CLAY_DISABLE_SIMD) && defined(__aarch64__)\nstatic inline void Clay__SIMDARXMix(uint64x2_t* a, uint64x2_t* b) {\n    *a = vaddq_u64(*a, *b);\n    *b = veorq_u64(vorrq_u64(vshlq_n_u64(*b, 17), vshrq_n_u64(*b, 64 - 17)), *a);\n}\n\nuint64_t Clay__HashData(const uint8_t* data, size_t length) {\n    // Pinched these constants from the BLAKE implementation\n    uint64x2_t v0 = vdupq_n_u64(0x6a09e667f3bcc908ULL);\n    uint64x2_t v1 = vdupq_n_u64(0xbb67ae8584caa73bULL);\n    uint64x2_t v2 = vdupq_n_u64(0x3c6ef372fe94f82bULL);\n    uint64x2_t v3 = vdupq_n_u64(0xa54ff53a5f1d36f1ULL);\n\n    uint8_t overflowBuffer[8] = { 0 };\n\n    while (length > 0) {\n        uint64x2_t msg;\n        if (length > 16) {\n            msg = vld1q_u64((const uint64_t*)data);\n            data += 16;\n            length -= 16;\n        }\n        else if (length > 8) {\n            msg = vcombine_u64(vld1_u64((const uint64_t*)data), vdup_n_u64(0));\n            data += 8;\n            length -= 8;\n        }\n        else {\n            for (size_t i = 0; i < length; i++) {\n                overflowBuffer[i] = data[i];\n            }\n            uint8x8_t lower = vld1_u8(overflowBuffer);\n            msg = vreinterpretq_u64_u8(vcombine_u8(lower, vdup_n_u8(0)));\n            length = 0;\n        }\n        v0 = veorq_u64(v0, msg);\n        Clay__SIMDARXMix(&v0, &v1);\n        Clay__SIMDARXMix(&v2, &v3);\n\n        v0 = vaddq_u64(v0, v2);\n        v1 = vaddq_u64(v1, v3);\n    }\n\n    Clay__SIMDARXMix(&v0, &v1);\n    Clay__SIMDARXMix(&v2, &v3);\n    v0 = vaddq_u64(v0, v2);\n    v1 = vaddq_u64(v1, v3);\n    v0 = vaddq_u64(v0, v1);\n\n    uint64_t result[2];\n    vst1q_u64(result, v0);\n\n    return result[0] ^ result[1];\n}\n#else\nuint64_t Clay__HashData(const uint8_t* data, size_t length) {\n    uint64_t hash = 0;\n\n    for (size_t i = 0; i < length; i++) {\n        hash += data[i];\n        hash += (hash << 10);\n        hash ^= (hash >> 6);\n    }\n    return hash;\n}\n#endif\n\nuint32_t Clay__HashStringContentsWithConfig(Clay_String *text, Clay_TextElementConfig *config) {\n    uint32_t hash = 0;\n    if (text->isStaticallyAllocated) {\n        hash += (uintptr_t)text->chars;\n        hash += (hash << 10);\n        hash ^= (hash >> 6);\n        hash += text->length;\n        hash += (hash << 10);\n        hash ^= (hash >> 6);\n    } else {\n        hash = Clay__HashData((const uint8_t *)text->chars, text->length) % UINT32_MAX;\n    }\n\n    hash += config->fontId;\n    hash += (hash << 10);\n    hash ^= (hash >> 6);\n\n    hash += config->fontSize;\n    hash += (hash << 10);\n    hash ^= (hash >> 6);\n\n    hash += config->letterSpacing;\n    hash += (hash << 10);\n    hash ^= (hash >> 6);\n\n    hash += (hash << 3);\n    hash ^= (hash >> 11);\n    hash += (hash << 15);\n    return hash + 1; // Reserve the hash result of zero as \"null id\"\n}\n\nClay__MeasuredWord *Clay__AddMeasuredWord(Clay__MeasuredWord word, Clay__MeasuredWord *previousWord) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->measuredWordsFreeList.length > 0) {\n        uint32_t newItemIndex = Clay__int32_tArray_GetValue(&context->measuredWordsFreeList, (int)context->measuredWordsFreeList.length - 1);\n        context->measuredWordsFreeList.length--;\n        Clay__MeasuredWordArray_Set(&context->measuredWords, (int)newItemIndex, word);\n        previousWord->next = (int32_t)newItemIndex;\n        return Clay__MeasuredWordArray_Get(&context->measuredWords, (int)newItemIndex);\n    } else {\n        previousWord->next = (int32_t)context->measuredWords.length;\n        return Clay__MeasuredWordArray_Add(&context->measuredWords, word);\n    }\n}\n\nClay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_TextElementConfig *config) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    #ifndef CLAY_WASM\n    if (!Clay__MeasureText) {\n        if (!context->booleanWarnings.textMeasurementFunctionNotSet) {\n            context->booleanWarnings.textMeasurementFunctionNotSet = true;\n            context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n                    .errorType = CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,\n                    .errorText = CLAY_STRING(\"Clay's internal MeasureText function is null. You may have forgotten to call Clay_SetMeasureTextFunction(), or passed a NULL function pointer by mistake.\"),\n                    .userData = context->errorHandler.userData });\n        }\n        return &Clay__MeasureTextCacheItem_DEFAULT;\n    }\n    #endif\n    uint32_t id = Clay__HashStringContentsWithConfig(text, config);\n    uint32_t hashBucket = id % (context->maxMeasureTextCacheWordCount / 32);\n    int32_t elementIndexPrevious = 0;\n    int32_t elementIndex = context->measureTextHashMap.internalArray[hashBucket];\n    while (elementIndex != 0) {\n        Clay__MeasureTextCacheItem *hashEntry = Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, elementIndex);\n        if (hashEntry->id == id) {\n            hashEntry->generation = context->generation;\n            return hashEntry;\n        }\n        // This element hasn't been seen in a few frames, delete the hash map item\n        if (context->generation - hashEntry->generation > 2) {\n            // Add all the measured words that were included in this measurement to the freelist\n            int32_t nextWordIndex = hashEntry->measuredWordsStartIndex;\n            while (nextWordIndex != -1) {\n                Clay__MeasuredWord *measuredWord = Clay__MeasuredWordArray_Get(&context->measuredWords, nextWordIndex);\n                Clay__int32_tArray_Add(&context->measuredWordsFreeList, nextWordIndex);\n                nextWordIndex = measuredWord->next;\n            }\n\n            int32_t nextIndex = hashEntry->nextIndex;\n            Clay__MeasureTextCacheItemArray_Set(&context->measureTextHashMapInternal, elementIndex, CLAY__INIT(Clay__MeasureTextCacheItem) { .measuredWordsStartIndex = -1 });\n            Clay__int32_tArray_Add(&context->measureTextHashMapInternalFreeList, elementIndex);\n            if (elementIndexPrevious == 0) {\n                context->measureTextHashMap.internalArray[hashBucket] = nextIndex;\n            } else {\n                Clay__MeasureTextCacheItem *previousHashEntry = Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, elementIndexPrevious);\n                previousHashEntry->nextIndex = nextIndex;\n            }\n            elementIndex = nextIndex;\n        } else {\n            elementIndexPrevious = elementIndex;\n            elementIndex = hashEntry->nextIndex;\n        }\n    }\n\n    int32_t newItemIndex = 0;\n    Clay__MeasureTextCacheItem newCacheItem = { .measuredWordsStartIndex = -1, .id = id, .generation = context->generation };\n    Clay__MeasureTextCacheItem *measured = NULL;\n    if (context->measureTextHashMapInternalFreeList.length > 0) {\n        newItemIndex = Clay__int32_tArray_GetValue(&context->measureTextHashMapInternalFreeList, context->measureTextHashMapInternalFreeList.length - 1);\n        context->measureTextHashMapInternalFreeList.length--;\n        Clay__MeasureTextCacheItemArray_Set(&context->measureTextHashMapInternal, newItemIndex, newCacheItem);\n        measured = Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, newItemIndex);\n    } else {\n        if (context->measureTextHashMapInternal.length == context->measureTextHashMapInternal.capacity - 1) {\n            if (!context->booleanWarnings.maxTextMeasureCacheExceeded) {\n                context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n                        .errorType = CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,\n                        .errorText = CLAY_STRING(\"Clay ran out of capacity while attempting to measure text elements. Try using Clay_SetMaxElementCount() with a higher value.\"),\n                        .userData = context->errorHandler.userData });\n                context->booleanWarnings.maxTextMeasureCacheExceeded = true;\n            }\n            return &Clay__MeasureTextCacheItem_DEFAULT;\n        }\n        measured = Clay__MeasureTextCacheItemArray_Add(&context->measureTextHashMapInternal, newCacheItem);\n        newItemIndex = context->measureTextHashMapInternal.length - 1;\n    }\n\n    int32_t start = 0;\n    int32_t end = 0;\n    float lineWidth = 0;\n    float measuredWidth = 0;\n    float measuredHeight = 0;\n    float spaceWidth = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) { .length = 1, .chars = CLAY__SPACECHAR.chars, .baseChars = CLAY__SPACECHAR.chars }, config, context->measureTextUserData).width;\n    Clay__MeasuredWord tempWord = { .next = -1 };\n    Clay__MeasuredWord *previousWord = &tempWord;\n    while (end < text->length) {\n        if (context->measuredWords.length == context->measuredWords.capacity - 1) {\n            if (!context->booleanWarnings.maxTextMeasureCacheExceeded) {\n                context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n                    .errorType = CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED,\n                    .errorText = CLAY_STRING(\"Clay has run out of space in it's internal text measurement cache. Try using Clay_SetMaxMeasureTextCacheWordCount() (default 16384, with 1 unit storing 1 measured word).\"),\n                    .userData = context->errorHandler.userData });\n                context->booleanWarnings.maxTextMeasureCacheExceeded = true;\n            }\n            return &Clay__MeasureTextCacheItem_DEFAULT;\n        }\n        char current = text->chars[end];\n        if (current == ' ' || current == '\\n') {\n            int32_t length = end - start;\n            Clay_Dimensions dimensions = CLAY__DEFAULT_STRUCT;\n            if (length > 0) {\n                dimensions = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) {.length = length, .chars = &text->chars[start], .baseChars = text->chars}, config, context->measureTextUserData);\n            }\n            measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth);\n            measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);\n            if (current == ' ') {\n                dimensions.width += spaceWidth;\n                previousWord = Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = length + 1, .width = dimensions.width, .next = -1 }, previousWord);\n                lineWidth += dimensions.width;\n            }\n            if (current == '\\n') {\n                if (length > 0) {\n                    previousWord = Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = length, .width = dimensions.width, .next = -1 }, previousWord);\n                }\n                previousWord = Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = end + 1, .length = 0, .width = 0, .next = -1 }, previousWord);\n                lineWidth += dimensions.width;\n                measuredWidth = CLAY__MAX(lineWidth, measuredWidth);\n                measured->containsNewlines = true;\n                lineWidth = 0;\n            }\n            start = end + 1;\n        }\n        end++;\n    }\n    if (end - start > 0) {\n        Clay_Dimensions dimensions = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) { .length = end - start, .chars = &text->chars[start], .baseChars = text->chars }, config, context->measureTextUserData);\n        Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = end - start, .width = dimensions.width, .next = -1 }, previousWord);\n        lineWidth += dimensions.width;\n        measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);\n        measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth);\n    }\n    measuredWidth = CLAY__MAX(lineWidth, measuredWidth) - config->letterSpacing;\n\n    measured->measuredWordsStartIndex = tempWord.next;\n    measured->unwrappedDimensions.width = measuredWidth;\n    measured->unwrappedDimensions.height = measuredHeight;\n\n    if (elementIndexPrevious != 0) {\n        Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, elementIndexPrevious)->nextIndex = newItemIndex;\n    } else {\n        context->measureTextHashMap.internalArray[hashBucket] = newItemIndex;\n    }\n    return measured;\n}\n\nbool Clay__PointIsInsideRect(Clay_Vector2 point, Clay_BoundingBox rect) {\n    return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;\n}\n\nClay_LayoutElementHashMapItem* Clay__AddHashMapItem(Clay_ElementId elementId, Clay_LayoutElement* layoutElement) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->layoutElementsHashMapInternal.length == context->layoutElementsHashMapInternal.capacity - 1) {\n        return NULL;\n    }\n    Clay_LayoutElementHashMapItem item = { .elementId = elementId, .layoutElement = layoutElement, .nextIndex = -1, .generation = context->generation + 1 };\n    uint32_t hashBucket = elementId.id % context->layoutElementsHashMap.capacity;\n    int32_t hashItemPrevious = -1;\n    int32_t hashItemIndex = context->layoutElementsHashMap.internalArray[hashBucket];\n    while (hashItemIndex != -1) { // Just replace collision, not a big deal - leave it up to the end user\n        Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Get(&context->layoutElementsHashMapInternal, hashItemIndex);\n        if (hashItem->elementId.id == elementId.id) { // Collision - resolve based on generation\n            item.nextIndex = hashItem->nextIndex;\n            if (hashItem->generation <= context->generation) { // First collision - assume this is the \"same\" element\n                hashItem->elementId = elementId; // Make sure to copy this across. If the stringId reference has changed, we should update the hash item to use the new one.\n                hashItem->generation = context->generation + 1;\n                hashItem->layoutElement = layoutElement;\n                hashItem->debugData->collision = false;\n                hashItem->onHoverFunction = NULL;\n                hashItem->hoverFunctionUserData = 0;\n            } else { // Multiple collisions this frame - two elements have the same ID\n                context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n                    .errorType = CLAY_ERROR_TYPE_DUPLICATE_ID,\n                    .errorText = CLAY_STRING(\"An element with this ID was already previously declared during this layout.\"),\n                    .userData = context->errorHandler.userData });\n                if (context->debugModeEnabled) {\n                    hashItem->debugData->collision = true;\n                }\n            }\n            return hashItem;\n        }\n        hashItemPrevious = hashItemIndex;\n        hashItemIndex = hashItem->nextIndex;\n    }\n    Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Add(&context->layoutElementsHashMapInternal, item);\n    hashItem->debugData = Clay__DebugElementDataArray_Add(&context->debugElementData, CLAY__INIT(Clay__DebugElementData) CLAY__DEFAULT_STRUCT);\n    if (hashItemPrevious != -1) {\n        Clay__LayoutElementHashMapItemArray_Get(&context->layoutElementsHashMapInternal, hashItemPrevious)->nextIndex = (int32_t)context->layoutElementsHashMapInternal.length - 1;\n    } else {\n        context->layoutElementsHashMap.internalArray[hashBucket] = (int32_t)context->layoutElementsHashMapInternal.length - 1;\n    }\n    return hashItem;\n}\n\nClay_LayoutElementHashMapItem *Clay__GetHashMapItem(uint32_t id) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    uint32_t hashBucket = id % context->layoutElementsHashMap.capacity;\n    int32_t elementIndex = context->layoutElementsHashMap.internalArray[hashBucket];\n    while (elementIndex != -1) {\n        Clay_LayoutElementHashMapItem *hashEntry = Clay__LayoutElementHashMapItemArray_Get(&context->layoutElementsHashMapInternal, elementIndex);\n        if (hashEntry->elementId.id == id) {\n            return hashEntry;\n        }\n        elementIndex = hashEntry->nextIndex;\n    }\n    return &Clay_LayoutElementHashMapItem_DEFAULT;\n}\n\nClay_ElementId Clay__GenerateIdForAnonymousElement(Clay_LayoutElement *openLayoutElement) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    Clay_LayoutElement *parentElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2));\n    uint32_t offset = parentElement->childrenOrTextContent.children.length + parentElement->floatingChildrenCount;\n    Clay_ElementId elementId = Clay__HashNumber(offset, parentElement->id);\n    openLayoutElement->id = elementId.id;\n    Clay__AddHashMapItem(elementId, openLayoutElement);\n    Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);\n    return elementId;\n}\n\nbool Clay__ElementHasConfig(Clay_LayoutElement *layoutElement, Clay__ElementConfigType type) {\n    for (int32_t i = 0; i < layoutElement->elementConfigs.length; i++) {\n        if (Clay__ElementConfigArraySlice_Get(&layoutElement->elementConfigs, i)->type == type) {\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid Clay__UpdateAspectRatioBox(Clay_LayoutElement *layoutElement) {\n    for (int32_t j = 0; j < layoutElement->elementConfigs.length; j++) {\n        Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&layoutElement->elementConfigs, j);\n        if (config->type == CLAY__ELEMENT_CONFIG_TYPE_ASPECT) {\n            Clay_AspectRatioElementConfig *aspectConfig = config->config.aspectRatioElementConfig;\n            if (aspectConfig->aspectRatio == 0) {\n                break;\n            }\n            if (layoutElement->dimensions.width == 0 && layoutElement->dimensions.height != 0) {\n                layoutElement->dimensions.width = layoutElement->dimensions.height * aspectConfig->aspectRatio;\n            } else if (layoutElement->dimensions.width != 0 && layoutElement->dimensions.height == 0) {\n                layoutElement->dimensions.height = layoutElement->dimensions.width * (1 / aspectConfig->aspectRatio);\n            }\n            break;\n        }\n    }\n}\n\nvoid Clay__CloseElement(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->booleanWarnings.maxElementsExceeded) {\n        return;\n    }\n    Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();\n    Clay_LayoutConfig *layoutConfig = openLayoutElement->layoutConfig;\n    if (!layoutConfig) {\n        openLayoutElement->layoutConfig = &Clay_LayoutConfig_DEFAULT;\n        layoutConfig = &Clay_LayoutConfig_DEFAULT;\n    }\n    bool elementHasClipHorizontal = false;\n    bool elementHasClipVertical = false;\n    for (int32_t i = 0; i < openLayoutElement->elementConfigs.length; i++) {\n        Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&openLayoutElement->elementConfigs, i);\n        if (config->type == CLAY__ELEMENT_CONFIG_TYPE_CLIP) {\n            elementHasClipHorizontal = config->config.clipElementConfig->horizontal;\n            elementHasClipVertical = config->config.clipElementConfig->vertical;\n            context->openClipElementStack.length--;\n            break;\n        } else if (config->type == CLAY__ELEMENT_CONFIG_TYPE_FLOATING) {\n            context->openClipElementStack.length--;\n        }\n    }\n\n    float leftRightPadding = (float)(layoutConfig->padding.left + layoutConfig->padding.right);\n    float topBottomPadding = (float)(layoutConfig->padding.top + layoutConfig->padding.bottom);\n\n    // Attach children to the current open element\n    openLayoutElement->childrenOrTextContent.children.elements = &context->layoutElementChildren.internalArray[context->layoutElementChildren.length];\n    if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {\n        openLayoutElement->dimensions.width = leftRightPadding;\n        openLayoutElement->minDimensions.width = leftRightPadding;\n        for (int32_t i = 0; i < openLayoutElement->childrenOrTextContent.children.length; i++) {\n            int32_t childIndex = Clay__int32_tArray_GetValue(&context->layoutElementChildrenBuffer, (int)context->layoutElementChildrenBuffer.length - openLayoutElement->childrenOrTextContent.children.length + i);\n            Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);\n            openLayoutElement->dimensions.width += child->dimensions.width;\n            openLayoutElement->dimensions.height = CLAY__MAX(openLayoutElement->dimensions.height, child->dimensions.height + topBottomPadding);\n            // Minimum size of child elements doesn't matter to clip containers as they can shrink and hide their contents\n            if (!elementHasClipHorizontal) {\n                openLayoutElement->minDimensions.width += child->minDimensions.width;\n            }\n            if (!elementHasClipVertical) {\n                openLayoutElement->minDimensions.height = CLAY__MAX(openLayoutElement->minDimensions.height, child->minDimensions.height + topBottomPadding);\n            }\n            Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);\n        }\n        float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);\n        openLayoutElement->dimensions.width += childGap;\n        if (!elementHasClipHorizontal) {\n            openLayoutElement->minDimensions.width += childGap;\n        }\n    }\n    else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {\n        openLayoutElement->dimensions.height = topBottomPadding;\n        openLayoutElement->minDimensions.height = topBottomPadding;\n        for (int32_t i = 0; i < openLayoutElement->childrenOrTextContent.children.length; i++) {\n            int32_t childIndex = Clay__int32_tArray_GetValue(&context->layoutElementChildrenBuffer, (int)context->layoutElementChildrenBuffer.length - openLayoutElement->childrenOrTextContent.children.length + i);\n            Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);\n            openLayoutElement->dimensions.height += child->dimensions.height;\n            openLayoutElement->dimensions.width = CLAY__MAX(openLayoutElement->dimensions.width, child->dimensions.width + leftRightPadding);\n            // Minimum size of child elements doesn't matter to clip containers as they can shrink and hide their contents\n            if (!elementHasClipVertical) {\n                openLayoutElement->minDimensions.height += child->minDimensions.height;\n            }\n            if (!elementHasClipHorizontal) {\n                openLayoutElement->minDimensions.width = CLAY__MAX(openLayoutElement->minDimensions.width, child->minDimensions.width + leftRightPadding);\n            }\n            Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);\n        }\n        float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);\n        openLayoutElement->dimensions.height += childGap;\n        if (!elementHasClipVertical) {\n            openLayoutElement->minDimensions.height += childGap;\n        }\n    }\n\n    context->layoutElementChildrenBuffer.length -= openLayoutElement->childrenOrTextContent.children.length;\n\n    // Clamp element min and max width to the values configured in the layout\n    if (layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) {\n        if (layoutConfig->sizing.width.size.minMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier\n            layoutConfig->sizing.width.size.minMax.max = CLAY__MAXFLOAT;\n        }\n        openLayoutElement->dimensions.width = CLAY__MIN(CLAY__MAX(openLayoutElement->dimensions.width, layoutConfig->sizing.width.size.minMax.min), layoutConfig->sizing.width.size.minMax.max);\n        openLayoutElement->minDimensions.width = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.width, layoutConfig->sizing.width.size.minMax.min), layoutConfig->sizing.width.size.minMax.max);\n    } else {\n        openLayoutElement->dimensions.width = 0;\n    }\n\n    // Clamp element min and max height to the values configured in the layout\n    if (layoutConfig->sizing.height.type != CLAY__SIZING_TYPE_PERCENT) {\n        if (layoutConfig->sizing.height.size.minMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier\n            layoutConfig->sizing.height.size.minMax.max = CLAY__MAXFLOAT;\n        }\n        openLayoutElement->dimensions.height = CLAY__MIN(CLAY__MAX(openLayoutElement->dimensions.height, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max);\n        openLayoutElement->minDimensions.height = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.height, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max);\n    } else {\n        openLayoutElement->dimensions.height = 0;\n    }\n\n    Clay__UpdateAspectRatioBox(openLayoutElement);\n\n    bool elementIsFloating = Clay__ElementHasConfig(openLayoutElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING);\n\n    // Close the currently open element\n    int32_t closingElementIndex = Clay__int32_tArray_RemoveSwapback(&context->openLayoutElementStack, (int)context->openLayoutElementStack.length - 1);\n\n    // Get the currently open parent\n    openLayoutElement = Clay__GetOpenLayoutElement();\n\n    if (context->openLayoutElementStack.length > 1) {\n        if(elementIsFloating) {\n            openLayoutElement->floatingChildrenCount++;\n            return;\n        }\n        openLayoutElement->childrenOrTextContent.children.length++;\n        Clay__int32_tArray_Add(&context->layoutElementChildrenBuffer, closingElementIndex);\n    }\n}\n\nbool Clay__MemCmp(const char *s1, const char *s2, int32_t length);\n#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))\n    bool Clay__MemCmp(const char *s1, const char *s2, int32_t length) {\n        while (length >= 16) {\n            __m128i v1 = _mm_loadu_si128((const __m128i *)s1);\n            __m128i v2 = _mm_loadu_si128((const __m128i *)s2);\n\n            if (_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)) != 0xFFFF) { // If any byte differs\n                return false;\n            }\n\n            s1 += 16;\n            s2 += 16;\n            length -= 16;\n        }\n\n        // Handle remaining bytes\n        while (length--) {\n            if (*s1 != *s2) {\n                return false;\n            }\n            s1++;\n            s2++;\n        }\n\n        return true;\n    }\n#elif !defined(CLAY_DISABLE_SIMD) && defined(__aarch64__)\n    bool Clay__MemCmp(const char *s1, const char *s2, int32_t length) {\n        while (length >= 16) {\n            uint8x16_t v1 = vld1q_u8((const uint8_t *)s1);\n            uint8x16_t v2 = vld1q_u8((const uint8_t *)s2);\n\n            // Compare vectors\n            if (vminvq_u32(vreinterpretq_u32_u8(vceqq_u8(v1, v2))) != 0xFFFFFFFF) { // If there's a difference\n                return false;\n            }\n\n            s1 += 16;\n            s2 += 16;\n            length -= 16;\n        }\n\n        // Handle remaining bytes\n        while (length--) {\n            if (*s1 != *s2) {\n                return false;\n            }\n            s1++;\n            s2++;\n        }\n\n        return true;\n    }\n#else\n    bool Clay__MemCmp(const char *s1, const char *s2, int32_t length) {\n        for (int32_t i = 0; i < length; i++) {\n            if (s1[i] != s2[i]) {\n                return false;\n            }\n        }\n        return true;\n    }\n#endif\n\nvoid Clay__OpenElement(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->layoutElements.length == context->layoutElements.capacity - 1 || context->booleanWarnings.maxElementsExceeded) {\n        context->booleanWarnings.maxElementsExceeded = true;\n        return;\n    }\n    Clay_LayoutElement layoutElement = CLAY__DEFAULT_STRUCT;\n    Clay_LayoutElement* openLayoutElement = Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement);\n    Clay__int32_tArray_Add(&context->openLayoutElementStack, context->layoutElements.length - 1);\n    Clay__GenerateIdForAnonymousElement(openLayoutElement);\n    if (context->openClipElementStack.length > 0) {\n        Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1));\n    } else {\n        Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, 0);\n    }\n}\n\nvoid Clay__OpenElementWithId(Clay_ElementId elementId) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->layoutElements.length == context->layoutElements.capacity - 1 || context->booleanWarnings.maxElementsExceeded) {\n        context->booleanWarnings.maxElementsExceeded = true;\n        return;\n    }\n    Clay_LayoutElement layoutElement = CLAY__DEFAULT_STRUCT;\n    layoutElement.id = elementId.id;\n    Clay_LayoutElement * openLayoutElement = Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement);\n    Clay__int32_tArray_Add(&context->openLayoutElementStack, context->layoutElements.length - 1);\n    Clay__AddHashMapItem(elementId, openLayoutElement);\n    Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);\n    if (context->openClipElementStack.length > 0) {\n        Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1));\n    } else {\n        Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, 0);\n    }\n}\n\nvoid Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->layoutElements.length == context->layoutElements.capacity - 1 || context->booleanWarnings.maxElementsExceeded) {\n        context->booleanWarnings.maxElementsExceeded = true;\n        return;\n    }\n    Clay_LayoutElement *parentElement = Clay__GetOpenLayoutElement();\n\n    Clay_LayoutElement layoutElement = CLAY__DEFAULT_STRUCT;\n    Clay_LayoutElement *textElement = Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement);\n    if (context->openClipElementStack.length > 0) {\n        Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1));\n    } else {\n        Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, 0);\n    }\n\n    Clay__int32_tArray_Add(&context->layoutElementChildrenBuffer, context->layoutElements.length - 1);\n    Clay__MeasureTextCacheItem *textMeasured = Clay__MeasureTextCached(&text, textConfig);\n    Clay_ElementId elementId = Clay__HashNumber(parentElement->childrenOrTextContent.children.length + parentElement->floatingChildrenCount, parentElement->id);\n    textElement->id = elementId.id;\n    Clay__AddHashMapItem(elementId, textElement);\n    Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);\n    Clay_Dimensions textDimensions = { .width = textMeasured->unwrappedDimensions.width, .height = textConfig->lineHeight > 0 ? (float)textConfig->lineHeight : textMeasured->unwrappedDimensions.height };\n    textElement->dimensions = textDimensions;\n    textElement->minDimensions = CLAY__INIT(Clay_Dimensions) { .width = textMeasured->minWidth, .height = textDimensions.height };\n    textElement->childrenOrTextContent.textElementData = Clay__TextElementDataArray_Add(&context->textElementData, CLAY__INIT(Clay__TextElementData) { .text = text, .preferredDimensions = textMeasured->unwrappedDimensions, .elementIndex = context->layoutElements.length - 1 });\n    textElement->elementConfigs = CLAY__INIT(Clay__ElementConfigArraySlice) {\n            .length = 1,\n            .internalArray = Clay__ElementConfigArray_Add(&context->elementConfigs, CLAY__INIT(Clay_ElementConfig) { .type = CLAY__ELEMENT_CONFIG_TYPE_TEXT, .config = { .textElementConfig = textConfig }})\n    };\n    textElement->layoutConfig = &CLAY_LAYOUT_DEFAULT;\n    parentElement->childrenOrTextContent.children.length++;\n}\n\nvoid Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();\n    openLayoutElement->layoutConfig = Clay__StoreLayoutConfig(declaration->layout);\n    if ((declaration->layout.sizing.width.type == CLAY__SIZING_TYPE_PERCENT && declaration->layout.sizing.width.size.percent > 1) || (declaration->layout.sizing.height.type == CLAY__SIZING_TYPE_PERCENT && declaration->layout.sizing.height.size.percent > 1)) {\n        context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n                .errorType = CLAY_ERROR_TYPE_PERCENTAGE_OVER_1,\n                .errorText = CLAY_STRING(\"An element was configured with CLAY_SIZING_PERCENT, but the provided percentage value was over 1.0. Clay expects a value between 0 and 1, i.e. 20% is 0.2.\"),\n                .userData = context->errorHandler.userData });\n    }\n\n    openLayoutElement->elementConfigs.internalArray = &context->elementConfigs.internalArray[context->elementConfigs.length];\n    Clay_SharedElementConfig *sharedConfig = NULL;\n    if (declaration->backgroundColor.a > 0) {\n        sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .backgroundColor = declaration->backgroundColor });\n        Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);\n    }\n    if (!Clay__MemCmp((char *)(&declaration->cornerRadius), (char *)(&Clay__CornerRadius_DEFAULT), sizeof(Clay_CornerRadius))) {\n        if (sharedConfig) {\n            sharedConfig->cornerRadius = declaration->cornerRadius;\n        } else {\n            sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .cornerRadius = declaration->cornerRadius });\n            Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);\n        }\n    }\n    if (declaration->userData != 0) {\n        if (sharedConfig) {\n            sharedConfig->userData = declaration->userData;\n        } else {\n            sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .userData = declaration->userData });\n            Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);\n        }\n    }\n    if (declaration->image.imageData) {\n        Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = Clay__StoreImageElementConfig(declaration->image) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE);\n    }\n    if (declaration->aspectRatio.aspectRatio > 0) {\n        Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .aspectRatioElementConfig = Clay__StoreAspectRatioElementConfig(declaration->aspectRatio) }, CLAY__ELEMENT_CONFIG_TYPE_ASPECT);\n        Clay__int32_tArray_Add(&context->aspectRatioElementIndexes, context->layoutElements.length - 1);\n    }\n    if (declaration->floating.attachTo != CLAY_ATTACH_TO_NONE) {\n        Clay_FloatingElementConfig floatingConfig = declaration->floating;\n        // This looks dodgy but because of the auto generated root element the depth of the tree will always be at least 2 here\n        Clay_LayoutElement *hierarchicalParent = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2));\n        if (hierarchicalParent) {\n            uint32_t clipElementId = 0;\n            if (declaration->floating.attachTo == CLAY_ATTACH_TO_PARENT) {\n                // Attach to the element's direct hierarchical parent\n                floatingConfig.parentId = hierarchicalParent->id;\n                if (context->openClipElementStack.length > 0) {\n                    clipElementId = Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1);\n                }\n            } else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ELEMENT_WITH_ID) {\n                Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingConfig.parentId);\n                if (parentItem == &Clay_LayoutElementHashMapItem_DEFAULT) {\n                    context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n                            .errorType = CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND,\n                            .errorText = CLAY_STRING(\"A floating element was declared with a parentId, but no element with that ID was found.\"),\n                            .userData = context->errorHandler.userData });\n                } else {\n                    clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(parentItem->layoutElement - context->layoutElements.internalArray));\n                }\n            } else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ROOT) {\n                floatingConfig.parentId = Clay__HashString(CLAY_STRING(\"Clay__RootContainer\"), 0).id;\n            }\n            if (declaration->floating.clipTo == CLAY_CLIP_TO_NONE) {\n                clipElementId = 0;\n            }\n            int32_t currentElementIndex = Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1);\n            Clay__int32_tArray_Set(&context->layoutElementClipElementIds, currentElementIndex, clipElementId);\n            Clay__int32_tArray_Add(&context->openClipElementStack, clipElementId);\n            Clay__LayoutElementTreeRootArray_Add(&context->layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) {\n                    .layoutElementIndex = Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1),\n                    .parentId = floatingConfig.parentId,\n                    .clipElementId = clipElementId,\n                    .zIndex = floatingConfig.zIndex,\n            });\n            Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .floatingElementConfig = Clay__StoreFloatingElementConfig(floatingConfig) }, CLAY__ELEMENT_CONFIG_TYPE_FLOATING);\n        }\n    }\n    if (declaration->custom.customData) {\n        Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .customElementConfig = Clay__StoreCustomElementConfig(declaration->custom) }, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM);\n    }\n\n    if (declaration->clip.horizontal | declaration->clip.vertical) {\n        Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .clipElementConfig = Clay__StoreClipElementConfig(declaration->clip) }, CLAY__ELEMENT_CONFIG_TYPE_CLIP);\n        Clay__int32_tArray_Add(&context->openClipElementStack, (int)openLayoutElement->id);\n        // Retrieve or create cached data to track scroll position across frames\n        Clay__ScrollContainerDataInternal *scrollOffset = CLAY__NULL;\n        for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {\n            Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);\n            if (openLayoutElement->id == mapping->elementId) {\n                scrollOffset = mapping;\n                scrollOffset->layoutElement = openLayoutElement;\n                scrollOffset->openThisFrame = true;\n            }\n        }\n        if (!scrollOffset) {\n            scrollOffset = Clay__ScrollContainerDataInternalArray_Add(&context->scrollContainerDatas, CLAY__INIT(Clay__ScrollContainerDataInternal){.layoutElement = openLayoutElement, .scrollOrigin = {-1,-1}, .elementId = openLayoutElement->id, .openThisFrame = true});\n        }\n        if (context->externalScrollHandlingEnabled) {\n            scrollOffset->scrollPosition = Clay__QueryScrollOffset(scrollOffset->elementId, context->queryScrollOffsetUserData);\n        }\n    }\n    if (!Clay__MemCmp((char *)(&declaration->border.width), (char *)(&Clay__BorderWidth_DEFAULT), sizeof(Clay_BorderWidth))) {\n        Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .borderElementConfig = Clay__StoreBorderElementConfig(declaration->border) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER);\n    }\n}\n\nvoid Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {\n    Clay__ConfigureOpenElementPtr(&declaration);\n}\n\nvoid Clay__InitializeEphemeralMemory(Clay_Context* context) {\n    int32_t maxElementCount = context->maxElementCount;\n    // Ephemeral Memory - reset every frame\n    Clay_Arena *arena = &context->internalArena;\n    arena->nextAllocation = context->arenaResetOffset;\n\n    context->layoutElementChildrenBuffer = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->layoutElements = Clay_LayoutElementArray_Allocate_Arena(maxElementCount, arena);\n    context->warnings = Clay__WarningArray_Allocate_Arena(100, arena);\n\n    context->layoutConfigs = Clay__LayoutConfigArray_Allocate_Arena(maxElementCount, arena);\n    context->elementConfigs = Clay__ElementConfigArray_Allocate_Arena(maxElementCount, arena);\n    context->textElementConfigs = Clay__TextElementConfigArray_Allocate_Arena(maxElementCount, arena);\n    context->aspectRatioElementConfigs = Clay__AspectRatioElementConfigArray_Allocate_Arena(maxElementCount, arena);\n    context->imageElementConfigs = Clay__ImageElementConfigArray_Allocate_Arena(maxElementCount, arena);\n    context->floatingElementConfigs = Clay__FloatingElementConfigArray_Allocate_Arena(maxElementCount, arena);\n    context->clipElementConfigs = Clay__ClipElementConfigArray_Allocate_Arena(maxElementCount, arena);\n    context->customElementConfigs = Clay__CustomElementConfigArray_Allocate_Arena(maxElementCount, arena);\n    context->borderElementConfigs = Clay__BorderElementConfigArray_Allocate_Arena(maxElementCount, arena);\n    context->sharedElementConfigs = Clay__SharedElementConfigArray_Allocate_Arena(maxElementCount, arena);\n\n    context->layoutElementIdStrings = Clay__StringArray_Allocate_Arena(maxElementCount, arena);\n    context->wrappedTextLines = Clay__WrappedTextLineArray_Allocate_Arena(maxElementCount, arena);\n    context->layoutElementTreeNodeArray1 = Clay__LayoutElementTreeNodeArray_Allocate_Arena(maxElementCount, arena);\n    context->layoutElementTreeRoots = Clay__LayoutElementTreeRootArray_Allocate_Arena(maxElementCount, arena);\n    context->layoutElementChildren = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->openLayoutElementStack = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->textElementData = Clay__TextElementDataArray_Allocate_Arena(maxElementCount, arena);\n    context->aspectRatioElementIndexes = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->renderCommands = Clay_RenderCommandArray_Allocate_Arena(maxElementCount, arena);\n    context->treeNodeVisited = Clay__boolArray_Allocate_Arena(maxElementCount, arena);\n    context->treeNodeVisited.length = context->treeNodeVisited.capacity; // This array is accessed directly rather than behaving as a list\n    context->openClipElementStack = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->reusableElementIndexBuffer = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->layoutElementClipElementIds = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->dynamicStringData = Clay__charArray_Allocate_Arena(maxElementCount, arena);\n}\n\nvoid Clay__InitializePersistentMemory(Clay_Context* context) {\n    // Persistent memory - initialized once and not reset\n    int32_t maxElementCount = context->maxElementCount;\n    int32_t maxMeasureTextCacheWordCount = context->maxMeasureTextCacheWordCount;\n    Clay_Arena *arena = &context->internalArena;\n\n    context->scrollContainerDatas = Clay__ScrollContainerDataInternalArray_Allocate_Arena(100, arena);\n    context->layoutElementsHashMapInternal = Clay__LayoutElementHashMapItemArray_Allocate_Arena(maxElementCount, arena);\n    context->layoutElementsHashMap = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->measureTextHashMapInternal = Clay__MeasureTextCacheItemArray_Allocate_Arena(maxElementCount, arena);\n    context->measureTextHashMapInternalFreeList = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->measuredWordsFreeList = Clay__int32_tArray_Allocate_Arena(maxMeasureTextCacheWordCount, arena);\n    context->measureTextHashMap = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);\n    context->measuredWords = Clay__MeasuredWordArray_Allocate_Arena(maxMeasureTextCacheWordCount, arena);\n    context->pointerOverIds = Clay_ElementIdArray_Allocate_Arena(maxElementCount, arena);\n    context->debugElementData = Clay__DebugElementDataArray_Allocate_Arena(maxElementCount, arena);\n    context->arenaResetOffset = arena->nextAllocation;\n}\n\nconst float CLAY__EPSILON = 0.01;\n\nbool Clay__FloatEqual(float left, float right) {\n    float subtracted = left - right;\n    return subtracted < CLAY__EPSILON && subtracted > -CLAY__EPSILON;\n}\n\nvoid Clay__SizeContainersAlongAxis(bool xAxis) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    Clay__int32_tArray bfsBuffer = context->layoutElementChildrenBuffer;\n    Clay__int32_tArray resizableContainerBuffer = context->openLayoutElementStack;\n    for (int32_t rootIndex = 0; rootIndex < context->layoutElementTreeRoots.length; ++rootIndex) {\n        bfsBuffer.length = 0;\n        Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex);\n        Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)root->layoutElementIndex);\n        Clay__int32_tArray_Add(&bfsBuffer, (int32_t)root->layoutElementIndex);\n\n        // Size floating containers to their parents\n        if (Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING)) {\n            Clay_FloatingElementConfig *floatingElementConfig = Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING).floatingElementConfig;\n            Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingElementConfig->parentId);\n            if (parentItem && parentItem != &Clay_LayoutElementHashMapItem_DEFAULT) {\n                Clay_LayoutElement *parentLayoutElement = parentItem->layoutElement;\n                switch (rootElement->layoutConfig->sizing.width.type) {\n                    case CLAY__SIZING_TYPE_GROW: {\n                        rootElement->dimensions.width = parentLayoutElement->dimensions.width;\n                        break;\n                    }\n                    case CLAY__SIZING_TYPE_PERCENT: {\n                        rootElement->dimensions.width = parentLayoutElement->dimensions.width * rootElement->layoutConfig->sizing.width.size.percent;\n                        break;\n                    }\n                    default: break;\n                }\n                switch (rootElement->layoutConfig->sizing.height.type) {\n                    case CLAY__SIZING_TYPE_GROW: {\n                        rootElement->dimensions.height = parentLayoutElement->dimensions.height;\n                        break;\n                    }\n                    case CLAY__SIZING_TYPE_PERCENT: {\n                        rootElement->dimensions.height = parentLayoutElement->dimensions.height * rootElement->layoutConfig->sizing.height.size.percent;\n                        break;\n                    }\n                    default: break;\n                }\n            }\n        }\n\n        if (rootElement->layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) {\n            rootElement->dimensions.width = CLAY__MIN(CLAY__MAX(rootElement->dimensions.width, rootElement->layoutConfig->sizing.width.size.minMax.min), rootElement->layoutConfig->sizing.width.size.minMax.max);\n        }\n        if (rootElement->layoutConfig->sizing.height.type != CLAY__SIZING_TYPE_PERCENT) {\n            rootElement->dimensions.height = CLAY__MIN(CLAY__MAX(rootElement->dimensions.height, rootElement->layoutConfig->sizing.height.size.minMax.min), rootElement->layoutConfig->sizing.height.size.minMax.max);\n        }\n\n        for (int32_t i = 0; i < bfsBuffer.length; ++i) {\n            int32_t parentIndex = Clay__int32_tArray_GetValue(&bfsBuffer, i);\n            Clay_LayoutElement *parent = Clay_LayoutElementArray_Get(&context->layoutElements, parentIndex);\n            Clay_LayoutConfig *parentStyleConfig = parent->layoutConfig;\n            int32_t growContainerCount = 0;\n            float parentSize = xAxis ? parent->dimensions.width : parent->dimensions.height;\n            float parentPadding = (float)(xAxis ? (parent->layoutConfig->padding.left + parent->layoutConfig->padding.right) : (parent->layoutConfig->padding.top + parent->layoutConfig->padding.bottom));\n            float innerContentSize = 0, totalPaddingAndChildGaps = parentPadding;\n            bool sizingAlongAxis = (xAxis && parentStyleConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) || (!xAxis && parentStyleConfig->layoutDirection == CLAY_TOP_TO_BOTTOM);\n            resizableContainerBuffer.length = 0;\n            float parentChildGap = parentStyleConfig->childGap;\n\n            for (int32_t childOffset = 0; childOffset < parent->childrenOrTextContent.children.length; childOffset++) {\n                int32_t childElementIndex = parent->childrenOrTextContent.children.elements[childOffset];\n                Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, childElementIndex);\n                Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;\n                float childSize = xAxis ? childElement->dimensions.width : childElement->dimensions.height;\n\n                if (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) && childElement->childrenOrTextContent.children.length > 0) {\n                    Clay__int32_tArray_Add(&bfsBuffer, childElementIndex);\n                }\n\n                if (childSizing.type != CLAY__SIZING_TYPE_PERCENT\n                    && childSizing.type != CLAY__SIZING_TYPE_FIXED\n                    && (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || (Clay__FindElementConfigWithType(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig->wrapMode == CLAY_TEXT_WRAP_WORDS)) // todo too many loops\n//                    && (xAxis || !Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT))\n                ) {\n                    Clay__int32_tArray_Add(&resizableContainerBuffer, childElementIndex);\n                }\n\n                if (sizingAlongAxis) {\n                    innerContentSize += (childSizing.type == CLAY__SIZING_TYPE_PERCENT ? 0 : childSize);\n                    if (childSizing.type == CLAY__SIZING_TYPE_GROW) {\n                        growContainerCount++;\n                    }\n                    if (childOffset > 0) {\n                        innerContentSize += parentChildGap; // For children after index 0, the childAxisOffset is the gap from the previous child\n                        totalPaddingAndChildGaps += parentChildGap;\n                    }\n                } else {\n                    innerContentSize = CLAY__MAX(childSize, innerContentSize);\n                }\n            }\n\n            // Expand percentage containers to size\n            for (int32_t childOffset = 0; childOffset < parent->childrenOrTextContent.children.length; childOffset++) {\n                int32_t childElementIndex = parent->childrenOrTextContent.children.elements[childOffset];\n                Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, childElementIndex);\n                Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;\n                float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height;\n                if (childSizing.type == CLAY__SIZING_TYPE_PERCENT) {\n                    *childSize = (parentSize - totalPaddingAndChildGaps) * childSizing.size.percent;\n                    if (sizingAlongAxis) {\n                        innerContentSize += *childSize;\n                    }\n                    Clay__UpdateAspectRatioBox(childElement);\n                }\n            }\n\n            if (sizingAlongAxis) {\n                float sizeToDistribute = parentSize - parentPadding - innerContentSize;\n                // The content is too large, compress the children as much as possible\n                if (sizeToDistribute < 0) {\n                    // If the parent clips content in this axis direction, don't compress children, just leave them alone\n                    Clay_ClipElementConfig *clipElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;\n                    if (clipElementConfig) {\n                        if (((xAxis && clipElementConfig->horizontal) || (!xAxis && clipElementConfig->vertical))) {\n                            continue;\n                        }\n                    }\n                    // Scrolling containers preferentially compress before others\n                    while (sizeToDistribute < -CLAY__EPSILON && resizableContainerBuffer.length > 0) {\n                        float largest = 0;\n                        float secondLargest = 0;\n                        float widthToAdd = sizeToDistribute;\n                        for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {\n                            Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));\n                            float childSize = xAxis ? child->dimensions.width : child->dimensions.height;\n                            if (Clay__FloatEqual(childSize, largest)) { continue; }\n                            if (childSize > largest) {\n                                secondLargest = largest;\n                                largest = childSize;\n                            }\n                            if (childSize < largest) {\n                                secondLargest = CLAY__MAX(secondLargest, childSize);\n                                widthToAdd = secondLargest - largest;\n                            }\n                        }\n\n                        widthToAdd = CLAY__MAX(widthToAdd, sizeToDistribute / resizableContainerBuffer.length);\n\n                        for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {\n                            Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));\n                            float *childSize = xAxis ? &child->dimensions.width : &child->dimensions.height;\n                            float minSize = xAxis ? child->minDimensions.width : child->minDimensions.height;\n                            float previousWidth = *childSize;\n                            if (Clay__FloatEqual(*childSize, largest)) {\n                                *childSize += widthToAdd;\n                                if (*childSize <= minSize) {\n                                    *childSize = minSize;\n                                    Clay__int32_tArray_RemoveSwapback(&resizableContainerBuffer, childIndex--);\n                                }\n                                sizeToDistribute -= (*childSize - previousWidth);\n                            }\n                        }\n                    }\n                // The content is too small, allow SIZING_GROW containers to expand\n                } else if (sizeToDistribute > 0 && growContainerCount > 0) {\n                    for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {\n                        Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));\n                        Clay__SizingType childSizing = xAxis ? child->layoutConfig->sizing.width.type : child->layoutConfig->sizing.height.type;\n                        if (childSizing != CLAY__SIZING_TYPE_GROW) {\n                            Clay__int32_tArray_RemoveSwapback(&resizableContainerBuffer, childIndex--);\n                        }\n                    }\n                    while (sizeToDistribute > CLAY__EPSILON && resizableContainerBuffer.length > 0) {\n                        float smallest = CLAY__MAXFLOAT;\n                        float secondSmallest = CLAY__MAXFLOAT;\n                        float widthToAdd = sizeToDistribute;\n                        for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {\n                            Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));\n                            float childSize = xAxis ? child->dimensions.width : child->dimensions.height;\n                            if (Clay__FloatEqual(childSize, smallest)) { continue; }\n                            if (childSize < smallest) {\n                                secondSmallest = smallest;\n                                smallest = childSize;\n                            }\n                            if (childSize > smallest) {\n                                secondSmallest = CLAY__MIN(secondSmallest, childSize);\n                                widthToAdd = secondSmallest - smallest;\n                            }\n                        }\n\n                        widthToAdd = CLAY__MIN(widthToAdd, sizeToDistribute / resizableContainerBuffer.length);\n\n                        for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {\n                            Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));\n                            float *childSize = xAxis ? &child->dimensions.width : &child->dimensions.height;\n                            float maxSize = xAxis ? child->layoutConfig->sizing.width.size.minMax.max : child->layoutConfig->sizing.height.size.minMax.max;\n                            float previousWidth = *childSize;\n                            if (Clay__FloatEqual(*childSize, smallest)) {\n                                *childSize += widthToAdd;\n                                if (*childSize >= maxSize) {\n                                    *childSize = maxSize;\n                                    Clay__int32_tArray_RemoveSwapback(&resizableContainerBuffer, childIndex--);\n                                }\n                                sizeToDistribute -= (*childSize - previousWidth);\n                            }\n                        }\n                    }\n                }\n            // Sizing along the non layout axis (\"off axis\")\n            } else {\n                for (int32_t childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) {\n                    Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childOffset));\n                    Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;\n                    float minSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height;\n                    float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height;\n\n                    float maxSize = parentSize - parentPadding;\n                    // If we're laying out the children of a scroll panel, grow containers expand to the size of the inner content, not the outer container\n                    if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP)) {\n                        Clay_ClipElementConfig *clipElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;\n                        if (((xAxis && clipElementConfig->horizontal) || (!xAxis && clipElementConfig->vertical))) {\n                            maxSize = CLAY__MAX(maxSize, innerContentSize);\n                        }\n                    }\n                    if (childSizing.type == CLAY__SIZING_TYPE_GROW) {\n                        *childSize = CLAY__MIN(maxSize, childSizing.size.minMax.max);\n                    }\n                    *childSize = CLAY__MAX(minSize, CLAY__MIN(*childSize, maxSize));\n                }\n            }\n        }\n    }\n}\n\nClay_String Clay__IntToString(int32_t integer) {\n    if (integer == 0) {\n        return CLAY__INIT(Clay_String) { .length = 1, .chars = \"0\" };\n    }\n    Clay_Context* context = Clay_GetCurrentContext();\n    char *chars = (char *)(context->dynamicStringData.internalArray + context->dynamicStringData.length);\n    int32_t length = 0;\n    int32_t sign = integer;\n\n    if (integer < 0) {\n        integer = -integer;\n    }\n    while (integer > 0) {\n        chars[length++] = (char)(integer % 10 + '0');\n        integer /= 10;\n    }\n\n    if (sign < 0) {\n        chars[length++] = '-';\n    }\n\n    // Reverse the string to get the correct order\n    for (int32_t j = 0, k = length - 1; j < k; j++, k--) {\n        char temp = chars[j];\n        chars[j] = chars[k];\n        chars[k] = temp;\n    }\n    context->dynamicStringData.length += length;\n    return CLAY__INIT(Clay_String) { .length = length, .chars = chars };\n}\n\nvoid Clay__AddRenderCommand(Clay_RenderCommand renderCommand) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->renderCommands.length < context->renderCommands.capacity - 1) {\n        Clay_RenderCommandArray_Add(&context->renderCommands, renderCommand);\n    } else {\n        if (!context->booleanWarnings.maxRenderCommandsExceeded) {\n            context->booleanWarnings.maxRenderCommandsExceeded = true;\n            context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n                .errorType = CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,\n                .errorText = CLAY_STRING(\"Clay ran out of capacity while attempting to create render commands. This is usually caused by a large amount of wrapping text elements while close to the max element capacity. Try using Clay_SetMaxElementCount() with a higher value.\"),\n                .userData = context->errorHandler.userData });\n        }\n    }\n}\n\nbool Clay__ElementIsOffscreen(Clay_BoundingBox *boundingBox) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->disableCulling) {\n        return false;\n    }\n\n    return (boundingBox->x > (float)context->layoutDimensions.width) ||\n           (boundingBox->y > (float)context->layoutDimensions.height) ||\n           (boundingBox->x + boundingBox->width < 0) ||\n           (boundingBox->y + boundingBox->height < 0);\n}\n\nvoid Clay__CalculateFinalLayout(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    // Calculate sizing along the X axis\n    Clay__SizeContainersAlongAxis(true);\n\n    // Wrap text\n    for (int32_t textElementIndex = 0; textElementIndex < context->textElementData.length; ++textElementIndex) {\n        Clay__TextElementData *textElementData = Clay__TextElementDataArray_Get(&context->textElementData, textElementIndex);\n        textElementData->wrappedLines = CLAY__INIT(Clay__WrappedTextLineArraySlice) { .length = 0, .internalArray = &context->wrappedTextLines.internalArray[context->wrappedTextLines.length] };\n        Clay_LayoutElement *containerElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)textElementData->elementIndex);\n        Clay_TextElementConfig *textConfig = Clay__FindElementConfigWithType(containerElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig;\n        Clay__MeasureTextCacheItem *measureTextCacheItem = Clay__MeasureTextCached(&textElementData->text, textConfig);\n        float lineWidth = 0;\n        float lineHeight = textConfig->lineHeight > 0 ? (float)textConfig->lineHeight : textElementData->preferredDimensions.height;\n        int32_t lineLengthChars = 0;\n        int32_t lineStartOffset = 0;\n        if (!measureTextCacheItem->containsNewlines && textElementData->preferredDimensions.width <= containerElement->dimensions.width) {\n            Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { containerElement->dimensions,  textElementData->text });\n            textElementData->wrappedLines.length++;\n            continue;\n        }\n        float spaceWidth = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) { .length = 1, .chars = CLAY__SPACECHAR.chars, .baseChars = CLAY__SPACECHAR.chars }, textConfig, context->measureTextUserData).width;\n        int32_t wordIndex = measureTextCacheItem->measuredWordsStartIndex;\n        while (wordIndex != -1) {\n            if (context->wrappedTextLines.length > context->wrappedTextLines.capacity - 1) {\n                break;\n            }\n            Clay__MeasuredWord *measuredWord = Clay__MeasuredWordArray_Get(&context->measuredWords, wordIndex);\n            // Only word on the line is too large, just render it anyway\n            if (lineLengthChars == 0 && lineWidth + measuredWord->width > containerElement->dimensions.width) {\n                Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { measuredWord->width, lineHeight }, { .length = measuredWord->length, .chars = &textElementData->text.chars[measuredWord->startOffset] } });\n                textElementData->wrappedLines.length++;\n                wordIndex = measuredWord->next;\n                lineStartOffset = measuredWord->startOffset + measuredWord->length;\n            }\n            // measuredWord->length == 0 means a newline character\n            else if (measuredWord->length == 0 || lineWidth + measuredWord->width > containerElement->dimensions.width) {\n                // Wrapped text lines list has overflowed, just render out the line\n                bool finalCharIsSpace = textElementData->text.chars[CLAY__MAX(lineStartOffset + lineLengthChars - 1, 0)] == ' ';\n                Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth + (finalCharIsSpace ? -spaceWidth : 0), lineHeight }, { .length = lineLengthChars + (finalCharIsSpace ? -1 : 0), .chars = &textElementData->text.chars[lineStartOffset] } });\n                textElementData->wrappedLines.length++;\n                if (lineLengthChars == 0 || measuredWord->length == 0) {\n                    wordIndex = measuredWord->next;\n                }\n                lineWidth = 0;\n                lineLengthChars = 0;\n                lineStartOffset = measuredWord->startOffset;\n            } else {\n                lineWidth += measuredWord->width + textConfig->letterSpacing;\n                lineLengthChars += measuredWord->length;\n                wordIndex = measuredWord->next;\n            }\n        }\n        if (lineLengthChars > 0) {\n            Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth - textConfig->letterSpacing, lineHeight }, {.length = lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] } });\n            textElementData->wrappedLines.length++;\n        }\n        containerElement->dimensions.height = lineHeight * (float)textElementData->wrappedLines.length;\n    }\n\n    // Scale vertical heights according to aspect ratio\n    for (int32_t i = 0; i < context->aspectRatioElementIndexes.length; ++i) {\n        Clay_LayoutElement* aspectElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->aspectRatioElementIndexes, i));\n        Clay_AspectRatioElementConfig *config = Clay__FindElementConfigWithType(aspectElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;\n        aspectElement->dimensions.height = (1 / config->aspectRatio) * aspectElement->dimensions.width;\n        aspectElement->layoutConfig->sizing.height.size.minMax.max = aspectElement->dimensions.height;\n    }\n\n    // Propagate effect of text wrapping, aspect scaling etc. on height of parents\n    Clay__LayoutElementTreeNodeArray dfsBuffer = context->layoutElementTreeNodeArray1;\n    dfsBuffer.length = 0;\n    for (int32_t i = 0; i < context->layoutElementTreeRoots.length; ++i) {\n        Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, i);\n        context->treeNodeVisited.internalArray[dfsBuffer.length] = false;\n        Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)root->layoutElementIndex) });\n    }\n    while (dfsBuffer.length > 0) {\n        Clay__LayoutElementTreeNode *currentElementTreeNode = Clay__LayoutElementTreeNodeArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1);\n        Clay_LayoutElement *currentElement = currentElementTreeNode->layoutElement;\n        if (!context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) {\n            context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;\n            // If the element has no children or is the container for a text element, don't bother inspecting it\n            if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || currentElement->childrenOrTextContent.children.length == 0) {\n                dfsBuffer.length--;\n                continue;\n            }\n            // Add the children to the DFS buffer (needs to be pushed in reverse so that stack traversal is in correct layout order)\n            for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; i++) {\n                context->treeNodeVisited.internalArray[dfsBuffer.length] = false;\n                Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]) });\n            }\n            continue;\n        }\n        dfsBuffer.length--;\n\n        // DFS node has been visited, this is on the way back up to the root\n        Clay_LayoutConfig *layoutConfig = currentElement->layoutConfig;\n        if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {\n            // Resize any parent containers that have grown in height along their non layout axis\n            for (int32_t j = 0; j < currentElement->childrenOrTextContent.children.length; ++j) {\n                Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[j]);\n                float childHeightWithPadding = CLAY__MAX(childElement->dimensions.height + layoutConfig->padding.top + layoutConfig->padding.bottom, currentElement->dimensions.height);\n                currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(childHeightWithPadding, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max);\n            }\n        } else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {\n            // Resizing along the layout axis\n            float contentHeight = (float)(layoutConfig->padding.top + layoutConfig->padding.bottom);\n            for (int32_t j = 0; j < currentElement->childrenOrTextContent.children.length; ++j) {\n                Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[j]);\n                contentHeight += childElement->dimensions.height;\n            }\n            contentHeight += (float)(CLAY__MAX(currentElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);\n            currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(contentHeight, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max);\n        }\n    }\n\n    // Calculate sizing along the Y axis\n    Clay__SizeContainersAlongAxis(false);\n\n    // Scale horizontal widths according to aspect ratio\n    for (int32_t i = 0; i < context->aspectRatioElementIndexes.length; ++i) {\n        Clay_LayoutElement* aspectElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->aspectRatioElementIndexes, i));\n        Clay_AspectRatioElementConfig *config = Clay__FindElementConfigWithType(aspectElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;\n        aspectElement->dimensions.width = config->aspectRatio * aspectElement->dimensions.height;\n    }\n\n    // Sort tree roots by z-index\n    int32_t sortMax = context->layoutElementTreeRoots.length - 1;\n    while (sortMax > 0) { // todo dumb bubble sort\n        for (int32_t i = 0; i < sortMax; ++i) {\n            Clay__LayoutElementTreeRoot current = *Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, i);\n            Clay__LayoutElementTreeRoot next = *Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, i + 1);\n            if (next.zIndex < current.zIndex) {\n                Clay__LayoutElementTreeRootArray_Set(&context->layoutElementTreeRoots, i, next);\n                Clay__LayoutElementTreeRootArray_Set(&context->layoutElementTreeRoots, i + 1, current);\n            }\n        }\n        sortMax--;\n    }\n\n    // Calculate final positions and generate render commands\n    context->renderCommands.length = 0;\n    dfsBuffer.length = 0;\n    for (int32_t rootIndex = 0; rootIndex < context->layoutElementTreeRoots.length; ++rootIndex) {\n        dfsBuffer.length = 0;\n        Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex);\n        Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)root->layoutElementIndex);\n        Clay_Vector2 rootPosition = CLAY__DEFAULT_STRUCT;\n        Clay_LayoutElementHashMapItem *parentHashMapItem = Clay__GetHashMapItem(root->parentId);\n        // Position root floating containers\n        if (Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING) && parentHashMapItem) {\n            Clay_FloatingElementConfig *config = Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING).floatingElementConfig;\n            Clay_Dimensions rootDimensions = rootElement->dimensions;\n            Clay_BoundingBox parentBoundingBox = parentHashMapItem->boundingBox;\n            // Set X position\n            Clay_Vector2 targetAttachPosition = CLAY__DEFAULT_STRUCT;\n            switch (config->attachPoints.parent) {\n                case CLAY_ATTACH_POINT_LEFT_TOP:\n                case CLAY_ATTACH_POINT_LEFT_CENTER:\n                case CLAY_ATTACH_POINT_LEFT_BOTTOM: targetAttachPosition.x = parentBoundingBox.x; break;\n                case CLAY_ATTACH_POINT_CENTER_TOP:\n                case CLAY_ATTACH_POINT_CENTER_CENTER:\n                case CLAY_ATTACH_POINT_CENTER_BOTTOM: targetAttachPosition.x = parentBoundingBox.x + (parentBoundingBox.width / 2); break;\n                case CLAY_ATTACH_POINT_RIGHT_TOP:\n                case CLAY_ATTACH_POINT_RIGHT_CENTER:\n                case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.x = parentBoundingBox.x + parentBoundingBox.width; break;\n            }\n            switch (config->attachPoints.element) {\n                case CLAY_ATTACH_POINT_LEFT_TOP:\n                case CLAY_ATTACH_POINT_LEFT_CENTER:\n                case CLAY_ATTACH_POINT_LEFT_BOTTOM: break;\n                case CLAY_ATTACH_POINT_CENTER_TOP:\n                case CLAY_ATTACH_POINT_CENTER_CENTER:\n                case CLAY_ATTACH_POINT_CENTER_BOTTOM: targetAttachPosition.x -= (rootDimensions.width / 2); break;\n                case CLAY_ATTACH_POINT_RIGHT_TOP:\n                case CLAY_ATTACH_POINT_RIGHT_CENTER:\n                case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.x -= rootDimensions.width; break;\n            }\n            switch (config->attachPoints.parent) { // I know I could merge the x and y switch statements, but this is easier to read\n                case CLAY_ATTACH_POINT_LEFT_TOP:\n                case CLAY_ATTACH_POINT_RIGHT_TOP:\n                case CLAY_ATTACH_POINT_CENTER_TOP: targetAttachPosition.y = parentBoundingBox.y; break;\n                case CLAY_ATTACH_POINT_LEFT_CENTER:\n                case CLAY_ATTACH_POINT_CENTER_CENTER:\n                case CLAY_ATTACH_POINT_RIGHT_CENTER: targetAttachPosition.y = parentBoundingBox.y + (parentBoundingBox.height / 2); break;\n                case CLAY_ATTACH_POINT_LEFT_BOTTOM:\n                case CLAY_ATTACH_POINT_CENTER_BOTTOM:\n                case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.y = parentBoundingBox.y + parentBoundingBox.height; break;\n            }\n            switch (config->attachPoints.element) {\n                case CLAY_ATTACH_POINT_LEFT_TOP:\n                case CLAY_ATTACH_POINT_RIGHT_TOP:\n                case CLAY_ATTACH_POINT_CENTER_TOP: break;\n                case CLAY_ATTACH_POINT_LEFT_CENTER:\n                case CLAY_ATTACH_POINT_CENTER_CENTER:\n                case CLAY_ATTACH_POINT_RIGHT_CENTER: targetAttachPosition.y -= (rootDimensions.height / 2); break;\n                case CLAY_ATTACH_POINT_LEFT_BOTTOM:\n                case CLAY_ATTACH_POINT_CENTER_BOTTOM:\n                case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.y -= rootDimensions.height; break;\n            }\n            targetAttachPosition.x += config->offset.x;\n            targetAttachPosition.y += config->offset.y;\n            rootPosition = targetAttachPosition;\n        }\n        if (root->clipElementId) {\n            Clay_LayoutElementHashMapItem *clipHashMapItem = Clay__GetHashMapItem(root->clipElementId);\n            if (clipHashMapItem) {\n                // Floating elements that are attached to scrolling contents won't be correctly positioned if external scroll handling is enabled, fix here\n                if (context->externalScrollHandlingEnabled) {\n                    Clay_ClipElementConfig *clipConfig = Clay__FindElementConfigWithType(clipHashMapItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;\n                    if (clipConfig->horizontal) {\n                        rootPosition.x += clipConfig->childOffset.x;\n                    }\n                    if (clipConfig->vertical) {\n                        rootPosition.y += clipConfig->childOffset.y;\n                    }\n                }\n                Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {\n                    .boundingBox = clipHashMapItem->boundingBox,\n                    .userData = 0,\n                    .id = Clay__HashNumber(rootElement->id, rootElement->childrenOrTextContent.children.length + 10).id, // TODO need a better strategy for managing derived ids\n                    .zIndex = root->zIndex,\n                    .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START,\n                });\n            }\n        }\n        Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = rootElement, .position = rootPosition, .nextChildOffset = { .x = (float)rootElement->layoutConfig->padding.left, .y = (float)rootElement->layoutConfig->padding.top } });\n\n        context->treeNodeVisited.internalArray[0] = false;\n        while (dfsBuffer.length > 0) {\n            Clay__LayoutElementTreeNode *currentElementTreeNode = Clay__LayoutElementTreeNodeArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1);\n            Clay_LayoutElement *currentElement = currentElementTreeNode->layoutElement;\n            Clay_LayoutConfig *layoutConfig = currentElement->layoutConfig;\n            Clay_Vector2 scrollOffset = CLAY__DEFAULT_STRUCT;\n\n            // This will only be run a single time for each element in downwards DFS order\n            if (!context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) {\n                context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;\n\n                Clay_BoundingBox currentElementBoundingBox = { currentElementTreeNode->position.x, currentElementTreeNode->position.y, currentElement->dimensions.width, currentElement->dimensions.height };\n                if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING)) {\n                    Clay_FloatingElementConfig *floatingElementConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING).floatingElementConfig;\n                    Clay_Dimensions expand = floatingElementConfig->expand;\n                    currentElementBoundingBox.x -= expand.width;\n                    currentElementBoundingBox.width += expand.width * 2;\n                    currentElementBoundingBox.y -= expand.height;\n                    currentElementBoundingBox.height += expand.height * 2;\n                }\n\n                Clay__ScrollContainerDataInternal *scrollContainerData = CLAY__NULL;\n                // Apply scroll offsets to container\n                if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP)) {\n                    Clay_ClipElementConfig *clipConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;\n\n                    // This linear scan could theoretically be slow under very strange conditions, but I can't imagine a real UI with more than a few 10's of scroll containers\n                    for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {\n                        Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);\n                        if (mapping->layoutElement == currentElement) {\n                            scrollContainerData = mapping;\n                            mapping->boundingBox = currentElementBoundingBox;\n                            scrollOffset = clipConfig->childOffset;\n                            if (context->externalScrollHandlingEnabled) {\n                                scrollOffset = CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT;\n                            }\n                            break;\n                        }\n                    }\n                }\n\n                Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(currentElement->id);\n                if (hashMapItem) {\n                    hashMapItem->boundingBox = currentElementBoundingBox;\n                }\n\n                int32_t sortedConfigIndexes[20];\n                for (int32_t elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) {\n                    sortedConfigIndexes[elementConfigIndex] = elementConfigIndex;\n                }\n                sortMax = currentElement->elementConfigs.length - 1;\n                while (sortMax > 0) { // todo dumb bubble sort\n                    for (int32_t i = 0; i < sortMax; ++i) {\n                        int32_t current = sortedConfigIndexes[i];\n                        int32_t next = sortedConfigIndexes[i + 1];\n                        Clay__ElementConfigType currentType = Clay__ElementConfigArraySlice_Get(&currentElement->elementConfigs, current)->type;\n                        Clay__ElementConfigType nextType = Clay__ElementConfigArraySlice_Get(&currentElement->elementConfigs, next)->type;\n                        if (nextType == CLAY__ELEMENT_CONFIG_TYPE_CLIP || currentType == CLAY__ELEMENT_CONFIG_TYPE_BORDER) {\n                            sortedConfigIndexes[i] = next;\n                            sortedConfigIndexes[i + 1] = current;\n                        }\n                    }\n                    sortMax--;\n                }\n\n                bool emitRectangle = false;\n                // Create the render commands for this element\n                Clay_SharedElementConfig *sharedConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SHARED).sharedElementConfig;\n                if (sharedConfig && sharedConfig->backgroundColor.a > 0) {\n                   emitRectangle = true;\n                }\n                else if (!sharedConfig) {\n                    emitRectangle = false;\n                    sharedConfig = &Clay_SharedElementConfig_DEFAULT;\n                }\n                for (int32_t elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) {\n                    Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(&currentElement->elementConfigs, sortedConfigIndexes[elementConfigIndex]);\n                    Clay_RenderCommand renderCommand = {\n                        .boundingBox = currentElementBoundingBox,\n                        .userData = sharedConfig->userData,\n                        .id = currentElement->id,\n                    };\n\n                    bool offscreen = Clay__ElementIsOffscreen(&currentElementBoundingBox);\n                    // Culling - Don't bother to generate render commands for rectangles entirely outside the screen - this won't stop their children from being rendered if they overflow\n                    bool shouldRender = !offscreen;\n                    switch (elementConfig->type) {\n                        case CLAY__ELEMENT_CONFIG_TYPE_ASPECT:\n                        case CLAY__ELEMENT_CONFIG_TYPE_FLOATING:\n                        case CLAY__ELEMENT_CONFIG_TYPE_SHARED:\n                        case CLAY__ELEMENT_CONFIG_TYPE_BORDER: {\n                            shouldRender = false;\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_CLIP: {\n                            renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START;\n                            renderCommand.renderData = CLAY__INIT(Clay_RenderData) {\n                                .clip = {\n                                    .horizontal = elementConfig->config.clipElementConfig->horizontal,\n                                    .vertical = elementConfig->config.clipElementConfig->vertical,\n                                }\n                            };\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: {\n                            renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_IMAGE;\n                            renderCommand.renderData = CLAY__INIT(Clay_RenderData) {\n                                .image = {\n                                    .backgroundColor = sharedConfig->backgroundColor,\n                                    .cornerRadius = sharedConfig->cornerRadius,\n                                    .imageData = elementConfig->config.imageElementConfig->imageData,\n                               }\n                            };\n                            emitRectangle = false;\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_TEXT: {\n                            if (!shouldRender) {\n                                break;\n                            }\n                            shouldRender = false;\n                            Clay_ElementConfigUnion configUnion = elementConfig->config;\n                            Clay_TextElementConfig *textElementConfig = configUnion.textElementConfig;\n                            float naturalLineHeight = currentElement->childrenOrTextContent.textElementData->preferredDimensions.height;\n                            float finalLineHeight = textElementConfig->lineHeight > 0 ? (float)textElementConfig->lineHeight : naturalLineHeight;\n                            float lineHeightOffset = (finalLineHeight - naturalLineHeight) / 2;\n                            float yPosition = lineHeightOffset;\n                            for (int32_t lineIndex = 0; lineIndex < currentElement->childrenOrTextContent.textElementData->wrappedLines.length; ++lineIndex) {\n                                Clay__WrappedTextLine *wrappedLine = Clay__WrappedTextLineArraySlice_Get(&currentElement->childrenOrTextContent.textElementData->wrappedLines, lineIndex);\n                                if (wrappedLine->line.length == 0) {\n                                    yPosition += finalLineHeight;\n                                    continue;\n                                }\n                                float offset = (currentElementBoundingBox.width - wrappedLine->dimensions.width);\n                                if (textElementConfig->textAlignment == CLAY_TEXT_ALIGN_LEFT) {\n                                    offset = 0;\n                                }\n                                if (textElementConfig->textAlignment == CLAY_TEXT_ALIGN_CENTER) {\n                                    offset /= 2;\n                                }\n                                Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {\n                                    .boundingBox = { currentElementBoundingBox.x + offset, currentElementBoundingBox.y + yPosition, wrappedLine->dimensions.width, wrappedLine->dimensions.height },\n                                    .renderData = { .text = {\n                                        .stringContents = CLAY__INIT(Clay_StringSlice) { .length = wrappedLine->line.length, .chars = wrappedLine->line.chars, .baseChars = currentElement->childrenOrTextContent.textElementData->text.chars },\n                                        .textColor = textElementConfig->textColor,\n                                        .fontId = textElementConfig->fontId,\n                                        .fontSize = textElementConfig->fontSize,\n                                        .letterSpacing = textElementConfig->letterSpacing,\n                                        .lineHeight = textElementConfig->lineHeight,\n                                    }},\n                                    .userData = textElementConfig->userData,\n                                    .id = Clay__HashNumber(lineIndex, currentElement->id).id,\n                                    .zIndex = root->zIndex,\n                                    .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT,\n                                });\n                                yPosition += finalLineHeight;\n\n                                if (!context->disableCulling && (currentElementBoundingBox.y + yPosition > context->layoutDimensions.height)) {\n                                    break;\n                                }\n                            }\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: {\n                            renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM;\n                            renderCommand.renderData = CLAY__INIT(Clay_RenderData) {\n                                .custom = {\n                                    .backgroundColor = sharedConfig->backgroundColor,\n                                    .cornerRadius = sharedConfig->cornerRadius,\n                                    .customData = elementConfig->config.customElementConfig->customData,\n                                }\n                            };\n                            emitRectangle = false;\n                            break;\n                        }\n                        default: break;\n                    }\n                    if (shouldRender) {\n                        Clay__AddRenderCommand(renderCommand);\n                    }\n                    if (offscreen) {\n                        // NOTE: You may be tempted to try an early return / continue if an element is off screen. Why bother calculating layout for its children, right?\n                        // Unfortunately, a FLOATING_CONTAINER may be defined that attaches to a child or grandchild of this element, which is large enough to still\n                        // be on screen, even if this element isn't. That depends on this element and it's children being laid out correctly (even if they are entirely off screen)\n                    }\n                }\n\n                if (emitRectangle) {\n                    Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {\n                        .boundingBox = currentElementBoundingBox,\n                        .renderData = { .rectangle = {\n                                .backgroundColor = sharedConfig->backgroundColor,\n                                .cornerRadius = sharedConfig->cornerRadius,\n                        }},\n                        .userData = sharedConfig->userData,\n                        .id = currentElement->id,\n                        .zIndex = root->zIndex,\n                        .commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE,\n                    });\n                }\n\n                // Setup initial on-axis alignment\n                if (!Clay__ElementHasConfig(currentElementTreeNode->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) {\n                    Clay_Dimensions contentSize = {0,0};\n                    if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {\n                        for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {\n                            Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);\n                            contentSize.width += childElement->dimensions.width;\n                            contentSize.height = CLAY__MAX(contentSize.height, childElement->dimensions.height);\n                        }\n                        contentSize.width += (float)(CLAY__MAX(currentElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);\n                        float extraSpace = currentElement->dimensions.width - (float)(layoutConfig->padding.left + layoutConfig->padding.right) - contentSize.width;\n                        switch (layoutConfig->childAlignment.x) {\n                            case CLAY_ALIGN_X_LEFT: extraSpace = 0; break;\n                            case CLAY_ALIGN_X_CENTER: extraSpace /= 2; break;\n                            default: break;\n                        }\n                        currentElementTreeNode->nextChildOffset.x += extraSpace;\n                        extraSpace = CLAY__MAX(0, extraSpace);\n                    } else {\n                        for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {\n                            Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);\n                            contentSize.width = CLAY__MAX(contentSize.width, childElement->dimensions.width);\n                            contentSize.height += childElement->dimensions.height;\n                        }\n                        contentSize.height += (float)(CLAY__MAX(currentElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);\n                        float extraSpace = currentElement->dimensions.height - (float)(layoutConfig->padding.top + layoutConfig->padding.bottom) - contentSize.height;\n                        switch (layoutConfig->childAlignment.y) {\n                            case CLAY_ALIGN_Y_TOP: extraSpace = 0; break;\n                            case CLAY_ALIGN_Y_CENTER: extraSpace /= 2; break;\n                            default: break;\n                        }\n                        extraSpace = CLAY__MAX(0, extraSpace);\n                        currentElementTreeNode->nextChildOffset.y += extraSpace;\n                    }\n\n                    if (scrollContainerData) {\n                        scrollContainerData->contentSize = CLAY__INIT(Clay_Dimensions) { contentSize.width + (float)(layoutConfig->padding.left + layoutConfig->padding.right), contentSize.height + (float)(layoutConfig->padding.top + layoutConfig->padding.bottom) };\n                    }\n                }\n            }\n            else {\n                // DFS is returning upwards backwards\n                bool closeClipElement = false;\n                Clay_ClipElementConfig *clipConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;\n                if (clipConfig) {\n                    closeClipElement = true;\n                    for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {\n                        Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);\n                        if (mapping->layoutElement == currentElement) {\n                            scrollOffset = clipConfig->childOffset;\n                            if (context->externalScrollHandlingEnabled) {\n                                scrollOffset = CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT;\n                            }\n                            break;\n                        }\n                    }\n                }\n\n                if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_BORDER)) {\n                    Clay_LayoutElementHashMapItem *currentElementData = Clay__GetHashMapItem(currentElement->id);\n                    Clay_BoundingBox currentElementBoundingBox = currentElementData->boundingBox;\n\n                    // Culling - Don't bother to generate render commands for rectangles entirely outside the screen - this won't stop their children from being rendered if they overflow\n                    if (!Clay__ElementIsOffscreen(&currentElementBoundingBox)) {\n                        Clay_SharedElementConfig *sharedConfig = Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SHARED) ? Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SHARED).sharedElementConfig : &Clay_SharedElementConfig_DEFAULT;\n                        Clay_BorderElementConfig *borderConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_BORDER).borderElementConfig;\n                        Clay_RenderCommand renderCommand = {\n                                .boundingBox = currentElementBoundingBox,\n                                .renderData = { .border = {\n                                    .color = borderConfig->color,\n                                    .cornerRadius = sharedConfig->cornerRadius,\n                                    .width = borderConfig->width\n                                }},\n                                .userData = sharedConfig->userData,\n                                .id = Clay__HashNumber(currentElement->id, currentElement->childrenOrTextContent.children.length).id,\n                                .commandType = CLAY_RENDER_COMMAND_TYPE_BORDER,\n                        };\n                        Clay__AddRenderCommand(renderCommand);\n                        if (borderConfig->width.betweenChildren > 0 && borderConfig->color.a > 0) {\n                            float halfGap = layoutConfig->childGap / 2;\n                            Clay_Vector2 borderOffset = { (float)layoutConfig->padding.left - halfGap, (float)layoutConfig->padding.top - halfGap };\n                            if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {\n                                for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {\n                                    Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);\n                                    if (i > 0) {\n                                        Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {\n                                            .boundingBox = { currentElementBoundingBox.x + borderOffset.x + scrollOffset.x, currentElementBoundingBox.y + scrollOffset.y, (float)borderConfig->width.betweenChildren, currentElement->dimensions.height },\n                                            .renderData = { .rectangle = {\n                                                .backgroundColor = borderConfig->color,\n                                            } },\n                                            .userData = sharedConfig->userData,\n                                            .id = Clay__HashNumber(currentElement->id, currentElement->childrenOrTextContent.children.length + 1 + i).id,\n                                            .commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE,\n                                        });\n                                    }\n                                    borderOffset.x += (childElement->dimensions.width + (float)layoutConfig->childGap);\n                                }\n                            } else {\n                                for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {\n                                    Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);\n                                    if (i > 0) {\n                                        Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {\n                                            .boundingBox = { currentElementBoundingBox.x + scrollOffset.x, currentElementBoundingBox.y + borderOffset.y + scrollOffset.y, currentElement->dimensions.width, (float)borderConfig->width.betweenChildren },\n                                            .renderData = { .rectangle = {\n                                                    .backgroundColor = borderConfig->color,\n                                            } },\n                                            .userData = sharedConfig->userData,\n                                            .id = Clay__HashNumber(currentElement->id, currentElement->childrenOrTextContent.children.length + 1 + i).id,\n                                            .commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE,\n                                        });\n                                    }\n                                    borderOffset.y += (childElement->dimensions.height + (float)layoutConfig->childGap);\n                                }\n                            }\n                        }\n                    }\n                }\n                // This exists because the scissor needs to end _after_ borders between elements\n                if (closeClipElement) {\n                    Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {\n                        .id = Clay__HashNumber(currentElement->id, rootElement->childrenOrTextContent.children.length + 11).id,\n                        .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END,\n                    });\n                }\n\n                dfsBuffer.length--;\n                continue;\n            }\n\n            // Add children to the DFS buffer\n            if (!Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) {\n                dfsBuffer.length += currentElement->childrenOrTextContent.children.length;\n                for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {\n                    Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);\n                    // Alignment along non layout axis\n                    if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {\n                        currentElementTreeNode->nextChildOffset.y = currentElement->layoutConfig->padding.top;\n                        float whiteSpaceAroundChild = currentElement->dimensions.height - (float)(layoutConfig->padding.top + layoutConfig->padding.bottom) - childElement->dimensions.height;\n                        switch (layoutConfig->childAlignment.y) {\n                            case CLAY_ALIGN_Y_TOP: break;\n                            case CLAY_ALIGN_Y_CENTER: currentElementTreeNode->nextChildOffset.y += whiteSpaceAroundChild / 2; break;\n                            case CLAY_ALIGN_Y_BOTTOM: currentElementTreeNode->nextChildOffset.y += whiteSpaceAroundChild; break;\n                        }\n                    } else {\n                        currentElementTreeNode->nextChildOffset.x = currentElement->layoutConfig->padding.left;\n                        float whiteSpaceAroundChild = currentElement->dimensions.width - (float)(layoutConfig->padding.left + layoutConfig->padding.right) - childElement->dimensions.width;\n                        switch (layoutConfig->childAlignment.x) {\n                            case CLAY_ALIGN_X_LEFT: break;\n                            case CLAY_ALIGN_X_CENTER: currentElementTreeNode->nextChildOffset.x += whiteSpaceAroundChild / 2; break;\n                            case CLAY_ALIGN_X_RIGHT: currentElementTreeNode->nextChildOffset.x += whiteSpaceAroundChild; break;\n                        }\n                    }\n\n                    Clay_Vector2 childPosition = {\n                        currentElementTreeNode->position.x + currentElementTreeNode->nextChildOffset.x + scrollOffset.x,\n                        currentElementTreeNode->position.y + currentElementTreeNode->nextChildOffset.y + scrollOffset.y,\n                    };\n\n                    // DFS buffer elements need to be added in reverse because stack traversal happens backwards\n                    uint32_t newNodeIndex = dfsBuffer.length - 1 - i;\n                    dfsBuffer.internalArray[newNodeIndex] = CLAY__INIT(Clay__LayoutElementTreeNode) {\n                        .layoutElement = childElement,\n                        .position = { childPosition.x, childPosition.y },\n                        .nextChildOffset = { .x = (float)childElement->layoutConfig->padding.left, .y = (float)childElement->layoutConfig->padding.top },\n                    };\n                    context->treeNodeVisited.internalArray[newNodeIndex] = false;\n\n                    // Update parent offsets\n                    if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {\n                        currentElementTreeNode->nextChildOffset.x += childElement->dimensions.width + (float)layoutConfig->childGap;\n                    } else {\n                        currentElementTreeNode->nextChildOffset.y += childElement->dimensions.height + (float)layoutConfig->childGap;\n                    }\n                }\n            }\n        }\n\n        if (root->clipElementId) {\n            Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) { .id = Clay__HashNumber(rootElement->id, rootElement->childrenOrTextContent.children.length + 11).id, .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END });\n        }\n    }\n}\n\nCLAY_WASM_EXPORT(\"Clay_GetPointerOverIds\")\nCLAY_DLL_EXPORT Clay_ElementIdArray Clay_GetPointerOverIds(void) {\n    return Clay_GetCurrentContext()->pointerOverIds;\n}\n\n#pragma region DebugTools\nClay_Color CLAY__DEBUGVIEW_COLOR_1 = {58, 56, 52, 255};\nClay_Color CLAY__DEBUGVIEW_COLOR_2 = {62, 60, 58, 255};\nClay_Color CLAY__DEBUGVIEW_COLOR_3 = {141, 133, 135, 255};\nClay_Color CLAY__DEBUGVIEW_COLOR_4 = {238, 226, 231, 255};\nClay_Color CLAY__DEBUGVIEW_COLOR_SELECTED_ROW = {102, 80, 78, 255};\nconst int32_t CLAY__DEBUGVIEW_ROW_HEIGHT = 30;\nconst int32_t CLAY__DEBUGVIEW_OUTER_PADDING = 10;\nconst int32_t CLAY__DEBUGVIEW_INDENT_WIDTH = 16;\nClay_TextElementConfig Clay__DebugView_TextNameConfig = {.textColor = {238, 226, 231, 255}, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE };\nClay_LayoutConfig Clay__DebugView_ScrollViewItemLayoutConfig = CLAY__DEFAULT_STRUCT;\n\ntypedef struct {\n    Clay_String label;\n    Clay_Color color;\n} Clay__DebugElementConfigTypeLabelConfig;\n\nClay__DebugElementConfigTypeLabelConfig Clay__DebugGetElementConfigTypeLabel(Clay__ElementConfigType type) {\n    switch (type) {\n        case CLAY__ELEMENT_CONFIG_TYPE_SHARED: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING(\"Shared\"), {243,134,48,255} };\n        case CLAY__ELEMENT_CONFIG_TYPE_TEXT: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING(\"Text\"), {105,210,231,255} };\n        case CLAY__ELEMENT_CONFIG_TYPE_ASPECT: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING(\"Aspect\"), {101,149,194,255} };\n        case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING(\"Image\"), {121,189,154,255} };\n        case CLAY__ELEMENT_CONFIG_TYPE_FLOATING: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING(\"Floating\"), {250,105,0,255} };\n        case CLAY__ELEMENT_CONFIG_TYPE_CLIP: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) {CLAY_STRING(\"Scroll\"), {242, 196, 90, 255} };\n        case CLAY__ELEMENT_CONFIG_TYPE_BORDER: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) {CLAY_STRING(\"Border\"), {108, 91, 123, 255} };\n        case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING(\"Custom\"), {11,72,107,255} };\n        default: break;\n    }\n    return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING(\"Error\"), {0,0,0,255} };\n}\n\ntypedef struct {\n    int32_t rowCount;\n    int32_t selectedElementRowIndex;\n} Clay__RenderDebugLayoutData;\n\n// Returns row count\nClay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialRootsLength, int32_t highlightedRowIndex) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    Clay__int32_tArray dfsBuffer = context->reusableElementIndexBuffer;\n    Clay__DebugView_ScrollViewItemLayoutConfig = CLAY__INIT(Clay_LayoutConfig) { .sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT) }, .childGap = 6, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }};\n    Clay__RenderDebugLayoutData layoutData = CLAY__DEFAULT_STRUCT;\n\n    uint32_t highlightedElementId = 0;\n\n    for (int32_t rootIndex = 0; rootIndex < initialRootsLength; ++rootIndex) {\n        dfsBuffer.length = 0;\n        Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex);\n        Clay__int32_tArray_Add(&dfsBuffer, (int32_t)root->layoutElementIndex);\n        context->treeNodeVisited.internalArray[0] = false;\n        if (rootIndex > 0) {\n            CLAY(CLAY_IDI(\"Clay__DebugView_EmptyRowOuter\", rootIndex), { .layout = { .sizing = {.width = CLAY_SIZING_GROW(0)}, .padding = {CLAY__DEBUGVIEW_INDENT_WIDTH / 2, 0, 0, 0} } }) {\n                CLAY(CLAY_IDI(\"Clay__DebugView_EmptyRow\", rootIndex), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED((float)CLAY__DEBUGVIEW_ROW_HEIGHT) }}, .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .top = 1 } } }) {}\n            }\n            layoutData.rowCount++;\n        }\n        while (dfsBuffer.length > 0) {\n            int32_t currentElementIndex = Clay__int32_tArray_GetValue(&dfsBuffer, (int)dfsBuffer.length - 1);\n            Clay_LayoutElement *currentElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)currentElementIndex);\n            if (context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) {\n                if (!Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) && currentElement->childrenOrTextContent.children.length > 0) {\n                    Clay__CloseElement();\n                    Clay__CloseElement();\n                    Clay__CloseElement();\n                }\n                dfsBuffer.length--;\n                continue;\n            }\n\n            if (highlightedRowIndex == layoutData.rowCount) {\n                if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n                    context->debugSelectedElementId = currentElement->id;\n                }\n                highlightedElementId = currentElement->id;\n            }\n\n            context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;\n            Clay_LayoutElementHashMapItem *currentElementData = Clay__GetHashMapItem(currentElement->id);\n            bool offscreen = Clay__ElementIsOffscreen(&currentElementData->boundingBox);\n            if (context->debugSelectedElementId == currentElement->id) {\n                layoutData.selectedElementRowIndex = layoutData.rowCount;\n            }\n            CLAY(CLAY_IDI(\"Clay__DebugView_ElementOuter\", currentElement->id), { .layout = Clay__DebugView_ScrollViewItemLayoutConfig }) {\n                // Collapse icon / button\n                if (!(Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || currentElement->childrenOrTextContent.children.length == 0)) {\n                    CLAY(CLAY_IDI(\"Clay__DebugView_CollapseElement\", currentElement->id), {\n                        .layout = { .sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} },\n                        .cornerRadius = CLAY_CORNER_RADIUS(4),\n                        .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = {1, 1, 1, 1, 0} },\n                    }) {\n                        CLAY_TEXT((currentElementData && currentElementData->debugData->collapsed) ? CLAY_STRING(\"+\") : CLAY_STRING(\"-\"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));\n                    }\n                } else { // Square dot for empty containers\n                    CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER } } }) {\n                        CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_FIXED(8), CLAY_SIZING_FIXED(8)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3, .cornerRadius = CLAY_CORNER_RADIUS(2) }) {}\n                    }\n                }\n                // Collisions and offscreen info\n                if (currentElementData) {\n                    if (currentElementData->debugData->collision) {\n                        CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 }}, .border = { .color = {177, 147, 8, 255}, .width = {1, 1, 1, 1, 0} } }) {\n                            CLAY_TEXT(CLAY_STRING(\"Duplicate ID\"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }));\n                        }\n                    }\n                    if (offscreen) {\n                        CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .border = {  .color = CLAY__DEBUGVIEW_COLOR_3, .width = { 1, 1, 1, 1, 0} } }) {\n                            CLAY_TEXT(CLAY_STRING(\"Offscreen\"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }));\n                        }\n                    }\n                }\n                Clay_String idString = context->layoutElementIdStrings.internalArray[currentElementIndex];\n                if (idString.length > 0) {\n                    CLAY_TEXT(idString, offscreen ? CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }) : &Clay__DebugView_TextNameConfig);\n                }\n                for (int32_t elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) {\n                    Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(&currentElement->elementConfigs, elementConfigIndex);\n                    if (elementConfig->type == CLAY__ELEMENT_CONFIG_TYPE_SHARED) {\n                        Clay_Color labelColor = {243,134,48,90};\n                        labelColor.a = 90;\n                        Clay_Color backgroundColor = elementConfig->config.sharedElementConfig->backgroundColor;\n                        Clay_CornerRadius radius = elementConfig->config.sharedElementConfig->cornerRadius;\n                        if (backgroundColor.a > 0) {\n                            CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = labelColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = labelColor, .width = { 1, 1, 1, 1, 0} } }) {\n                                CLAY_TEXT(CLAY_STRING(\"Color\"), CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));\n                            }\n                        }\n                        if (radius.bottomLeft > 0) {\n                            CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = labelColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = labelColor, .width = { 1, 1, 1, 1, 0 } } }) {\n                                CLAY_TEXT(CLAY_STRING(\"Radius\"), CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));\n                            }\n                        }\n                        continue;\n                    }\n                    Clay__DebugElementConfigTypeLabelConfig config = Clay__DebugGetElementConfigTypeLabel(elementConfig->type);\n                    Clay_Color backgroundColor = config.color;\n                    backgroundColor.a = 90;\n                    CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = config.color, .width = { 1, 1, 1, 1, 0 } } }) {\n                        CLAY_TEXT(config.label, CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));\n                    }\n                }\n            }\n\n            // Render the text contents below the element as a non-interactive row\n            if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) {\n                layoutData.rowCount++;\n                Clay__TextElementData *textElementData = currentElement->childrenOrTextContent.textElementData;\n                Clay_TextElementConfig *rawTextConfig = offscreen ? CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }) : &Clay__DebugView_TextNameConfig;\n                CLAY_AUTO_ID({ .layout = { .sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } } }) {\n                    CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_INDENT_WIDTH + 16) } } }) {}\n                    CLAY_TEXT(CLAY_STRING(\"\\\"\"), rawTextConfig);\n                    CLAY_TEXT(textElementData->text.length > 40 ? (CLAY__INIT(Clay_String) { .length = 40, .chars = textElementData->text.chars }) : textElementData->text, rawTextConfig);\n                    if (textElementData->text.length > 40) {\n                        CLAY_TEXT(CLAY_STRING(\"...\"), rawTextConfig);\n                    }\n                    CLAY_TEXT(CLAY_STRING(\"\\\"\"), rawTextConfig);\n                }\n            } else if (currentElement->childrenOrTextContent.children.length > 0) {\n                Clay__OpenElement();\n                Clay__ConfigureOpenElement(CLAY__INIT(Clay_ElementDeclaration) { .layout = { .padding = { .left = 8 } } });\n                Clay__OpenElement();\n                Clay__ConfigureOpenElement(CLAY__INIT(Clay_ElementDeclaration) { .layout = { .padding = { .left = CLAY__DEBUGVIEW_INDENT_WIDTH }}, .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .left = 1 } }});\n                Clay__OpenElement();\n                Clay__ConfigureOpenElement(CLAY__INIT(Clay_ElementDeclaration) { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } });\n            }\n\n            layoutData.rowCount++;\n            if (!(Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || (currentElementData && currentElementData->debugData->collapsed))) {\n                for (int32_t i = currentElement->childrenOrTextContent.children.length - 1; i >= 0; --i) {\n                    Clay__int32_tArray_Add(&dfsBuffer, currentElement->childrenOrTextContent.children.elements[i]);\n                    context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = false; // TODO needs to be ranged checked\n                }\n            }\n        }\n    }\n\n    if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n        Clay_ElementId collapseButtonId = Clay__HashString(CLAY_STRING(\"Clay__DebugView_CollapseElement\"), 0);\n        for (int32_t i = (int)context->pointerOverIds.length - 1; i >= 0; i--) {\n            Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i);\n            if (elementId->baseId == collapseButtonId.baseId) {\n                Clay_LayoutElementHashMapItem *highlightedItem = Clay__GetHashMapItem(elementId->offset);\n                highlightedItem->debugData->collapsed = !highlightedItem->debugData->collapsed;\n                break;\n            }\n        }\n    }\n\n    if (highlightedElementId) {\n        CLAY(CLAY_ID(\"Clay__DebugView_ElementHighlight\"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .floating = { .parentId = highlightedElementId, .zIndex = 32767, .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID } }) {\n            CLAY(CLAY_ID(\"Clay__DebugView_ElementHighlightRectangle\"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .backgroundColor = Clay__debugViewHighlightColor }) {}\n        }\n    }\n    return layoutData;\n}\n\nvoid Clay__RenderDebugLayoutSizing(Clay_SizingAxis sizing, Clay_TextElementConfig *infoTextConfig) {\n    Clay_String sizingLabel = CLAY_STRING(\"GROW\");\n    if (sizing.type == CLAY__SIZING_TYPE_FIT) {\n        sizingLabel = CLAY_STRING(\"FIT\");\n    } else if (sizing.type == CLAY__SIZING_TYPE_PERCENT) {\n        sizingLabel = CLAY_STRING(\"PERCENT\");\n    } else if (sizing.type == CLAY__SIZING_TYPE_FIXED) {\n        sizingLabel = CLAY_STRING(\"FIXED\");\n    }\n    CLAY_TEXT(sizingLabel, infoTextConfig);\n    if (sizing.type == CLAY__SIZING_TYPE_GROW || sizing.type == CLAY__SIZING_TYPE_FIT || sizing.type == CLAY__SIZING_TYPE_FIXED) {\n        CLAY_TEXT(CLAY_STRING(\"(\"), infoTextConfig);\n        if (sizing.size.minMax.min != 0) {\n            CLAY_TEXT(CLAY_STRING(\"min: \"), infoTextConfig);\n            CLAY_TEXT(Clay__IntToString(sizing.size.minMax.min), infoTextConfig);\n            if (sizing.size.minMax.max != CLAY__MAXFLOAT) {\n                CLAY_TEXT(CLAY_STRING(\", \"), infoTextConfig);\n            }\n        }\n        if (sizing.size.minMax.max != CLAY__MAXFLOAT) {\n            CLAY_TEXT(CLAY_STRING(\"max: \"), infoTextConfig);\n            CLAY_TEXT(Clay__IntToString(sizing.size.minMax.max), infoTextConfig);\n        }\n        CLAY_TEXT(CLAY_STRING(\")\"), infoTextConfig);\n    } else if (sizing.type == CLAY__SIZING_TYPE_PERCENT) {\n        CLAY_TEXT(CLAY_STRING(\"(\"), infoTextConfig);\n        CLAY_TEXT(Clay__IntToString(sizing.size.percent * 100), infoTextConfig);\n        CLAY_TEXT(CLAY_STRING(\"%)\"), infoTextConfig);\n    }\n}\n\nvoid Clay__RenderDebugViewElementConfigHeader(Clay_String elementId, Clay__ElementConfigType type) {\n    Clay__DebugElementConfigTypeLabelConfig config = Clay__DebugGetElementConfigTypeLabel(type);\n    Clay_Color backgroundColor = config.color;\n    backgroundColor.a = 90;\n    CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(CLAY__DEBUGVIEW_OUTER_PADDING), .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } } }) {\n        CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = config.color, .width = { 1, 1, 1, 1, 0 } } }) {\n            CLAY_TEXT(config.label, CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));\n        }\n        CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}\n        CLAY_TEXT(elementId, CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }));\n    }\n}\n\nvoid Clay__RenderDebugViewColor(Clay_Color color, Clay_TextElementConfig *textConfig) {\n    CLAY_AUTO_ID({ .layout = { .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {\n        CLAY_TEXT(CLAY_STRING(\"{ r: \"), textConfig);\n        CLAY_TEXT(Clay__IntToString(color.r), textConfig);\n        CLAY_TEXT(CLAY_STRING(\", g: \"), textConfig);\n        CLAY_TEXT(Clay__IntToString(color.g), textConfig);\n        CLAY_TEXT(CLAY_STRING(\", b: \"), textConfig);\n        CLAY_TEXT(Clay__IntToString(color.b), textConfig);\n        CLAY_TEXT(CLAY_STRING(\", a: \"), textConfig);\n        CLAY_TEXT(Clay__IntToString(color.a), textConfig);\n        CLAY_TEXT(CLAY_STRING(\" }\"), textConfig);\n        CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_FIXED(10) } } }) {}\n        CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8)} }, .backgroundColor = color, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = CLAY__DEBUGVIEW_COLOR_4, .width = { 1, 1, 1, 1, 0 } } }) {}\n    }\n}\n\nvoid Clay__RenderDebugViewCornerRadius(Clay_CornerRadius cornerRadius, Clay_TextElementConfig *textConfig) {\n    CLAY_AUTO_ID({ .layout = { .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {\n        CLAY_TEXT(CLAY_STRING(\"{ topLeft: \"), textConfig);\n        CLAY_TEXT(Clay__IntToString(cornerRadius.topLeft), textConfig);\n        CLAY_TEXT(CLAY_STRING(\", topRight: \"), textConfig);\n        CLAY_TEXT(Clay__IntToString(cornerRadius.topRight), textConfig);\n        CLAY_TEXT(CLAY_STRING(\", bottomLeft: \"), textConfig);\n        CLAY_TEXT(Clay__IntToString(cornerRadius.bottomLeft), textConfig);\n        CLAY_TEXT(CLAY_STRING(\", bottomRight: \"), textConfig);\n        CLAY_TEXT(Clay__IntToString(cornerRadius.bottomRight), textConfig);\n        CLAY_TEXT(CLAY_STRING(\" }\"), textConfig);\n    }\n}\n\nvoid HandleDebugViewCloseButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    (void) elementId; (void) pointerInfo; (void) userData;\n    if (pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n        context->debugModeEnabled = false;\n    }\n}\n\nvoid Clay__RenderDebugView(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    Clay_ElementId closeButtonId = Clay__HashString(CLAY_STRING(\"Clay__DebugViewTopHeaderCloseButtonOuter\"), 0);\n    if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n        for (int32_t i = 0; i < context->pointerOverIds.length; ++i) {\n            Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i);\n            if (elementId->id == closeButtonId.id) {\n                context->debugModeEnabled = false;\n                return;\n            }\n        }\n    }\n\n    uint32_t initialRootsLength = context->layoutElementTreeRoots.length;\n    uint32_t initialElementsLength = context->layoutElements.length;\n    Clay_TextElementConfig *infoTextConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE });\n    Clay_TextElementConfig *infoTitleConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE });\n    Clay_ElementId scrollId = Clay__HashString(CLAY_STRING(\"Clay__DebugViewOuterScrollPane\"), 0);\n    float scrollYOffset = 0;\n    bool pointerInDebugView = context->pointerInfo.position.y < context->layoutDimensions.height - 300;\n    for (int32_t i = 0; i < context->scrollContainerDatas.length; ++i) {\n        Clay__ScrollContainerDataInternal *scrollContainerData = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);\n        if (scrollContainerData->elementId == scrollId.id) {\n            if (!context->externalScrollHandlingEnabled) {\n                scrollYOffset = scrollContainerData->scrollPosition.y;\n            } else {\n                pointerInDebugView = context->pointerInfo.position.y + scrollContainerData->scrollPosition.y < context->layoutDimensions.height - 300;\n            }\n            break;\n        }\n    }\n    int32_t highlightedRow = pointerInDebugView\n            ? (int32_t)((context->pointerInfo.position.y - scrollYOffset) / (float)CLAY__DEBUGVIEW_ROW_HEIGHT) - 1\n            : -1;\n    if (context->pointerInfo.position.x < context->layoutDimensions.width - (float)Clay__debugViewWidth) {\n        highlightedRow = -1;\n    }\n    Clay__RenderDebugLayoutData layoutData = CLAY__DEFAULT_STRUCT;\n    CLAY(CLAY_ID(\"Clay__DebugView\"), {\n         .layout = { .sizing = { CLAY_SIZING_FIXED((float)Clay__debugViewWidth) , CLAY_SIZING_FIXED(context->layoutDimensions.height) }, .layoutDirection = CLAY_TOP_TO_BOTTOM },\n        .floating = { .zIndex = 32765, .attachPoints = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_CENTER }, .attachTo = CLAY_ATTACH_TO_ROOT, .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT },\n        .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .bottom = 1 } }\n    }) {\n        CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_2 }) {\n            CLAY_TEXT(CLAY_STRING(\"Clay Debug Tools\"), infoTextConfig);\n            CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}\n            // Close button\n            CLAY_AUTO_ID({\n                .layout = { .sizing = {CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 10), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 10)}, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} },\n                .backgroundColor = {217,91,67,80},\n                .cornerRadius = CLAY_CORNER_RADIUS(4),\n                .border = { .color = { 217,91,67,255 }, .width = { 1, 1, 1, 1, 0 } },\n            }) {\n                Clay_OnHover(HandleDebugViewCloseButtonInteraction, 0);\n                CLAY_TEXT(CLAY_STRING(\"x\"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));\n            }\n        }\n        CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(1)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3 } ) {}\n        CLAY(scrollId, { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {\n            CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = ((initialElementsLength + initialRootsLength) & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1 }) {\n                Clay_ElementId panelContentsId = Clay__HashString(CLAY_STRING(\"Clay__DebugViewPaneOuter\"), 0);\n                // Element list\n                CLAY(panelContentsId, { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .floating = { .zIndex = 32766, .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, .attachTo = CLAY_ATTACH_TO_PARENT, .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT } }) {\n                    CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = { CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n                        layoutData = Clay__RenderDebugLayoutElementsList((int32_t)initialRootsLength, highlightedRow);\n                    }\n                }\n                float contentWidth = Clay__GetHashMapItem(panelContentsId.id)->layoutElement->dimensions.width;\n                CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(contentWidth) }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {}\n                for (int32_t i = 0; i < layoutData.rowCount; i++) {\n                    Clay_Color rowColor = (i & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1;\n                    if (i == layoutData.selectedElementRowIndex) {\n                        rowColor = CLAY__DEBUGVIEW_COLOR_SELECTED_ROW;\n                    }\n                    if (i == highlightedRow) {\n                        rowColor.r *= 1.25f;\n                        rowColor.g *= 1.25f;\n                        rowColor.b *= 1.25f;\n                    }\n                    CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = rowColor } ) {}\n                }\n            }\n        }\n        CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3 }) {}\n        if (context->debugSelectedElementId != 0) {\n            Clay_LayoutElementHashMapItem *selectedItem = Clay__GetHashMapItem(context->debugSelectedElementId);\n            CLAY_AUTO_ID({\n                .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(300)}, .layoutDirection = CLAY_TOP_TO_BOTTOM },\n                .backgroundColor = CLAY__DEBUGVIEW_COLOR_2 ,\n                .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },\n                .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .betweenChildren = 1 } }\n            }) {\n                CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT + 8)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {\n                    CLAY_TEXT(CLAY_STRING(\"Layout Config\"), infoTextConfig);\n                    CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}\n                    if (selectedItem->elementId.stringId.length != 0) {\n                        CLAY_TEXT(selectedItem->elementId.stringId, infoTitleConfig);\n                        if (selectedItem->elementId.offset != 0) {\n                            CLAY_TEXT(CLAY_STRING(\" (\"), infoTitleConfig);\n                            CLAY_TEXT(Clay__IntToString(selectedItem->elementId.offset), infoTitleConfig);\n                            CLAY_TEXT(CLAY_STRING(\")\"), infoTitleConfig);\n                        }\n                    }\n                }\n                Clay_Padding attributeConfigPadding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 8, 8};\n                // Clay_LayoutConfig debug info\n                CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n                    // .boundingBox\n                    CLAY_TEXT(CLAY_STRING(\"Bounding Box\"), infoTitleConfig);\n                    CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {\n                        CLAY_TEXT(CLAY_STRING(\"{ x: \"), infoTextConfig);\n                        CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.x), infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\", y: \"), infoTextConfig);\n                        CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.y), infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\", width: \"), infoTextConfig);\n                        CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.width), infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\", height: \"), infoTextConfig);\n                        CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.height), infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\" }\"), infoTextConfig);\n                    }\n                    // .layoutDirection\n                    CLAY_TEXT(CLAY_STRING(\"Layout Direction\"), infoTitleConfig);\n                    Clay_LayoutConfig *layoutConfig = selectedItem->layoutElement->layoutConfig;\n                    CLAY_TEXT(layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM ? CLAY_STRING(\"TOP_TO_BOTTOM\") : CLAY_STRING(\"LEFT_TO_RIGHT\"), infoTextConfig);\n                    // .sizing\n                    CLAY_TEXT(CLAY_STRING(\"Sizing\"), infoTitleConfig);\n                    CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {\n                        CLAY_TEXT(CLAY_STRING(\"width: \"), infoTextConfig);\n                        Clay__RenderDebugLayoutSizing(layoutConfig->sizing.width, infoTextConfig);\n                    }\n                    CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {\n                        CLAY_TEXT(CLAY_STRING(\"height: \"), infoTextConfig);\n                        Clay__RenderDebugLayoutSizing(layoutConfig->sizing.height, infoTextConfig);\n                    }\n                    // .padding\n                    CLAY_TEXT(CLAY_STRING(\"Padding\"), infoTitleConfig);\n                    CLAY(CLAY_ID(\"Clay__DebugViewElementInfoPadding\"), { }) {\n                        CLAY_TEXT(CLAY_STRING(\"{ left: \"), infoTextConfig);\n                        CLAY_TEXT(Clay__IntToString(layoutConfig->padding.left), infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\", right: \"), infoTextConfig);\n                        CLAY_TEXT(Clay__IntToString(layoutConfig->padding.right), infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\", top: \"), infoTextConfig);\n                        CLAY_TEXT(Clay__IntToString(layoutConfig->padding.top), infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\", bottom: \"), infoTextConfig);\n                        CLAY_TEXT(Clay__IntToString(layoutConfig->padding.bottom), infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\" }\"), infoTextConfig);\n                    }\n                    // .childGap\n                    CLAY_TEXT(CLAY_STRING(\"Child Gap\"), infoTitleConfig);\n                    CLAY_TEXT(Clay__IntToString(layoutConfig->childGap), infoTextConfig);\n                    // .childAlignment\n                    CLAY_TEXT(CLAY_STRING(\"Child Alignment\"), infoTitleConfig);\n                    CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {\n                        CLAY_TEXT(CLAY_STRING(\"{ x: \"), infoTextConfig);\n                        Clay_String alignX = CLAY_STRING(\"LEFT\");\n                        if (layoutConfig->childAlignment.x == CLAY_ALIGN_X_CENTER) {\n                            alignX = CLAY_STRING(\"CENTER\");\n                        } else if (layoutConfig->childAlignment.x == CLAY_ALIGN_X_RIGHT) {\n                            alignX = CLAY_STRING(\"RIGHT\");\n                        }\n                        CLAY_TEXT(alignX, infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\", y: \"), infoTextConfig);\n                        Clay_String alignY = CLAY_STRING(\"TOP\");\n                        if (layoutConfig->childAlignment.y == CLAY_ALIGN_Y_CENTER) {\n                            alignY = CLAY_STRING(\"CENTER\");\n                        } else if (layoutConfig->childAlignment.y == CLAY_ALIGN_Y_BOTTOM) {\n                            alignY = CLAY_STRING(\"BOTTOM\");\n                        }\n                        CLAY_TEXT(alignY, infoTextConfig);\n                        CLAY_TEXT(CLAY_STRING(\" }\"), infoTextConfig);\n                    }\n                }\n                for (int32_t elementConfigIndex = 0; elementConfigIndex < selectedItem->layoutElement->elementConfigs.length; ++elementConfigIndex) {\n                    Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(&selectedItem->layoutElement->elementConfigs, elementConfigIndex);\n                    Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, elementConfig->type);\n                    switch (elementConfig->type) {\n                        case CLAY__ELEMENT_CONFIG_TYPE_SHARED: {\n                            Clay_SharedElementConfig *sharedConfig = elementConfig->config.sharedElementConfig;\n                            CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {\n                                // .backgroundColor\n                                CLAY_TEXT(CLAY_STRING(\"Background Color\"), infoTitleConfig);\n                                Clay__RenderDebugViewColor(sharedConfig->backgroundColor, infoTextConfig);\n                                // .cornerRadius\n                                CLAY_TEXT(CLAY_STRING(\"Corner Radius\"), infoTitleConfig);\n                                Clay__RenderDebugViewCornerRadius(sharedConfig->cornerRadius, infoTextConfig);\n                            }\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_TEXT: {\n                            Clay_TextElementConfig *textConfig = elementConfig->config.textElementConfig;\n                            CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n                                // .fontSize\n                                CLAY_TEXT(CLAY_STRING(\"Font Size\"), infoTitleConfig);\n                                CLAY_TEXT(Clay__IntToString(textConfig->fontSize), infoTextConfig);\n                                // .fontId\n                                CLAY_TEXT(CLAY_STRING(\"Font ID\"), infoTitleConfig);\n                                CLAY_TEXT(Clay__IntToString(textConfig->fontId), infoTextConfig);\n                                // .lineHeight\n                                CLAY_TEXT(CLAY_STRING(\"Line Height\"), infoTitleConfig);\n                                CLAY_TEXT(textConfig->lineHeight == 0 ? CLAY_STRING(\"auto\") : Clay__IntToString(textConfig->lineHeight), infoTextConfig);\n                                // .letterSpacing\n                                CLAY_TEXT(CLAY_STRING(\"Letter Spacing\"), infoTitleConfig);\n                                CLAY_TEXT(Clay__IntToString(textConfig->letterSpacing), infoTextConfig);\n                                // .wrapMode\n                                CLAY_TEXT(CLAY_STRING(\"Wrap Mode\"), infoTitleConfig);\n                                Clay_String wrapMode = CLAY_STRING(\"WORDS\");\n                                if (textConfig->wrapMode == CLAY_TEXT_WRAP_NONE) {\n                                    wrapMode = CLAY_STRING(\"NONE\");\n                                } else if (textConfig->wrapMode == CLAY_TEXT_WRAP_NEWLINES) {\n                                    wrapMode = CLAY_STRING(\"NEWLINES\");\n                                }\n                                CLAY_TEXT(wrapMode, infoTextConfig);\n                                // .textAlignment\n                                CLAY_TEXT(CLAY_STRING(\"Text Alignment\"), infoTitleConfig);\n                                Clay_String textAlignment = CLAY_STRING(\"LEFT\");\n                                if (textConfig->textAlignment == CLAY_TEXT_ALIGN_CENTER) {\n                                    textAlignment = CLAY_STRING(\"CENTER\");\n                                } else if (textConfig->textAlignment == CLAY_TEXT_ALIGN_RIGHT) {\n                                    textAlignment = CLAY_STRING(\"RIGHT\");\n                                }\n                                CLAY_TEXT(textAlignment, infoTextConfig);\n                                // .textColor\n                                CLAY_TEXT(CLAY_STRING(\"Text Color\"), infoTitleConfig);\n                                Clay__RenderDebugViewColor(textConfig->textColor, infoTextConfig);\n                            }\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_ASPECT: {\n                            Clay_AspectRatioElementConfig *aspectRatioConfig = elementConfig->config.aspectRatioElementConfig;\n                            CLAY(CLAY_ID(\"Clay__DebugViewElementInfoAspectRatioBody\"), { .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n                                CLAY_TEXT(CLAY_STRING(\"Aspect Ratio\"), infoTitleConfig);\n                                // Aspect Ratio\n                                CLAY(CLAY_ID(\"Clay__DebugViewElementInfoAspectRatio\"), { }) {\n                                    CLAY_TEXT(Clay__IntToString(aspectRatioConfig->aspectRatio), infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\".\"), infoTextConfig);\n                                    float frac = aspectRatioConfig->aspectRatio - (int)(aspectRatioConfig->aspectRatio);\n                                    frac *= 100;\n                                    if ((int)frac < 10) {\n                                        CLAY_TEXT(CLAY_STRING(\"0\"), infoTextConfig);\n                                    }\n                                    CLAY_TEXT(Clay__IntToString(frac), infoTextConfig);\n                                }\n                            }\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: {\n                            Clay_ImageElementConfig *imageConfig = elementConfig->config.imageElementConfig;\n                            Clay_AspectRatioElementConfig aspectConfig = { 1 };\n                            if (Clay__ElementHasConfig(selectedItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT)) {\n                                aspectConfig = *Clay__FindElementConfigWithType(selectedItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;\n                            }\n                            CLAY(CLAY_ID(\"Clay__DebugViewElementInfoImageBody\"), { .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n                                // Image Preview\n                                CLAY_TEXT(CLAY_STRING(\"Preview\"), infoTitleConfig);\n                                CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(64, 128), .height = CLAY_SIZING_GROW(64, 128) }}, .aspectRatio = aspectConfig, .image = *imageConfig }) {}\n                            }\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_CLIP: {\n                            Clay_ClipElementConfig *clipConfig = elementConfig->config.clipElementConfig;\n                            CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n                                // .vertical\n                                CLAY_TEXT(CLAY_STRING(\"Vertical\"), infoTitleConfig);\n                                CLAY_TEXT(clipConfig->vertical ? CLAY_STRING(\"true\") : CLAY_STRING(\"false\") , infoTextConfig);\n                                // .horizontal\n                                CLAY_TEXT(CLAY_STRING(\"Horizontal\"), infoTitleConfig);\n                                CLAY_TEXT(clipConfig->horizontal ? CLAY_STRING(\"true\") : CLAY_STRING(\"false\") , infoTextConfig);\n                            }\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_FLOATING: {\n                            Clay_FloatingElementConfig *floatingConfig = elementConfig->config.floatingElementConfig;\n                            CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n                                // .offset\n                                CLAY_TEXT(CLAY_STRING(\"Offset\"), infoTitleConfig);\n                                CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {\n                                    CLAY_TEXT(CLAY_STRING(\"{ x: \"), infoTextConfig);\n                                    CLAY_TEXT(Clay__IntToString(floatingConfig->offset.x), infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\", y: \"), infoTextConfig);\n                                    CLAY_TEXT(Clay__IntToString(floatingConfig->offset.y), infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\" }\"), infoTextConfig);\n                                }\n                                // .expand\n                                CLAY_TEXT(CLAY_STRING(\"Expand\"), infoTitleConfig);\n                                CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {\n                                    CLAY_TEXT(CLAY_STRING(\"{ width: \"), infoTextConfig);\n                                    CLAY_TEXT(Clay__IntToString(floatingConfig->expand.width), infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\", height: \"), infoTextConfig);\n                                    CLAY_TEXT(Clay__IntToString(floatingConfig->expand.height), infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\" }\"), infoTextConfig);\n                                }\n                                // .zIndex\n                                CLAY_TEXT(CLAY_STRING(\"z-index\"), infoTitleConfig);\n                                CLAY_TEXT(Clay__IntToString(floatingConfig->zIndex), infoTextConfig);\n                                // .parentId\n                                CLAY_TEXT(CLAY_STRING(\"Parent\"), infoTitleConfig);\n                                Clay_LayoutElementHashMapItem *hashItem = Clay__GetHashMapItem(floatingConfig->parentId);\n                                CLAY_TEXT(hashItem->elementId.stringId, infoTextConfig);\n                                // .attachPoints\n                                CLAY_TEXT(CLAY_STRING(\"Attach Points\"), infoTitleConfig);\n                                CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {\n                                    CLAY_TEXT(CLAY_STRING(\"{ element: \"), infoTextConfig);\n                                    Clay_String attachPointElement = CLAY_STRING(\"LEFT_TOP\");\n                                    if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_LEFT_CENTER) {\n                                        attachPointElement = CLAY_STRING(\"LEFT_CENTER\");\n                                    } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_LEFT_BOTTOM) {\n                                        attachPointElement = CLAY_STRING(\"LEFT_BOTTOM\");\n                                    } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_CENTER_TOP) {\n                                        attachPointElement = CLAY_STRING(\"CENTER_TOP\");\n                                    } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_CENTER_CENTER) {\n                                        attachPointElement = CLAY_STRING(\"CENTER_CENTER\");\n                                    } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_CENTER_BOTTOM) {\n                                        attachPointElement = CLAY_STRING(\"CENTER_BOTTOM\");\n                                    } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_RIGHT_TOP) {\n                                        attachPointElement = CLAY_STRING(\"RIGHT_TOP\");\n                                    } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_RIGHT_CENTER) {\n                                        attachPointElement = CLAY_STRING(\"RIGHT_CENTER\");\n                                    } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_RIGHT_BOTTOM) {\n                                        attachPointElement = CLAY_STRING(\"RIGHT_BOTTOM\");\n                                    }\n                                    CLAY_TEXT(attachPointElement, infoTextConfig);\n                                    Clay_String attachPointParent = CLAY_STRING(\"LEFT_TOP\");\n                                    if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_LEFT_CENTER) {\n                                        attachPointParent = CLAY_STRING(\"LEFT_CENTER\");\n                                    } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_LEFT_BOTTOM) {\n                                        attachPointParent = CLAY_STRING(\"LEFT_BOTTOM\");\n                                    } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_CENTER_TOP) {\n                                        attachPointParent = CLAY_STRING(\"CENTER_TOP\");\n                                    } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_CENTER_CENTER) {\n                                        attachPointParent = CLAY_STRING(\"CENTER_CENTER\");\n                                    } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_CENTER_BOTTOM) {\n                                        attachPointParent = CLAY_STRING(\"CENTER_BOTTOM\");\n                                    } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_RIGHT_TOP) {\n                                        attachPointParent = CLAY_STRING(\"RIGHT_TOP\");\n                                    } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_RIGHT_CENTER) {\n                                        attachPointParent = CLAY_STRING(\"RIGHT_CENTER\");\n                                    } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_RIGHT_BOTTOM) {\n                                        attachPointParent = CLAY_STRING(\"RIGHT_BOTTOM\");\n                                    }\n                                    CLAY_TEXT(CLAY_STRING(\", parent: \"), infoTextConfig);\n                                    CLAY_TEXT(attachPointParent, infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\" }\"), infoTextConfig);\n                                }\n                                // .pointerCaptureMode\n                                CLAY_TEXT(CLAY_STRING(\"Pointer Capture Mode\"), infoTitleConfig);\n                                Clay_String pointerCaptureMode = CLAY_STRING(\"NONE\");\n                                if (floatingConfig->pointerCaptureMode == CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH) {\n                                    pointerCaptureMode = CLAY_STRING(\"PASSTHROUGH\");\n                                }\n                                CLAY_TEXT(pointerCaptureMode, infoTextConfig);\n                                // .attachTo\n                                CLAY_TEXT(CLAY_STRING(\"Attach To\"), infoTitleConfig);\n                                Clay_String attachTo = CLAY_STRING(\"NONE\");\n                                if (floatingConfig->attachTo == CLAY_ATTACH_TO_PARENT) {\n                                    attachTo = CLAY_STRING(\"PARENT\");\n                                } else if (floatingConfig->attachTo == CLAY_ATTACH_TO_ELEMENT_WITH_ID) {\n                                    attachTo = CLAY_STRING(\"ELEMENT_WITH_ID\");\n                                } else if (floatingConfig->attachTo == CLAY_ATTACH_TO_ROOT) {\n                                    attachTo = CLAY_STRING(\"ROOT\");\n                                }\n                                CLAY_TEXT(attachTo, infoTextConfig);\n                                // .clipTo\n                                CLAY_TEXT(CLAY_STRING(\"Clip To\"), infoTitleConfig);\n                                Clay_String clipTo = CLAY_STRING(\"ATTACHED_PARENT\");\n                                if (floatingConfig->clipTo == CLAY_CLIP_TO_NONE) {\n                                    clipTo = CLAY_STRING(\"NONE\");\n                                }\n                                CLAY_TEXT(clipTo, infoTextConfig);\n                            }\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_BORDER: {\n                            Clay_BorderElementConfig *borderConfig = elementConfig->config.borderElementConfig;\n                            CLAY(CLAY_ID(\"Clay__DebugViewElementInfoBorderBody\"), { .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {\n                                CLAY_TEXT(CLAY_STRING(\"Border Widths\"), infoTitleConfig);\n                                CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {\n                                    CLAY_TEXT(CLAY_STRING(\"{ left: \"), infoTextConfig);\n                                    CLAY_TEXT(Clay__IntToString(borderConfig->width.left), infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\", right: \"), infoTextConfig);\n                                    CLAY_TEXT(Clay__IntToString(borderConfig->width.right), infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\", top: \"), infoTextConfig);\n                                    CLAY_TEXT(Clay__IntToString(borderConfig->width.top), infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\", bottom: \"), infoTextConfig);\n                                    CLAY_TEXT(Clay__IntToString(borderConfig->width.bottom), infoTextConfig);\n                                    CLAY_TEXT(CLAY_STRING(\" }\"), infoTextConfig);\n                                }\n                                // .textColor\n                                CLAY_TEXT(CLAY_STRING(\"Border Color\"), infoTitleConfig);\n                                Clay__RenderDebugViewColor(borderConfig->color, infoTextConfig);\n                            }\n                            break;\n                        }\n                        case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM:\n                        default: break;\n                    }\n                }\n            }\n        } else {\n            CLAY(CLAY_ID(\"Clay__DebugViewWarningsScrollPane\"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(300)}, .childGap = 6, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_2, .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {\n                Clay_TextElementConfig *warningConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE });\n                CLAY(CLAY_ID(\"Clay__DebugViewWarningItemHeader\"), { .layout = { .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {\n                    CLAY_TEXT(CLAY_STRING(\"Warnings\"), warningConfig);\n                }\n                CLAY(CLAY_ID(\"Clay__DebugViewWarningsTopBorder\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)} }, .backgroundColor = {200, 200, 200, 255} }) {}\n                int32_t previousWarningsLength = context->warnings.length;\n                for (int32_t i = 0; i < previousWarningsLength; i++) {\n                    Clay__Warning warning = context->warnings.internalArray[i];\n                    CLAY(CLAY_IDI(\"Clay__DebugViewWarningItem\", i), { .layout = { .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {\n                        CLAY_TEXT(warning.baseMessage, warningConfig);\n                        if (warning.dynamicMessage.length > 0) {\n                            CLAY_TEXT(warning.dynamicMessage, warningConfig);\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n#pragma endregion\n\nuint32_t Clay__debugViewWidth = 400;\nClay_Color Clay__debugViewHighlightColor = { 168, 66, 28, 100 };\n\nClay__WarningArray Clay__WarningArray_Allocate_Arena(int32_t capacity, Clay_Arena *arena) {\n    size_t totalSizeBytes = capacity * sizeof(Clay_String);\n    Clay__WarningArray array = {.capacity = capacity, .length = 0};\n    uintptr_t nextAllocOffset = arena->nextAllocation + (64 - (arena->nextAllocation % 64));\n    if (nextAllocOffset + totalSizeBytes <= arena->capacity) {\n        array.internalArray = (Clay__Warning*)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset);\n        arena->nextAllocation = nextAllocOffset + totalSizeBytes;\n    }\n    else {\n        Clay__currentContext->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n            .errorType = CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,\n            .errorText = CLAY_STRING(\"Clay attempted to allocate memory in its arena, but ran out of capacity. Try increasing the capacity of the arena passed to Clay_Initialize()\"),\n            .userData = Clay__currentContext->errorHandler.userData });\n    }\n    return array;\n}\n\nClay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning item)\n{\n    if (array->length < array->capacity) {\n        array->internalArray[array->length++] = item;\n        return &array->internalArray[array->length - 1];\n    }\n    return &CLAY__WARNING_DEFAULT;\n}\n\nvoid* Clay__Array_Allocate_Arena(int32_t capacity, uint32_t itemSize, Clay_Arena *arena)\n{\n    size_t totalSizeBytes = capacity * itemSize;\n    uintptr_t nextAllocOffset = arena->nextAllocation + ((64 - (arena->nextAllocation % 64)) & 63);\n    if (nextAllocOffset + totalSizeBytes <= arena->capacity) {\n        arena->nextAllocation = nextAllocOffset + totalSizeBytes;\n        return (void*)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset);\n    }\n    else {\n        Clay__currentContext->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n                .errorType = CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,\n                .errorText = CLAY_STRING(\"Clay attempted to allocate memory in its arena, but ran out of capacity. Try increasing the capacity of the arena passed to Clay_Initialize()\"),\n                .userData = Clay__currentContext->errorHandler.userData });\n    }\n    return CLAY__NULL;\n}\n\nbool Clay__Array_RangeCheck(int32_t index, int32_t length)\n{\n    if (index < length && index >= 0) {\n        return true;\n    }\n    Clay_Context* context = Clay_GetCurrentContext();\n    context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n            .errorType = CLAY_ERROR_TYPE_INTERNAL_ERROR,\n            .errorText = CLAY_STRING(\"Clay attempted to make an out of bounds array access. This is an internal error and is likely a bug.\"),\n            .userData = context->errorHandler.userData });\n    return false;\n}\n\nbool Clay__Array_AddCapacityCheck(int32_t length, int32_t capacity)\n{\n    if (length < capacity) {\n        return true;\n    }\n    Clay_Context* context = Clay_GetCurrentContext();\n    context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n        .errorType = CLAY_ERROR_TYPE_INTERNAL_ERROR,\n        .errorText = CLAY_STRING(\"Clay attempted to make an out of bounds array access. This is an internal error and is likely a bug.\"),\n        .userData = context->errorHandler.userData });\n    return false;\n}\n\n// PUBLIC API FROM HERE ---------------------------------------\n\nCLAY_WASM_EXPORT(\"Clay_MinMemorySize\")\nuint32_t Clay_MinMemorySize(void) {\n    Clay_Context fakeContext = {\n        .maxElementCount = Clay__defaultMaxElementCount,\n        .maxMeasureTextCacheWordCount = Clay__defaultMaxMeasureTextWordCacheCount,\n        .internalArena = {\n            .capacity = SIZE_MAX,\n            .memory = NULL,\n        }\n    };\n    Clay_Context* currentContext = Clay_GetCurrentContext();\n    if (currentContext) {\n        fakeContext.maxElementCount = currentContext->maxElementCount;\n        fakeContext.maxMeasureTextCacheWordCount = currentContext->maxMeasureTextCacheWordCount;\n    }\n    // Reserve space in the arena for the context, important for calculating min memory size correctly\n    Clay__Context_Allocate_Arena(&fakeContext.internalArena);\n    Clay__InitializePersistentMemory(&fakeContext);\n    Clay__InitializeEphemeralMemory(&fakeContext);\n    return (uint32_t)fakeContext.internalArena.nextAllocation + 128;\n}\n\nCLAY_WASM_EXPORT(\"Clay_CreateArenaWithCapacityAndMemory\")\nClay_Arena Clay_CreateArenaWithCapacityAndMemory(size_t capacity, void *memory) {\n    Clay_Arena arena = {\n        .capacity = capacity,\n        .memory = (char *)memory\n    };\n    return arena;\n}\n\n#ifndef CLAY_WASM\nvoid Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData), void *userData) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    Clay__MeasureText = measureTextFunction;\n    context->measureTextUserData = userData;\n}\nvoid Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId, void *userData), void *userData) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    Clay__QueryScrollOffset = queryScrollOffsetFunction;\n    context->queryScrollOffsetUserData = userData;\n}\n#endif\n\nCLAY_WASM_EXPORT(\"Clay_SetLayoutDimensions\")\nvoid Clay_SetLayoutDimensions(Clay_Dimensions dimensions) {\n    Clay_GetCurrentContext()->layoutDimensions = dimensions;\n}\n\nCLAY_WASM_EXPORT(\"Clay_SetPointerState\")\nvoid Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->booleanWarnings.maxElementsExceeded) {\n        return;\n    }\n    context->pointerInfo.position = position;\n    context->pointerOverIds.length = 0;\n    Clay__int32_tArray dfsBuffer = context->layoutElementChildrenBuffer;\n    for (int32_t rootIndex = context->layoutElementTreeRoots.length - 1; rootIndex >= 0; --rootIndex) {\n        dfsBuffer.length = 0;\n        Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex);\n        Clay__int32_tArray_Add(&dfsBuffer, (int32_t)root->layoutElementIndex);\n        context->treeNodeVisited.internalArray[0] = false;\n        bool found = false;\n        while (dfsBuffer.length > 0) {\n            if (context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) {\n                dfsBuffer.length--;\n                continue;\n            }\n            context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;\n            Clay_LayoutElement *currentElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&dfsBuffer, (int)dfsBuffer.length - 1));\n            Clay_LayoutElementHashMapItem *mapItem = Clay__GetHashMapItem(currentElement->id); // TODO think of a way around this, maybe the fact that it's essentially a binary tree limits the cost, but the worst case is not great\n            int32_t clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(currentElement - context->layoutElements.internalArray));\n            Clay_LayoutElementHashMapItem *clipItem = Clay__GetHashMapItem(clipElementId);\n            if (mapItem) {\n                Clay_BoundingBox elementBox = mapItem->boundingBox;\n                elementBox.x -= root->pointerOffset.x;\n                elementBox.y -= root->pointerOffset.y;\n                if ((Clay__PointIsInsideRect(position, elementBox)) && (clipElementId == 0 || (Clay__PointIsInsideRect(position, clipItem->boundingBox)) || context->externalScrollHandlingEnabled)) {\n                    if (mapItem->onHoverFunction) {\n                        mapItem->onHoverFunction(mapItem->elementId, context->pointerInfo, mapItem->hoverFunctionUserData);\n                    }\n                    Clay_ElementIdArray_Add(&context->pointerOverIds, mapItem->elementId);\n                    found = true;\n                }\n                if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) {\n                    dfsBuffer.length--;\n                    continue;\n                }\n                for (int32_t i = currentElement->childrenOrTextContent.children.length - 1; i >= 0; --i) {\n                    Clay__int32_tArray_Add(&dfsBuffer, currentElement->childrenOrTextContent.children.elements[i]);\n                    context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = false; // TODO needs to be ranged checked\n                }\n            } else {\n                dfsBuffer.length--;\n            }\n        }\n\n        Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&context->layoutElements, root->layoutElementIndex);\n        if (found && Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING) &&\n                Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING).floatingElementConfig->pointerCaptureMode == CLAY_POINTER_CAPTURE_MODE_CAPTURE) {\n            break;\n        }\n    }\n\n    if (isPointerDown) {\n        if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n            context->pointerInfo.state = CLAY_POINTER_DATA_PRESSED;\n        } else if (context->pointerInfo.state != CLAY_POINTER_DATA_PRESSED) {\n            context->pointerInfo.state = CLAY_POINTER_DATA_PRESSED_THIS_FRAME;\n        }\n    } else {\n        if (context->pointerInfo.state == CLAY_POINTER_DATA_RELEASED_THIS_FRAME) {\n            context->pointerInfo.state = CLAY_POINTER_DATA_RELEASED;\n        } else if (context->pointerInfo.state != CLAY_POINTER_DATA_RELEASED)  {\n            context->pointerInfo.state = CLAY_POINTER_DATA_RELEASED_THIS_FRAME;\n        }\n    }\n}\n\nCLAY_WASM_EXPORT(\"Clay_Initialize\")\nClay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler) {\n    // Cacheline align memory passed in\n    uintptr_t baseOffset = 64 - ((uintptr_t)arena.memory % 64);\n    baseOffset = baseOffset == 64 ? 0 : baseOffset;\n    arena.memory += baseOffset;\n    Clay_Context *context = Clay__Context_Allocate_Arena(&arena);\n    if (context == NULL) return NULL;\n    // DEFAULTS\n    Clay_Context *oldContext = Clay_GetCurrentContext();\n    *context = CLAY__INIT(Clay_Context) {\n        .maxElementCount = oldContext ? oldContext->maxElementCount : Clay__defaultMaxElementCount,\n        .maxMeasureTextCacheWordCount = oldContext ? oldContext->maxMeasureTextCacheWordCount : Clay__defaultMaxMeasureTextWordCacheCount,\n        .errorHandler = errorHandler.errorHandlerFunction ? errorHandler : CLAY__INIT(Clay_ErrorHandler) { Clay__ErrorHandlerFunctionDefault, 0 },\n        .layoutDimensions = layoutDimensions,\n        .internalArena = arena,\n    };\n    Clay_SetCurrentContext(context);\n    Clay__InitializePersistentMemory(context);\n    Clay__InitializeEphemeralMemory(context);\n    for (int32_t i = 0; i < context->layoutElementsHashMap.capacity; ++i) {\n        context->layoutElementsHashMap.internalArray[i] = -1;\n    }\n    for (int32_t i = 0; i < context->measureTextHashMap.capacity; ++i) {\n        context->measureTextHashMap.internalArray[i] = 0;\n    }\n    context->measureTextHashMapInternal.length = 1; // Reserve the 0 value to mean \"no next element\"\n    context->layoutDimensions = layoutDimensions;\n    return context;\n}\n\nCLAY_WASM_EXPORT(\"Clay_GetCurrentContext\")\nClay_Context* Clay_GetCurrentContext(void) {\n    return Clay__currentContext;\n}\n\nCLAY_WASM_EXPORT(\"Clay_SetCurrentContext\")\nvoid Clay_SetCurrentContext(Clay_Context* context) {\n    Clay__currentContext = context;\n}\n\nCLAY_WASM_EXPORT(\"Clay_GetScrollOffset\")\nClay_Vector2 Clay_GetScrollOffset(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->booleanWarnings.maxElementsExceeded) {\n        return CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT;\n    }\n    Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();\n    // If the element has no id attached at this point, we need to generate one\n    if (openLayoutElement->id == 0) {\n        Clay__GenerateIdForAnonymousElement(openLayoutElement);\n    }\n    for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {\n        Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);\n        if (mapping->layoutElement == openLayoutElement) {\n            return mapping->scrollPosition;\n        }\n    }\n    return CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT;\n}\n\nCLAY_WASM_EXPORT(\"Clay_UpdateScrollContainers\")\nvoid Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    bool isPointerActive = enableDragScrolling && (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED || context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME);\n    // Don't apply scroll events to ancestors of the inner element\n    int32_t highestPriorityElementIndex = -1;\n    Clay__ScrollContainerDataInternal *highestPriorityScrollData = CLAY__NULL;\n    for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {\n        Clay__ScrollContainerDataInternal *scrollData = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);\n        if (!scrollData->openThisFrame) {\n            Clay__ScrollContainerDataInternalArray_RemoveSwapback(&context->scrollContainerDatas, i);\n            continue;\n        }\n        scrollData->openThisFrame = false;\n        Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(scrollData->elementId);\n        // Element isn't rendered this frame but scroll offset has been retained\n        if (!hashMapItem) {\n            Clay__ScrollContainerDataInternalArray_RemoveSwapback(&context->scrollContainerDatas, i);\n            continue;\n        }\n\n        // Touch / click is released\n        if (!isPointerActive && scrollData->pointerScrollActive) {\n            float xDiff = scrollData->scrollPosition.x - scrollData->scrollOrigin.x;\n            if (xDiff < -10 || xDiff > 10) {\n                scrollData->scrollMomentum.x = (scrollData->scrollPosition.x - scrollData->scrollOrigin.x) / (scrollData->momentumTime * 25);\n            }\n            float yDiff = scrollData->scrollPosition.y - scrollData->scrollOrigin.y;\n            if (yDiff < -10 || yDiff > 10) {\n                scrollData->scrollMomentum.y = (scrollData->scrollPosition.y - scrollData->scrollOrigin.y) / (scrollData->momentumTime * 25);\n            }\n            scrollData->pointerScrollActive = false;\n\n            scrollData->pointerOrigin = CLAY__INIT(Clay_Vector2){0,0};\n            scrollData->scrollOrigin = CLAY__INIT(Clay_Vector2){0,0};\n            scrollData->momentumTime = 0;\n        }\n\n        // Apply existing momentum\n        scrollData->scrollPosition.x += scrollData->scrollMomentum.x;\n        scrollData->scrollMomentum.x *= 0.95f;\n        bool scrollOccurred = scrollDelta.x != 0 || scrollDelta.y != 0;\n        if ((scrollData->scrollMomentum.x > -0.1f && scrollData->scrollMomentum.x < 0.1f) || scrollOccurred) {\n            scrollData->scrollMomentum.x = 0;\n        }\n        scrollData->scrollPosition.x = CLAY__MIN(CLAY__MAX(scrollData->scrollPosition.x, -(CLAY__MAX(scrollData->contentSize.width - scrollData->layoutElement->dimensions.width, 0))), 0);\n\n        scrollData->scrollPosition.y += scrollData->scrollMomentum.y;\n        scrollData->scrollMomentum.y *= 0.95f;\n        if ((scrollData->scrollMomentum.y > -0.1f && scrollData->scrollMomentum.y < 0.1f) || scrollOccurred) {\n            scrollData->scrollMomentum.y = 0;\n        }\n        scrollData->scrollPosition.y = CLAY__MIN(CLAY__MAX(scrollData->scrollPosition.y, -(CLAY__MAX(scrollData->contentSize.height - scrollData->layoutElement->dimensions.height, 0))), 0);\n\n        for (int32_t j = 0; j < context->pointerOverIds.length; ++j) { // TODO n & m are small here but this being n*m gives me the creeps\n            if (scrollData->layoutElement->id == Clay_ElementIdArray_Get(&context->pointerOverIds, j)->id) {\n                highestPriorityElementIndex = j;\n                highestPriorityScrollData = scrollData;\n            }\n        }\n    }\n\n    if (highestPriorityElementIndex > -1 && highestPriorityScrollData) {\n        Clay_LayoutElement *scrollElement = highestPriorityScrollData->layoutElement;\n        Clay_ClipElementConfig *clipConfig = Clay__FindElementConfigWithType(scrollElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;\n        bool canScrollVertically = clipConfig->vertical && highestPriorityScrollData->contentSize.height > scrollElement->dimensions.height;\n        bool canScrollHorizontally = clipConfig->horizontal && highestPriorityScrollData->contentSize.width > scrollElement->dimensions.width;\n        // Handle wheel scroll\n        if (canScrollVertically) {\n            highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollPosition.y + scrollDelta.y * 10;\n        }\n        if (canScrollHorizontally) {\n            highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollPosition.x + scrollDelta.x * 10;\n        }\n        // Handle click / touch scroll\n        if (isPointerActive) {\n            highestPriorityScrollData->scrollMomentum = CLAY__INIT(Clay_Vector2)CLAY__DEFAULT_STRUCT;\n            if (!highestPriorityScrollData->pointerScrollActive) {\n                highestPriorityScrollData->pointerOrigin = context->pointerInfo.position;\n                highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition;\n                highestPriorityScrollData->pointerScrollActive = true;\n            } else {\n                float scrollDeltaX = 0, scrollDeltaY = 0;\n                if (canScrollHorizontally) {\n                    float oldXScrollPosition = highestPriorityScrollData->scrollPosition.x;\n                    highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollOrigin.x + (context->pointerInfo.position.x - highestPriorityScrollData->pointerOrigin.x);\n                    highestPriorityScrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.x, 0), -(highestPriorityScrollData->contentSize.width - highestPriorityScrollData->boundingBox.width));\n                    scrollDeltaX = highestPriorityScrollData->scrollPosition.x - oldXScrollPosition;\n                }\n                if (canScrollVertically) {\n                    float oldYScrollPosition = highestPriorityScrollData->scrollPosition.y;\n                    highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollOrigin.y + (context->pointerInfo.position.y - highestPriorityScrollData->pointerOrigin.y);\n                    highestPriorityScrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.y, 0), -(highestPriorityScrollData->contentSize.height - highestPriorityScrollData->boundingBox.height));\n                    scrollDeltaY = highestPriorityScrollData->scrollPosition.y - oldYScrollPosition;\n                }\n                if (scrollDeltaX > -0.1f && scrollDeltaX < 0.1f && scrollDeltaY > -0.1f && scrollDeltaY < 0.1f && highestPriorityScrollData->momentumTime > 0.15f) {\n                    highestPriorityScrollData->momentumTime = 0;\n                    highestPriorityScrollData->pointerOrigin = context->pointerInfo.position;\n                    highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition;\n                } else {\n                     highestPriorityScrollData->momentumTime += deltaTime;\n                }\n            }\n        }\n        // Clamp any changes to scroll position to the maximum size of the contents\n        if (canScrollVertically) {\n            highestPriorityScrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.y, 0), -(highestPriorityScrollData->contentSize.height - scrollElement->dimensions.height));\n        }\n        if (canScrollHorizontally) {\n            highestPriorityScrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.x, 0), -(highestPriorityScrollData->contentSize.width - scrollElement->dimensions.width));\n        }\n    }\n}\n\nCLAY_WASM_EXPORT(\"Clay_BeginLayout\")\nvoid Clay_BeginLayout(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    Clay__InitializeEphemeralMemory(context);\n    context->generation++;\n    context->dynamicElementIndex = 0;\n    // Set up the root container that covers the entire window\n    Clay_Dimensions rootDimensions = {context->layoutDimensions.width, context->layoutDimensions.height};\n    if (context->debugModeEnabled) {\n        rootDimensions.width -= (float)Clay__debugViewWidth;\n    }\n    context->booleanWarnings = CLAY__INIT(Clay_BooleanWarnings) CLAY__DEFAULT_STRUCT;\n    Clay__OpenElementWithId(CLAY_ID(\"Clay__RootContainer\"));\n    Clay__ConfigureOpenElement(CLAY__INIT(Clay_ElementDeclaration) {\n        .layout = { .sizing = {CLAY_SIZING_FIXED((rootDimensions.width)), CLAY_SIZING_FIXED(rootDimensions.height)} }\n    });\n    Clay__int32_tArray_Add(&context->openLayoutElementStack, 0);\n    Clay__LayoutElementTreeRootArray_Add(&context->layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { .layoutElementIndex = 0 });\n}\n\nCLAY_WASM_EXPORT(\"Clay_EndLayout\")\nClay_RenderCommandArray Clay_EndLayout(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    Clay__CloseElement();\n    bool elementsExceededBeforeDebugView = context->booleanWarnings.maxElementsExceeded;\n    if (context->debugModeEnabled && !elementsExceededBeforeDebugView) {\n        context->warningsEnabled = false;\n        Clay__RenderDebugView();\n        context->warningsEnabled = true;\n    }\n    if (context->booleanWarnings.maxElementsExceeded) {\n        Clay_String message;\n        if (!elementsExceededBeforeDebugView) {\n            message = CLAY_STRING(\"Clay Error: Layout elements exceeded Clay__maxElementCount after adding the debug-view to the layout.\");\n        } else {\n            message = CLAY_STRING(\"Clay Error: Layout elements exceeded Clay__maxElementCount\");\n        }\n        Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand ) {\n            .boundingBox = { context->layoutDimensions.width / 2 - 59 * 4, context->layoutDimensions.height / 2, 0, 0 },\n            .renderData = { .text = { .stringContents = CLAY__INIT(Clay_StringSlice) { .length = message.length, .chars = message.chars, .baseChars = message.chars }, .textColor = {255, 0, 0, 255}, .fontSize = 16 } },\n            .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT\n        });\n    }\n    if (context->openLayoutElementStack.length > 1) {\n        context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {\n                .errorType = CLAY_ERROR_TYPE_UNBALANCED_OPEN_CLOSE,\n                .errorText = CLAY_STRING(\"There were still open layout elements when EndLayout was called. This results from an unequal number of calls to Clay__OpenElement and Clay__CloseElement.\"),\n                .userData = context->errorHandler.userData });\n    }\n    Clay__CalculateFinalLayout();\n    return context->renderCommands;\n}\n\nCLAY_WASM_EXPORT(\"Clay_GetElementId\")\nClay_ElementId Clay_GetElementId(Clay_String idString) {\n    return Clay__HashString(idString, 0);\n}\n\nCLAY_WASM_EXPORT(\"Clay_GetElementIdWithIndex\")\nClay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index) {\n    return Clay__HashStringWithOffset(idString, index, 0);\n}\n\nbool Clay_Hovered(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->booleanWarnings.maxElementsExceeded) {\n        return false;\n    }\n    Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();\n    // If the element has no id attached at this point, we need to generate one\n    if (openLayoutElement->id == 0) {\n        Clay__GenerateIdForAnonymousElement(openLayoutElement);\n    }\n    for (int32_t i = 0; i < context->pointerOverIds.length; ++i) {\n        if (Clay_ElementIdArray_Get(&context->pointerOverIds, i)->id == openLayoutElement->id) {\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData), void *userData) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context->booleanWarnings.maxElementsExceeded) {\n        return;\n    }\n    Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();\n    if (openLayoutElement->id == 0) {\n        Clay__GenerateIdForAnonymousElement(openLayoutElement);\n    }\n    Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(openLayoutElement->id);\n    hashMapItem->onHoverFunction = onHoverFunction;\n    hashMapItem->hoverFunctionUserData = userData;\n}\n\nCLAY_WASM_EXPORT(\"Clay_PointerOver\")\nbool Clay_PointerOver(Clay_ElementId elementId) { // TODO return priority for separating multiple results\n    Clay_Context* context = Clay_GetCurrentContext();\n    for (int32_t i = 0; i < context->pointerOverIds.length; ++i) {\n        if (Clay_ElementIdArray_Get(&context->pointerOverIds, i)->id == elementId.id) {\n            return true;\n        }\n    }\n    return false;\n}\n\nCLAY_WASM_EXPORT(\"Clay_GetScrollContainerData\")\nClay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    for (int32_t i = 0; i < context->scrollContainerDatas.length; ++i) {\n        Clay__ScrollContainerDataInternal *scrollContainerData = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);\n        if (scrollContainerData->elementId == id.id) {\n            Clay_ClipElementConfig *clipElementConfig = Clay__FindElementConfigWithType(scrollContainerData->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;\n            if (!clipElementConfig) { // This can happen on the first frame before a scroll container is declared\n                return CLAY__INIT(Clay_ScrollContainerData) CLAY__DEFAULT_STRUCT;\n            }\n            return CLAY__INIT(Clay_ScrollContainerData) {\n                .scrollPosition = &scrollContainerData->scrollPosition,\n                .scrollContainerDimensions = { scrollContainerData->boundingBox.width, scrollContainerData->boundingBox.height },\n                .contentDimensions = scrollContainerData->contentSize,\n                .config = *clipElementConfig,\n                .found = true\n            };\n        }\n    }\n    return CLAY__INIT(Clay_ScrollContainerData) CLAY__DEFAULT_STRUCT;\n}\n\nCLAY_WASM_EXPORT(\"Clay_GetElementData\")\nClay_ElementData Clay_GetElementData(Clay_ElementId id){\n    Clay_LayoutElementHashMapItem * item = Clay__GetHashMapItem(id.id);\n    if(item == &Clay_LayoutElementHashMapItem_DEFAULT) {\n        return CLAY__INIT(Clay_ElementData) CLAY__DEFAULT_STRUCT;\n    }\n\n    return CLAY__INIT(Clay_ElementData){\n        .boundingBox = item->boundingBox,\n        .found = true\n    };\n}\n\nCLAY_WASM_EXPORT(\"Clay_SetDebugModeEnabled\")\nvoid Clay_SetDebugModeEnabled(bool enabled) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    context->debugModeEnabled = enabled;\n}\n\nCLAY_WASM_EXPORT(\"Clay_IsDebugModeEnabled\")\nbool Clay_IsDebugModeEnabled(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    return context->debugModeEnabled;\n}\n\nCLAY_WASM_EXPORT(\"Clay_SetCullingEnabled\")\nvoid Clay_SetCullingEnabled(bool enabled) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    context->disableCulling = !enabled;\n}\n\nCLAY_WASM_EXPORT(\"Clay_SetExternalScrollHandlingEnabled\")\nvoid Clay_SetExternalScrollHandlingEnabled(bool enabled) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    context->externalScrollHandlingEnabled = enabled;\n}\n\nCLAY_WASM_EXPORT(\"Clay_GetMaxElementCount\")\nint32_t Clay_GetMaxElementCount(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    return context->maxElementCount;\n}\n\nCLAY_WASM_EXPORT(\"Clay_SetMaxElementCount\")\nvoid Clay_SetMaxElementCount(int32_t maxElementCount) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context) {\n        context->maxElementCount = maxElementCount;\n    } else {\n        Clay__defaultMaxElementCount = maxElementCount; // TODO: Fix this\n        Clay__defaultMaxMeasureTextWordCacheCount = maxElementCount * 2;\n    }\n}\n\nCLAY_WASM_EXPORT(\"Clay_GetMaxMeasureTextCacheWordCount\")\nint32_t Clay_GetMaxMeasureTextCacheWordCount(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    return context->maxMeasureTextCacheWordCount;\n}\n\nCLAY_WASM_EXPORT(\"Clay_SetMaxMeasureTextCacheWordCount\")\nvoid Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    if (context) {\n        Clay__currentContext->maxMeasureTextCacheWordCount = maxMeasureTextCacheWordCount;\n    } else {\n        Clay__defaultMaxMeasureTextWordCacheCount = maxMeasureTextCacheWordCount; // TODO: Fix this\n    }\n}\n\nCLAY_WASM_EXPORT(\"Clay_ResetMeasureTextCache\")\nvoid Clay_ResetMeasureTextCache(void) {\n    Clay_Context* context = Clay_GetCurrentContext();\n    context->measureTextHashMapInternal.length = 0;\n    context->measureTextHashMapInternalFreeList.length = 0;\n    context->measureTextHashMap.length = 0;\n    context->measuredWords.length = 0;\n    context->measuredWordsFreeList.length = 0;\n    \n    for (int32_t i = 0; i < context->measureTextHashMap.capacity; ++i) {\n        context->measureTextHashMap.internalArray[i] = 0;\n    }\n    context->measureTextHashMapInternal.length = 1; // Reserve the 0 value to mean \"no next element\"\n}\n\n#endif // CLAY_IMPLEMENTATION\n\n/*\nLICENSE\nzlib/libpng license\n\nCopyright (c) 2024 Nic Barker\n\nThis software is provided 'as-is', without any express or implied warranty.\nIn no event will the authors be held liable for any damages arising from the\nuse of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n    1. The origin of this software must not be misrepresented; you must not\n    claim that you wrote the original software. If you use this software in a\n    product, an acknowledgment in the product documentation would be\n    appreciated but is not required.\n\n    2. Altered source versions must be plainly marked as such, and must not\n    be misrepresented as being the original software.\n\n    3. This notice may not be removed or altered from any source\n    distribution.\n*/\n"
  },
  {
    "path": "cmake/FindCairo.cmake",
    "content": "# Defines:\n#  CAIRO_FOUND        - System has Cairo\n#  CAIRO_INCLUDE_DIRS - Cairo include directories\n#  CAIRO_LIBRARY      - Cairo library\n#  Cairo::Cairo       - Imported target\n\nfind_path(CAIRO_INCLUDE_DIRS\n        NAMES cairo/cairo.h\n        PATHS ${CAIRO_ROOT_DIR}\n        PATH_SUFFIXES include\n)\n\nfind_library(CAIRO_LIBRARY\n        NAMES cairo\n        PATHS ${CAIRO_ROOT_DIR}\n        PATH_SUFFIXES lib lib64\n)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(Cairo\n        REQUIRED_VARS CAIRO_LIBRARY CAIRO_INCLUDE_DIRS\n)\n\nif(Cairo_FOUND AND NOT TARGET Cairo::Cairo)\n    add_library(Cairo::Cairo UNKNOWN IMPORTED)\n    set_target_properties(Cairo::Cairo PROPERTIES\n            IMPORTED_LOCATION \"${CAIRO_LIBRARY}\"\n            INTERFACE_INCLUDE_DIRECTORIES \"${CAIRO_INCLUDE_DIRS}\"\n    )\nendif()\n\nmark_as_advanced(CAIRO_INCLUDE_DIRS CAIRO_LIBRARY)"
  },
  {
    "path": "examples/GLES3-GLFW-video-demo/.gitignore",
    "content": "/build/\n/website-demo-macos-glfw*\n"
  },
  {
    "path": "examples/GLES3-GLFW-video-demo/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(GLES3_GLFW_video_demo C)\n\nset(CMAKE_C_STANDARD 99)\nset(CMAKE_C_STANDARD_REQUIRED ON)\n\n# -------------------------------------------------\n# FetchContent\n# -------------------------------------------------\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\n\n# -------------------------------------------------\n# STB (header-only)\n# -------------------------------------------------\nFetchContent_Declare(\n    stb\n    GIT_REPOSITORY https://github.com/nothings/stb.git\n    GIT_TAG master\n)\nFetchContent_MakeAvailable(stb)\n\n# -------------------------------------------------\n# GLFW\n# -------------------------------------------------\nFetchContent_Declare(\n    glfw\n    GIT_REPOSITORY https://github.com/glfw/glfw.git\n    GIT_TAG 3.4\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(glfw)\n\n# Disable examples/tests/docs (important)\nset(GLFW_BUILD_EXAMPLES OFF CACHE BOOL \"\" FORCE)\nset(GLFW_BUILD_TESTS OFF CACHE BOOL \"\" FORCE)\nset(GLFW_BUILD_DOCS OFF CACHE BOOL \"\" FORCE)\n\n# -------------------------------------------------\n# Executable\n# -------------------------------------------------\nadd_executable(GLES3_GLFW_video_demo\n    main.c\n)\n\ntarget_include_directories(GLES3_GLFW_video_demo\n    PUBLIC\n        .\n        ../..\n        ${stb_SOURCE_DIR}\n)\n\n# -------------------------------------------------\n# Link libraries\n# -------------------------------------------------\ntarget_link_libraries(GLES3_GLFW_video_demo\n    PRIVATE\n        glfw\n)\n\n# -------------------------------------------------\n# Platform-specific OpenGL / GLES\n# -------------------------------------------------\nif(APPLE)\n    find_library(OPENGL_FRAMEWORK OpenGL)\n    target_link_libraries(GLES3_GLFW_video_demo PRIVATE ${OPENGL_FRAMEWORK})\n\n    # Needed for GLFW on macOS\n    target_link_libraries(GLES3_GLFW_video_demo\n        PRIVATE\n            \"-framework Cocoa\"\n            \"-framework IOKit\"\n            \"-framework CoreVideo\"\n    )\nelseif(WIN32)\n    target_link_libraries(GLES3_GLFW_video_demo PRIVATE opengl32)\nelseif(UNIX)\n    target_link_libraries(GLES3_GLFW_video_demo PRIVATE GL)\nendif()\n\n# -------------------------------------------------\n# Build flags (kept minimal)\n# -------------------------------------------------\nif(MSVC)\n    target_compile_options(GLES3_GLFW_video_demo PRIVATE /W3)\nelse()\n    target_compile_options(GLES3_GLFW_video_demo PRIVATE -Wall -Wextra)\nendif()\n\n# -------------------------------------------------\n# Copy resources\n# -------------------------------------------------\nadd_custom_command(\n    TARGET GLES3_GLFW_video_demo POST_BUILD\n    COMMAND ${CMAKE_COMMAND} -E copy_directory\n            ${CMAKE_CURRENT_SOURCE_DIR}/resources\n            ${CMAKE_CURRENT_BINARY_DIR}/resources\n)\n"
  },
  {
    "path": "examples/GLES3-GLFW-video-demo/Makefile.emscripten",
    "content": "# vim: set tabstop=4 shiftwidth=4 expandtab noexpandtab:\n\n#\n# PROGRAM COMPONENTS\n# \n\nCXX = emcc\n\nCXXFLAGS = -std=c99\nCXXFLAGS += -O0\nCXXFLAGS += -I../..\nCXXFLAGS += -I./build/_deps/stb-src\n\nLDLIBS += -s USE_ZLIB=1\nLDLIBS += -s USE_GLFW=3\nLDLIBS += -s FULL_ES2=1 -s USE_WEBGL2=1\nLDLIBS += -s ALLOW_MEMORY_GROWTH=1 -s GL_UNSAFE_OPTS=0\nLDLIBS += -s STACK_SIZE=2048kb\nLDLIBS += -s EXPORTED_FUNCTIONS=['_main']\nLDLIBS += -s ASSERTIONS=1 -s SAFE_HEAP=1\nLDLIBS += --preload-file $(PWD)/resources/Roboto-Regular.ttf@resources/Roboto-Regular.ttf\n\nmain:\n\tmkdir -p build/emscripten\n\ttime $(CXX) $(CXXFLAGS) \\\n\t\t$(PWD)/main.c \\\n\t    $(LDLIBS) -o build/emscripten/index.html \n\ntest:\n\tmake -f Makefile.emscripten main \\\n\t\t&& (cd build/emscripten && python3 -mhttp.server)\n\t\n.PHONY: main \n"
  },
  {
    "path": "examples/GLES3-GLFW-video-demo/Makefile.macos",
    "content": "# vim: set tabstop=4 shiftwidth=4 expandtab noexpandtab:\n\nCXX = clang\n\nCXXFLAGS = -std=c99\nCXXFLAGS += -g -O0 -fno-omit-frame-pointer\nCXXFLAGS += -ferror-limit=1\nCXXFLAGS += -I../..\nCXXFLAGS += -I./build/_deps/stb-src\nCXXFLAGS += -DGL_SILENCE_DEPRECATION\n\n# GLFW (Homebrew)\nCXXFLAGS += -I$(shell brew --prefix glfw)/include\nLDLIBS   += -L$(shell brew --prefix glfw)/lib -lglfw\n\n\n# macOS system frameworks (OpenGL needs these)\nLDLIBS += -framework OpenGL\nLDLIBS += -framework Cocoa\nLDLIBS += -framework IOKit\nLDLIBS += -framework CoreVideo\n\nmain:\n\tmkdir -p build\n\n\ttime $(CXX) $(CXXFLAGS) \\\n\t\t$(PWD)/main.c \\\n\t\t$(LDLIBS) \\\n\t\t-o build/website-demo-macos-glfw"
  },
  {
    "path": "examples/GLES3-GLFW-video-demo/README.md",
    "content": "GLES3 Renderer Video Demo (Using GLFW)\n======================================\n\nThis directory contains a standard Video-Demo example\nusing work-in-progress GLES3 renderer.\nWhile it still needs refinement, the renderer is already functional and demonstrates the core rendering pipeline.\n\nCurrent features\n\n-\tSupports all draw commands except custom.\n-\tIn the best-case scenario (no clipping):\n-\tAll quad-based commands (Rectangle, Image, Border) are rendered in a single draw call.\n-\tAll glyphs belonging to the same font are rendered in one instanced draw call.\n-\tWhen clipping (scissoring) is used:\n-\tThe renderer flushes draw calls before and after each scissor region.\n-\tSupports up to 4 fonts and 4 image textures.\n-\tImage textures may also be used as texture atlases.\n-\tCustom UserData provides per-image UV coordinates, allowing multiple images to share a single OpenGL texture.\n-\tUses stb_image.h and stb_truetype.h as single-header dependencies for asset loading.\n-\tThe loading layer is modular and can be replaced with a different asset pipeline if needed.\n\nCurrently builds on:\n-\tEmscripten\n-\tclang++ / macOS\n-\tCMake support is not available yet.\n\nWindowing and platform support\n\nThis example uses GLFW, and the renderer is framework agnostic\n\nHow to build it with CMake?\n---------------------------\n\nCmake build is the easiest way to build it:\n\n    mkdir build\n    cmake -S . -B ./build\n\nHow to build and run on Emscripten:\n----------------------------------\n\nFor Emscripten the build is a bit custom,\nbut it depends on CMakeBuild to install header-stb dependency.\nSo you still need to build it with CMake first.\n\nAnd then you have to source the Emscripten SDK:\n\n    source /path/to/emscripten/emsdk/emsdk_env.sh\n\nThen build it with hand-crafted Makefile.emscripten:\n\n    make -f Makefile.emscripten test\n\nand then navigate to http://localhost:8080\n\n"
  },
  {
    "path": "examples/GLES3-GLFW-video-demo/main.c",
    "content": "#include <stdio.h>\n\n#include <GLFW/glfw3.h>\n\n#define STB_IMAGE_IMPLEMENTATION\n#define STB_TRUETYPE_IMPLEMENTATION\n#define CLAY_IMPLEMENTATION\n#define CLAY_RENDERER_GLES3_IMPLEMENTATION\n\n#include <clay.h>\n\n#include \"../../renderers/GLES3/clay_renderer_gles3.h\"\n#include \"../shared-layouts/clay-video-demo.c\"\n#include \"../../renderers/GLES3/clay_renderer_gles3_loader_stb.c\"\n\ntypedef struct VideoCtx\n{\n    int shouldContinue;\n    GLFWwindow *glfwWindow;\n    int screenWidth, screenHeight;\n} VideoCtx;\n\nVideoCtx g_ctx;\n\nstatic int initVideo(VideoCtx *ctx, const int initialWidth, const int initialHeight)\n{\n    if (!glfwInit())\n    {\n        fprintf(stderr, \"Failed to init GLFW\\n\");\n        return 0;\n    }\n\n    glfwDefaultWindowHints();\n\tglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n\tglfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);\n\tglfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);\n\tglfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n\tglfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);\n\tglfwWindowHint(GLFW_SAMPLES, 4); // enable multisampling\n    \n    // NO solution for high DPI yet\n    glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);\n\n    g_ctx.glfwWindow = glfwCreateWindow(initialWidth, initialHeight, \"GLES3 GLFW Video Demo\", NULL, NULL);\n\n    if (g_ctx.glfwWindow == NULL)\n    {\n        fprintf(stderr, \"Failed to create GLFW window\\n\");\n        glfwTerminate();\n        return 0;\n    }\n    glfwMakeContextCurrent(g_ctx.glfwWindow);\n\n    glfwGetWindowSize(g_ctx.glfwWindow, &g_ctx.screenWidth, &g_ctx.screenHeight);\n    glViewport(0, 0, g_ctx.screenWidth, g_ctx.screenHeight);\n    printf(\"Frame buffer size %dx%d\\n\", g_ctx.screenWidth, g_ctx.screenHeight);\n\n\n    glEnable(GL_BLEND);\n    // Enables blending, which allows transparent textures to be rendered properly.\n    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n    // Sets the blending function.\n    // - `GL_SRC_ALPHA`: Uses the alpha value of the source (texture or color).\n    // - `GL_ONE_MINUS_SRC_ALPHA`: Makes the destination color blend with the background based on alpha.\n    // This is commonly used for standard transparency effects.\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    glEnable(GL_DEPTH_TEST);\n    // Enables depth testing, ensuring that objects closer to the camera are drawn in front of those farther away.\n    // This prevents objects from rendering incorrectly based on draw order.\n\n    return 1;\n}\n\nvoid My_ErrorHandler(Clay_ErrorData errorData)\n{\n    printf(\"[ClaY ErroR] %s\", errorData.errorText.chars);\n}\n\nStb_FontData g_stbFonts[MAX_FONTS]; // Fonts userData\nGles3_Renderer g_gles3;             // The renderer itself\n\nstatic Clay_Vector2 g_scrollDelta = {0.0f, 0.0f};\nstatic double g_lastTime = 0.0;\nstatic double g_deltaTime = 0.0;\nstatic void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)\n{\n    g_scrollDelta.x += (float)xoffset;\n    g_scrollDelta.y += (float)yoffset;\n}\n\nvoid init()\n{\n    size_t clayRequiredMemory = Clay_MinMemorySize();\n    g_gles3.clayMemory = (Clay_Arena){\n        .capacity = clayRequiredMemory,\n        .memory = (char *)malloc(clayRequiredMemory),\n    };\n    Clay_Context *clayCtx = Clay_Initialize(\n        g_gles3.clayMemory,\n        (Clay_Dimensions){\n            .width = (float)g_ctx.screenWidth,\n            .height = (float)g_ctx.screenHeight,\n        },\n        (Clay_ErrorHandler){\n            .errorHandlerFunction = My_ErrorHandler,\n        });\n\n    // Note that MeasureText has to be set after the Context is set!\n    Clay_SetCurrentContext(clayCtx);\n    Clay_SetMeasureTextFunction(Stb_MeasureText, &g_stbFonts);\n    // This example uses stb loader, but you can inject your custom loader\n    // to load Images and Fonts if you don't want to use STB library\n    Gles3_SetRenderTextFunction(&g_gles3, Stb_RenderText, &g_stbFonts);\n\n    Gles3_Initialize(&g_gles3, 4096);\n\n    int atlasW = 1024;\n    int atlasH = 1024;\n    if (!Stb_LoadFont(\n            &g_gles3.fontTextures[0],\n            &g_stbFonts[0],\n            \"resources/Roboto-Regular.ttf\",\n            24.0f, // bake pixel height\n            atlasW,\n            atlasH))\n        abort();\n\n    Clay_SetDebugModeEnabled(true);\n    glfwSetScrollCallback(g_ctx.glfwWindow, scroll_callback);\n}\n\n\nvoid loop()\n{\n    Clay_Vector2 scrollDelta = {0.0f, 0.0f};\n\n    glfwPollEvents();\n\n    /* Quit handling */\n    if (glfwWindowShouldClose(g_ctx.glfwWindow))\n    {\n        g_ctx.shouldContinue = false;\n    }\n\n    /* Consume scroll delta (accumulated via callback) */\n    scrollDelta = g_scrollDelta;\n    g_scrollDelta.x = 0.0f;\n    g_scrollDelta.y = 0.0f;\n\n    /* Delta time (milliseconds, like your SDL version) */\n    double now = glfwGetTime(); // seconds\n    g_deltaTime = (now - g_lastTime) * 1000.0;\n    g_lastTime = now;\n\n    double mouseX = 0.0;\n    double mouseY = 0.0;\n    glfwGetCursorPos(g_ctx.glfwWindow, &mouseX, &mouseY);\n\n    Clay_Vector2 mousePosition = {\n        (float)mouseX,\n        (float)mouseY\n    };\n\n    int mousePressed =\n        glfwGetMouseButton(g_ctx.glfwWindow, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;\n\n    Clay_SetPointerState(mousePosition, mousePressed);\n\n    Clay_UpdateScrollContainers(\n        true,\n        (Clay_Vector2){scrollDelta.x, scrollDelta.y},\n        g_deltaTime);\n\n    glfwGetWindowSize(g_ctx.glfwWindow, &g_ctx.screenWidth, &g_ctx.screenHeight);\n    glViewport(0, 0, g_ctx.screenWidth, g_ctx.screenHeight);\n\n    Clay_SetLayoutDimensions((Clay_Dimensions){(float)g_ctx.screenWidth, (float)g_ctx.screenHeight});\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    glDisable(GL_DEPTH_TEST);\n    glDepthMask(GL_FALSE); // Clay renderer is simple and never writes to depth buffer\n    glClearColor(0.1f, 0.2f, 0.1f, 1.0f);\n\n    ClayVideoDemo_Data data = ClayVideoDemo_Initialize();\n    Clay_RenderCommandArray cmds = ClayVideoDemo_CreateLayout(&data);\n\n    Gles3_Render(&g_gles3, cmds, g_stbFonts);\n\n    glfwSwapBuffers(g_ctx.glfwWindow);\n\n}\n\n// Just initializes and spins the animation loop\nint main()\n{\n    initVideo(&g_ctx, 1280, 720);\n    init();\n\n    g_ctx.shouldContinue = true;\n#ifdef __EMSCRIPTEN__\n    emscripten_set_main_loop(loop, 0, 1);\n#else\n    while (g_ctx.shouldContinue)\n    {\n        loop();\n    }\n#endif\n}"
  },
  {
    "path": "examples/GLES3-SDL2-sidebar-scrolling-container/.gitignore",
    "content": "/build/\n/website-demo-macos-sdl2*\n/macos-sidebar-scrolling-container*\n"
  },
  {
    "path": "examples/GLES3-SDL2-sidebar-scrolling-container/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nset(CMAKE_C_STANDARD 99)\nproject(GLES3_SDL2_sidebar_scrolling_container C)\n\n# -------------------------------------------------\n# FetchContent\n# -------------------------------------------------\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\n\n# -------------------------------------------------\n# STB (header-only)\n# -------------------------------------------------\nFetchContent_Declare(\n    stb\n    GIT_REPOSITORY https://github.com/nothings/stb.git\n    GIT_TAG master\n)\nFetchContent_MakeAvailable(stb)\n\n# -------------------------------------------------\n# SDL2\n# -------------------------------------------------\nFetchContent_Declare(\n    SDL2\n    GIT_REPOSITORY \"https://github.com/libsdl-org/SDL.git\"\n    GIT_TAG \"release-2.30.10\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(SDL2)\n\n# -------------------------------------------------\n# Executable\n# -------------------------------------------------\nadd_executable(GLES3_SDL2_sidebar_scrolling_container main.c)\ntarget_compile_options(GLES3_SDL2_sidebar_scrolling_container PUBLIC)\ntarget_include_directories(GLES3_SDL2_sidebar_scrolling_container\n    PUBLIC\n        .  # This renderer\n        ../.. # Clay\n        ${stb_SOURCE_DIR} # STB header only depencency that does not have its own CMake build\n\n)\n\n# -------------------------------------------------\n# Link libraries\n# -------------------------------------------------\ntarget_link_libraries(GLES3_SDL2_sidebar_scrolling_container PUBLIC\n    SDL2::SDL2main\n    SDL2::SDL2-static\n)\n\n# -------------------------------------------------\n# Platform-specific OpenGL / GLES\n# -------------------------------------------------\nfind_package(SDL2 REQUIRED)\nfind_library(OPENGL_FRAMEWORK OpenGL)\ntarget_link_libraries(GLES3_SDL2_sidebar_scrolling_container\n    PRIVATE\n        ${OPENGL_FRAMEWORK}\n)\n\n# -------------------------------------------------\n# Build flags (kept minimal)\n# -------------------------------------------------\nif(MSVC)\n    set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\nelse()\n    set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\n    set(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\nendif()\n\n# -------------------------------------------------\n# Copy resources\n# -------------------------------------------------\nadd_custom_command(\n    TARGET GLES3_SDL2_sidebar_scrolling_container POST_BUILD\n    COMMAND ${CMAKE_COMMAND} -E copy_directory\n    ${CMAKE_CURRENT_SOURCE_DIR}/resources\n    ${CMAKE_CURRENT_BINARY_DIR}/resources\n)\n"
  },
  {
    "path": "examples/GLES3-SDL2-sidebar-scrolling-container/Makefile.emscripten",
    "content": "# vim: set tabstop=4 shiftwidth=4 expandtab noexpandtab:\n\n#\n# PROGRAM COMPONENTS\n# \n\nCXX = emcc\n\nCXXFLAGS = -std=c99\nCXXFLAGS += -O0\nCXXFLAGS += -I../..\nCXXFLAGS += -I./build/_deps/stb-src\n\nLDLIBS += -s USE_ZLIB=1\nLDLIBS += -s USE_SDL=2\nLDLIBS += -s FULL_ES2=1 -s USE_WEBGL2=1\nLDLIBS += -s ALLOW_MEMORY_GROWTH=1 -s GL_UNSAFE_OPTS=0\nLDLIBS += -s STACK_SIZE=2048kb\nLDLIBS += -s EXPORTED_FUNCTIONS=['_main']\nLDLIBS += -s ASSERTIONS=1 -s SAFE_HEAP=1\nLDLIBS += --preload-file $(PWD)/resources/profile-picture.png@resources/profile-picture.png\nLDLIBS += --preload-file $(PWD)/resources/millbank.jpeg@resources/millbank.jpeg\nLDLIBS += --preload-file $(PWD)/resources/Roboto-Regular.ttf@resources/Roboto-Regular.ttf\nLDLIBS += --preload-file $(PWD)/resources/RobotoMono-Medium.ttf@resources/RobotoMono-Medium.ttf\n\nmain:\n\tmkdir -p build/emscripten\n\ttime $(CXX) $(CXXFLAGS) \\\n\t\t$(PWD)/main.c \\\n\t    $(LDLIBS) -o build/emscripten/index.html \n\ntest:\n\tmake -f Makefile.emscripten main \\\n\t\t&& (cd build/emscripten && python3 -mhttp.server)\n\t\n.PHONY: main \n"
  },
  {
    "path": "examples/GLES3-SDL2-sidebar-scrolling-container/Makefile.macos",
    "content": "# vim: set tabstop=4 shiftwidth=4 expandtab noexpandtab:\n\nCXX = clang\n\nCXXFLAGS = -std=c99\nCXXFLAGS += -g -O0 -fno-omit-frame-pointer\nCXXFLAGS += -ferror-limit=1\nCXXFLAGS += -I../..\nCXXFLAGS += -I./build/_deps/stb-src\nCXXFLAGS += -DGL_SILENCE_DEPRECATION\n\n# SDL2 (Homebrew)\nCXXFLAGS += -I$(shell brew --prefix sdl2)/include/SDL2\nLDLIBS   += -L$(shell brew --prefix sdl2)/lib -lSDL2\n\n# macOS system frameworks (OpenGL needs these)\nLDLIBS += -framework OpenGL\nLDLIBS += -framework Cocoa\nLDLIBS += -framework IOKit\nLDLIBS += -framework CoreVideo\n\nmain:\n\tmkdir -p build\n\n\ttime $(CXX) $(CXXFLAGS) \\\n\t\t$(PWD)/main.c \\\n\t\t$(LDLIBS) \\\n\t\t-o build/macos-sidebar-scrolling-container-sdl2\n"
  },
  {
    "path": "examples/GLES3-SDL2-sidebar-scrolling-container/README.md",
    "content": "GLES3 Renderer Scrolling Container (Using SDL2)\n===============================================\n\nThis directory contains a complete example thatn can me used to test all different draw commands\nusing work-in-progress GLES3 renderer.\nWhile it still needs refinement, the renderer is already functional and demonstrates the core rendering pipeline.\n\nThe images used as resources in this example, namely:\n\n- millbank.jpeg a window with a panoramic city view;\n- and profile-picture.png showing objects aligned in a circle;\n\nare taken with my phone camera and are dedicated to the Public Domain.\n\nHow to build it with CMake?\n---------------------------\n\nCmake build is the easiest way to build it:\n\n    mkdir build\n    cmake -S . -B ./build\n\nHow to build and run on Emscripten:\n----------------------------------\n\nFor Emscripten the build is a bit custom,\nbut it depends on CMakeBuild to install header-stb dependency.\nSo you still need to build it with CMake first.\n\nAnd then you have to source the Emscripten SDK:\n\n    source /path/to/emscripten/emsdk/emsdk_env.sh\n\nThen build it with hand-crafted Makefile.emscripten:\n\n    make -f Makefile.emscripten test\n\nand then navigate to http://localhost:8080\n\nHow to build and run on Mac:\n----------------------------\n\nRequires SDL2:\n\n    brew install sdl2\n\nRequires STB:\n\n    cmake -B build\n\nBuild it with:\n\n    make -f Makefile.macos\n\nAnd run with:\n\n    ./macos-sidebar-scrolling-container-sdl2\n"
  },
  {
    "path": "examples/GLES3-SDL2-sidebar-scrolling-container/main.c",
    "content": "#include <SDL.h>\n#include <stdio.h>\n\n#define STB_IMAGE_IMPLEMENTATION\n#define STB_TRUETYPE_IMPLEMENTATION\n#define CLAY_IMPLEMENTATION\n#define CLAY_RENDERER_GLES3_IMPLEMENTATION\n\n#include <clay.h>\n\n#include \"../../renderers/GLES3/clay_renderer_gles3.h\"\n#include \"../shared-layouts/clay-video-demo.c\"\n#include \"../../renderers/GLES3/clay_renderer_gles3_loader_stb.c\"\n\ntypedef struct VideoCtx\n{\n    int shouldContinue;\n    SDL_Window *sdlWindow;\n    SDL_GLContext sdlContext;\n    int screenWidth, screenHeight;\n} VideoCtx;\n\nVideoCtx g_ctx;\n\nstatic int initVideo(VideoCtx *ctx, const int initialWidth, const int initialHeight)\n{\n    SDL_Init(SDL_INIT_VIDEO);\n\n#if defined(__EMSCRIPTEN__)\n    // OpenGL ES 3 profile\n    SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, \"1\");\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);\n#else\n    // Apple MacOs will use it own legacy desktop GL instead\n    // I know, I lied, I said this was an GLES3\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);\n#endif\n\n    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);\n    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);\n    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);\n    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);\n    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);\n\n    g_ctx.sdlWindow = SDL_CreateWindow(\n        \"SDL2 GLES3\",\n        SDL_WINDOWPOS_CENTERED,\n        SDL_WINDOWPOS_CENTERED,\n        initialWidth,\n        initialHeight,\n        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);\n    g_ctx.sdlContext = SDL_GL_CreateContext(g_ctx.sdlWindow);\n\n    SDL_ShowWindow(g_ctx.sdlWindow);\n    SDL_Delay(1);\n    SDL_GL_GetDrawableSize(g_ctx.sdlWindow, &g_ctx.screenWidth, &g_ctx.screenHeight);\n    glViewport(0, 0, g_ctx.screenWidth, g_ctx.screenHeight);\n\n    glEnable(GL_BLEND);\n    // Enables blending, which allows transparent textures to be rendered properly.\n    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n    // Sets the blending function.\n    // - `GL_SRC_ALPHA`: Uses the alpha value of the source (texture or color).\n    // - `GL_ONE_MINUS_SRC_ALPHA`: Makes the destination color blend with the background based on alpha.\n    // This is commonly used for standard transparency effects.\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    glEnable(GL_DEPTH_TEST);\n    // Enables depth testing, ensuring that objects closer to the camera are drawn in front of those farther away.\n    // This prevents objects from rendering incorrectly based on draw order.\n\n    return 1;\n}\n\nvoid My_ErrorHandler(Clay_ErrorData errorData)\n{\n    printf(\"[ClaY ErroR] %s\", errorData.errorText.chars);\n}\n\nStb_FontData g_stbFonts[MAX_FONTS]; // Fonts userData\nGles3_Renderer g_gles3;             // The renderer itself\n\nUint64 NOW = 0;\nUint64 LAST = 0;\ndouble deltaTime = 0;\n\nuint64_t g_drawCallsDuringLastFrame = 0;\n\ndouble g_timeAccumulator = 0.0;\ndouble g_avgFrameMs = 0.0;\ndouble g_fps = 0.0;\nint g_frameCount = 0;\n\nchar g_fpsText[128];\nsize_t g_fpsTextLen = 0;\n\nstatic double g_wallTimeAccumulator = 0.0;\n\nchar g_glInfoText[512];\nsize_t g_glInfoTextLen;\n\nvoid init()\n{\n    size_t clayRequiredMemory = Clay_MinMemorySize();\n    g_gles3.clayMemory = (Clay_Arena){\n        .capacity = clayRequiredMemory,\n        .memory = (char *)malloc(clayRequiredMemory),\n    };\n    Clay_Context *clayCtx = Clay_Initialize(\n        g_gles3.clayMemory,\n        (Clay_Dimensions){\n            .width = (float)g_ctx.screenWidth,\n            .height = (float)g_ctx.screenHeight,\n        },\n        (Clay_ErrorHandler){\n            .errorHandlerFunction = My_ErrorHandler,\n        });\n\n    // Note that MeasureText has to be set after the Context is set!\n    Clay_SetCurrentContext(clayCtx);\n    Clay_SetMeasureTextFunction(Stb_MeasureText, &g_stbFonts);\n    Gles3_SetRenderTextFunction(&g_gles3, Stb_RenderText, &g_stbFonts);\n\n    Gles3_Initialize(&g_gles3, 4096);\n\n    int atlasW = 1024;\n    int atlasH = 1024;\n    if (!Stb_LoadFont(\n            &g_gles3.fontTextures[0],\n            &g_stbFonts[0],\n            \"resources/Roboto-Regular.ttf\",\n            24.0f, // bake pixel height\n            atlasW,\n            atlasH))\n        abort();\n    if (!Stb_LoadFont(\n            &g_gles3.fontTextures[1],\n            &g_stbFonts[1],\n            \"resources/RobotoMono-Medium.ttf\",\n            24.0f, // bake pixel height\n            atlasW,\n            atlasH))\n        abort();\n\n    if (!Stb_LoadImage(\n            &g_gles3.imageTextures[0],\n            \"resources/profile-picture.png\"))\n        abort();\n\n    if (!Stb_LoadImage(\n            &g_gles3.imageTextures[1],\n            \"resources/millbank.jpeg\"))\n        abort();\n\n    Clay_SetDebugModeEnabled(true);\n\n    const GLubyte *glVersion = glGetString(GL_VERSION);\n    const GLubyte *glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);\n    const GLubyte *vendor = glGetString(GL_VENDOR);\n    const GLubyte *renderer = glGetString(GL_RENDERER);\n\n    g_glInfoTextLen = (size_t)snprintf(\n        g_glInfoText,\n        sizeof(g_glInfoText),\n        \"OpenGL Version : %s\\n\"\n        \"GLSL Version   : %s\\n\"\n        \"Vendor         : %s\\n\"\n        \"Renderer       : %s\",\n        glVersion ? (const char *)glVersion : \"unknown\",\n        glslVersion ? (const char *)glslVersion : \"unknown\",\n        vendor ? (const char *)vendor : \"unknown\",\n        renderer ? (const char *)renderer : \"unknown\");\n}\n\nGles3_ImageConfig g_profilePicture = (Gles3_ImageConfig){\n    .textureToUse = 0,\n    .u0 = 0.0f,\n    .v0 = 0.0f,\n    .u1 = 1.0f,\n    .v1 = 1.0f,\n};\n\nGles3_ImageConfig g_window1 = (Gles3_ImageConfig){\n    .textureToUse = 1,\n    .u0 = 0.0f,\n    .v0 = 0.35f,\n    .u1 = 0.18f,\n    .v1 = 0.75f,\n};\nGles3_ImageConfig g_window2 = (Gles3_ImageConfig){\n    .textureToUse = 1,\n    .u0 = 0.25f,\n    .v0 = 0.35f,\n    .u1 = 0.47f,\n    .v1 = 0.75f,\n};\nGles3_ImageConfig g_window3 = (Gles3_ImageConfig){\n    .textureToUse = 1,\n    .u0 = 0.52f,\n    .v0 = 0.35f,\n    .u1 = 0.76f,\n    .v1 = 0.75f,\n};\nGles3_ImageConfig g_window4 = (Gles3_ImageConfig){\n    .textureToUse = 1,\n    .u0 = 0.82f,\n    .v0 = 0.35f,\n    .u1 = 1.0f,\n    .v1 = 0.75f,\n};\n\nClay_LayoutConfig dropdownTextItemLayout = {.padding = {8, 8, 4, 4}};\nClay_TextElementConfig dropdownTextElementConfig = {.fontSize = 24, .textColor = {55, 55, 55, 255}};\nvoid RenderDropdownTextItem(int index)\n{\n    CLAY_AUTO_ID({.layout = dropdownTextItemLayout, .backgroundColor = {220, 220, 220, 255}})\n    {\n        CLAY_TEXT(CLAY_STRING(\"I'm a text field in a scroll container.\"), &dropdownTextElementConfig);\n    }\n}\n\nconst uint32_t FONT_ID_BODY_24 = 1;\n\nClay_String profileText = CLAY_STRING_CONST(\"Profile Page one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen\");\n\nvoid RenderHeaderButton1(Clay_String text)\n{\n    CLAY_AUTO_ID(\n        {.layout = {\n             .padding = {16, 16, 8, 8}},\n         .backgroundColor = {140, 140, 140, 255},\n         .border = {.width = CLAY_BORDER_OUTSIDE(14), .color = {180, 80, 80, 255}},\n         .cornerRadius = CLAY_CORNER_RADIUS(5)})\n    {\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG({.fontId = FONT_ID_BODY_16, .fontSize = 16, .textColor = {255, 255, 255, 255}}));\n    }\n}\nvoid RenderHeaderButton2(Clay_String text)\n{\n    CLAY_AUTO_ID(\n        {.layout = {\n             .padding = {16, 16, 8, 8}},\n         .backgroundColor = {140, 140, 140, 255},\n         // .border = { .width = CLAY_BORDER_OUTSIDE(4), .color = {180, 80, 80, 255} },\n         .cornerRadius = CLAY_CORNER_RADIUS(5)})\n    {\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG({.fontId = FONT_ID_BODY_16, .fontSize = 16, .textColor = {255, 255, 255, 255}}));\n    }\n}\nvoid RenderHeaderButton3(Clay_String text)\n{\n    CLAY_AUTO_ID(\n        {.layout = {\n             .padding = {16, 16, 8, 8}},\n         .backgroundColor = {140, 140, 140, 255},\n         .border = {.width = CLAY_BORDER_OUTSIDE(14), .color = {180, 80, 80, 255}},\n         .cornerRadius = CLAY_CORNER_RADIUS(0)})\n    {\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG({.fontId = FONT_ID_BODY_16, .fontSize = 16, .textColor = {255, 255, 255, 255}}));\n    }\n}\nvoid RenderHeaderButton4(Clay_String text)\n{\n    CLAY_AUTO_ID(\n        {.layout = {\n             .padding = {16, 16, 8, 8}},\n         .backgroundColor = {140, 140, 140, 255},\n         .border = {.width = CLAY_BORDER_OUTSIDE(4), .color = {180, 80, 80, 255}},\n         .cornerRadius = CLAY_CORNER_RADIUS(5)})\n    {\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG({.fontId = FONT_ID_BODY_16, .fontSize = 16, .textColor = {255, 255, 255, 255}}));\n    }\n}\nClay_RenderCommandArray CreateLayout(void)\n{\n    Clay_BeginLayout();\n    CLAY(CLAY_ID(\"OuterContainer\"),\n         {.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, .padding = {16, 16, 16, 16}, .childGap = 16}, .backgroundColor = {200, 200, 200, 255}})\n    {\n        CLAY(CLAY_ID(\"SideBar\"),\n             {.layout = {.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = {.width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0)}, .padding = {16, 16, 16, 16}, .childGap = 16}, .backgroundColor = {150, 150, 255, 255}})\n        {\n            CLAY(CLAY_ID(\"ProfilePictureOuter\"), {.layout = {.sizing = {.width = CLAY_SIZING_GROW(0)}, .padding = {8, 8, 8, 8}, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}}, .backgroundColor = {130, 130, 255, 255}})\n            {\n                CLAY(CLAY_ID(\"ProfilePicture\"),\n                     {\n                         .layout = {.sizing = {.width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60)}},\n                         .image = {.imageData = &g_profilePicture},\n                         .cornerRadius = {30, 30, 30, 30},\n                     })\n                {\n                }\n                CLAY_TEXT(profileText, CLAY_TEXT_CONFIG({.fontSize = 24, .textColor = {0, 0, 0, 255}, .textAlignment = CLAY_TEXT_ALIGN_RIGHT}));\n            }\n            CLAY(CLAY_ID(\"SidebarBlob1\"), {.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50)}}, .backgroundColor = {110, 110, 255, 255}}) {}\n            CLAY(CLAY_ID(\"SidebarBlob2\"), {.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50)}}, .backgroundColor = {110, 110, 255, 255}}) {}\n            CLAY(CLAY_ID(\"SidebarBlob3\"), {.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50)}}, .backgroundColor = {110, 110, 255, 255}}) {}\n            CLAY(CLAY_ID(\"SidebarBlob4\"), {.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50)}}, .backgroundColor = {110, 110, 255, 255}}) {}\n        }\n        CLAY(CLAY_ID(\"RightPanel\"), {.layout = {.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, .childGap = 16}})\n        {\n            CLAY_AUTO_ID({.layout = {.sizing = {.width = CLAY_SIZING_GROW(0)}, .childAlignment = {.x = CLAY_ALIGN_X_RIGHT}, .padding = {8, 8, 8, 8}, .childGap = 18}, .backgroundColor = {180, 180, 180, 255}})\n            {\n                RenderHeaderButton1(CLAY_STRING(\"Header Item 1\"));\n                RenderHeaderButton2(CLAY_STRING(\"Header Item 2\"));\n                RenderHeaderButton3(CLAY_STRING(\"Header Item 3\"));\n                RenderHeaderButton4(CLAY_STRING(\"Header Item 4\"));\n            }\n            CLAY(\n                CLAY_ID(\"MainContent\"),\n                {\n                    .layout = {.layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {16, 16, 16, 16}, .childGap = 16, .sizing = {.width = CLAY_SIZING_GROW(0)}},\n                    .backgroundColor = {200, 200, 255, 255},\n                    .clip = {.vertical = true, .childOffset = Clay_GetScrollOffset()},\n                })\n            {\n                CLAY(\n                    CLAY_ID(\"FloatingContainer\"),\n                    {\n                        .layout = {.sizing = {.width = CLAY_SIZING_PERCENT(0.5), .height = CLAY_SIZING_FIXED(300)}, .padding = {16, 16, 16, 16}},\n                        .backgroundColor = {140, 80, 200, 200},\n                        .floating = {.attachTo = CLAY_ATTACH_TO_PARENT, .zIndex = 1, .attachPoints = {CLAY_ATTACH_POINT_CENTER_TOP, CLAY_ATTACH_POINT_CENTER_TOP}, .offset = {0, 0}},\n                        .border = {.width = CLAY_BORDER_OUTSIDE(4), .color = {80, 80, 80, 255}},\n                        .cornerRadius = {30, 3, 3, 30},\n                    })\n                {\n                    CLAY_TEXT(\n                        CLAY_STRING(\"I'm an inline floating container.\"), CLAY_TEXT_CONFIG({.fontSize = 24, .textColor = {255, 255, 255, 255}}));\n                }\n\n                CLAY_TEXT(\n                    CLAY_STRING(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.\"),\n                    CLAY_TEXT_CONFIG({.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {0, 0, 0, 255}}));\n\n                CLAY_TEXT(\n                    CLAY_STRING(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.\"),\n                    CLAY_TEXT_CONFIG({.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {0, 0, 0, 255}}));\n\n                CLAY_TEXT(CLAY_STRING(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.\"),\n                          CLAY_TEXT_CONFIG({.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {0, 0, 0, 255}}));\n\n                CLAY(CLAY_ID(\"Photos2\"), {.layout = {.childGap = 16, .padding = {16, 16, 16, 16}}, .backgroundColor = {180, 180, 220, (float)(Clay_Hovered() ? 120 : 255)}})\n                {\n                    CLAY(CLAY_ID(\"Picture4\"), {.layout = {.sizing = {.width = CLAY_SIZING_FIXED(100), .height = CLAY_SIZING_FIXED(120)}}, .image = {.imageData = &g_window1}}) {}\n                    CLAY(CLAY_ID(\"Picture5\"), {.layout = {.sizing = {.width = CLAY_SIZING_FIXED(100), .height = CLAY_SIZING_FIXED(120)}}, .image = {.imageData = &g_window2}}) {}\n                    CLAY(CLAY_ID(\"Picture6\"), {.layout = {.sizing = {.width = CLAY_SIZING_FIXED(100), .height = CLAY_SIZING_FIXED(120)}}, .image = {.imageData = &g_window3}}) {}\n                    CLAY(CLAY_ID(\"Picture6.5\"), {.layout = {.sizing = {.width = CLAY_SIZING_FIXED(100), .height = CLAY_SIZING_FIXED(120)}}, .image = {.imageData = &g_window4}}) {}\n                }\n\n                Clay_String cs = {.isStaticallyAllocated = false, .length = g_glInfoTextLen, .chars = g_glInfoText};\n                Clay_TextElementConfig glInfoElementConfig = {.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {255, 255, 255, 255}};\n                CLAY_TEXT(cs, &glInfoElementConfig);\n\n                CLAY_TEXT(\n                    CLAY_STRING(\"Faucibus purus in massa tempor nec. Nec ullamcorper sit amet risus nullam eget felis eget nunc. Diam vulputate ut pharetra sit amet aliquam id diam. Lacus suspendisse faucibus interdum posuere lorem. A diam sollicitudin tempor id. Amet massa vitae tortor condimentum lacinia. Aliquet nibh praesent tristique magna.\"),\n                    CLAY_TEXT_CONFIG({.fontSize = 24, .lineHeight = 60, .textColor = {0, 0, 0, 255}, .textAlignment = CLAY_TEXT_ALIGN_CENTER}));\n\n                CLAY_TEXT(CLAY_STRING(\"Suspendisse in est ante in nibh. Amet venenatis urna cursus eget nunc scelerisque viverra. Elementum sagittis vitae et leo duis ut diam quam nulla. Enim nulla aliquet porttitor lacus. Pellentesque habitant morbi tristique senectus et. Facilisi nullam vehicula ipsum a arcu cursus vitae.\\nSem fringilla ut morbi tincidunt. Euismod quis viverra nibh cras pulvinar mattis nunc sed. Velit sed ullamcorper morbi tincidunt ornare massa. Varius quam quisque id diam vel quam. Nulla pellentesque dignissim enim sit amet venenatis. Enim lobortis scelerisque fermentum dui faucibus in. Pretium viverra suspendisse potenti nullam ac tortor vitae. Lectus vestibulum mattis ullamcorper velit sed. Eget mauris pharetra et ultrices neque ornare aenean euismod elementum. Habitant morbi tristique senectus et. Integer vitae justo eget magna fermentum iaculis eu. Semper quis lectus nulla at volutpat diam. Enim praesent elementum facilisis leo. Massa vitae tortor condimentum lacinia quis vel.\"),\n                          CLAY_TEXT_CONFIG({.fontSize = 24, .textColor = {0, 0, 0, 255}}));\n\n                CLAY(CLAY_ID(\"Photos\"), {.layout = {.sizing = {.width = CLAY_SIZING_GROW(0)}, .childAlignment = {.x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .childGap = 16, .padding = {16, 16, 16, 16}}, .backgroundColor = {180, 180, 220, 255}})\n                {\n                    CLAY(CLAY_ID(\"Picture2\"), {.layout = {.sizing = {.width = CLAY_SIZING_FIXED(120)}}, .aspectRatio = 1, .image = {.imageData = &g_profilePicture}}) {}\n                    CLAY(CLAY_ID(\"Picture1\"), {.layout = {.childAlignment = {.x = CLAY_ALIGN_X_CENTER}, .layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8, 8, 8}}, .backgroundColor = {170, 170, 220, 255}})\n                    {\n                        CLAY(CLAY_ID(\"ProfilePicture2\"), {.layout = {.sizing = {.width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60)}}, .image = {.imageData = &g_profilePicture}}) {}\n                        CLAY_TEXT(CLAY_STRING(\"Image caption below\"), CLAY_TEXT_CONFIG({.fontSize = 24, .textColor = {0, 0, 0, 255}}));\n                    }\n                    CLAY(CLAY_ID(\"Picture3\"), {.layout = {.sizing = {.width = CLAY_SIZING_FIXED(120)}}, .aspectRatio = 1, .image = {.imageData = &g_profilePicture}}) {}\n                }\n\n                CLAY_TEXT(\n                    CLAY_STRING(\"Amet cursus sit amet dictum sit amet justo donec. Et malesuada fames ac turpis egestas maecenas. A lacus vestibulum sed arcu non odio euismod lacinia. Gravida neque convallis a cras. Dui nunc mattis enim ut tellus elementum sagittis vitae et. Orci sagittis eu volutpat odio facilisis mauris. Neque gravida in fermentum et sollicitudin ac orci. Ultrices dui sapien eget mi proin sed libero. Euismod quis viverra nibh cras pulvinar mattis. Diam volutpat commodo sed egestas egestas. In fermentum posuere urna nec tincidunt praesent semper. Integer eget aliquet nibh praesent tristique magna.\\nId cursus metus aliquam eleifend mi in. Sed pulvinar proin gravida hendrerit lectus a. Etiam tempor orci eu lobortis elementum nibh tellus. Nullam vehicula ipsum a arcu cursus vitae. Elit scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Condimentum lacinia quis vel eros donec ac odio. Mattis pellentesque id nibh tortor id aliquet lectus. Turpis egestas integer eget aliquet nibh praesent tristique. Porttitor massa id neque aliquam vestibulum morbi. Mauris commodo quis imperdiet massa tincidunt nunc pulvinar sapien et. Nunc scelerisque viverra mauris in aliquam sem fringilla. Suspendisse ultrices gravida dictum fusce ut placerat orci nulla.\\nLacus laoreet non curabitur gravida arcu ac tortor dignissim. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar. Tristique senectus et netus et malesuada fames ac. Nunc aliquet bibendum enim facilisis gravida. Egestas maecenas pharetra convallis posuere morbi leo urna molestie. Sapien nec sagittis aliquam malesuada bibendum arcu vitae elementum curabitur. Ac turpis egestas maecenas pharetra convallis posuere morbi leo urna. Viverra vitae congue eu consequat. Aliquet enim tortor at auctor urna. Ornare massa eget egestas purus viverra accumsan in nisl nisi. Elit pellentesque habitant morbi tristique senectus et netus et malesuada.\\nSuspendisse ultrices gravida dictum fusce ut placerat orci nulla pellentesque. Lobortis feugiat vivamus at augue eget arcu. Vitae justo eget magna fermentum iaculis eu. Gravida rutrum quisque non tellus orci. Ipsum faucibus vitae aliquet nec. Nullam non nisi est sit amet. Nunc consequat interdum varius sit amet mattis vulputate enim. Sem fringilla ut morbi tincidunt augue interdum. Vitae purus faucibus ornare suspendisse. Massa tincidunt nunc pulvinar sapien et. Fringilla ut morbi tincidunt augue interdum velit euismod in. Donec massa sapien faucibus et. Est placerat in egestas erat imperdiet. Gravida rutrum quisque non tellus. Morbi non arcu risus quis varius quam quisque id diam. Habitant morbi tristique senectus et netus et malesuada fames ac. Eget lorem dolor sed viverra.\\nOrnare massa eget egestas purus viverra. Varius vel pharetra vel turpis nunc eget lorem. Consectetur purus ut faucibus pulvinar elementum. Placerat in egestas erat imperdiet sed euismod nisi. Interdum velit euismod in pellentesque massa placerat duis ultricies lacus. Aliquam nulla facilisi cras fermentum odio eu. Est pellentesque elit ullamcorper dignissim cras tincidunt. Nunc sed id semper risus in hendrerit gravida rutrum. A pellentesque sit amet porttitor eget dolor morbi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Nisl nunc mi ipsum faucibus vitae aliquet nec ullamcorper. Sed id semper risus in hendrerit gravida. Tincidunt praesent semper feugiat nibh. Aliquet lectus proin nibh nisl condimentum id venenatis a. Enim sit amet venenatis urna cursus eget. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Lacinia quis vel eros donec ac odio tempor orci. Donec pretium vulputate sapien nec sagittis aliquam malesuada bibendum arcu. Erat pellentesque adipiscing commodo elit at.\\nEgestas sed sed risus pretium quam vulputate. Vitae congue mauris rhoncus aenean vel elit scelerisque mauris pellentesque. Aliquam malesuada bibendum arcu vitae elementum. Congue mauris rhoncus aenean vel elit scelerisque mauris. Pellentesque dignissim enim sit amet venenatis urna cursus. Et malesuada fames ac turpis egestas sed tempus urna. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Nibh cras pulvinar mattis nunc sed blandit libero. Fringilla est ullamcorper eget nulla facilisi etiam dignissim. Aenean euismod elementum nisi quis eleifend quam adipiscing vitae proin. Mauris pharetra et ultrices neque ornare aenean euismod elementum. Ornare quam viverra orci sagittis eu. Odio ut sem nulla pharetra diam sit amet nisl suscipit. Ornare lectus sit amet est. Ullamcorper sit amet risus nullam eget. Tincidunt lobortis feugiat vivamus at augue eget arcu dictum.\\nUrna nec tincidunt praesent semper feugiat nibh. Ut venenatis tellus in metus vulputate eu scelerisque felis. Cursus risus at ultrices mi tempus. In pellentesque massa placerat duis ultricies lacus sed turpis. Platea dictumst quisque sagittis purus. Cras adipiscing enim eu turpis egestas. Egestas sed tempus urna et pharetra pharetra. Netus et malesuada fames ac turpis egestas integer eget aliquet. Ac turpis egestas sed tempus. Sed lectus vestibulum mattis ullamcorper velit sed. Ante metus dictum at tempor commodo ullamcorper a. Augue neque gravida in fermentum et sollicitudin ac. Praesent semper feugiat nibh sed pulvinar proin gravida. Metus aliquam eleifend mi in nulla posuere sollicitudin aliquam ultrices. Neque gravida in fermentum et sollicitudin ac orci phasellus egestas.\\nRidiculus mus mauris vitae ultricies. Morbi quis commodo odio aenean. Duis ultricies lacus sed turpis. Non pulvinar neque laoreet suspendisse interdum consectetur. Scelerisque eleifend donec pretium vulputate sapien nec sagittis aliquam. Volutpat est velit egestas dui id ornare arcu odio ut. Viverra tellus in hac habitasse platea dictumst vestibulum rhoncus est. Vestibulum lectus mauris ultrices eros. Sed blandit libero volutpat sed cras ornare. Id leo in vitae turpis massa sed elementum tempus. Gravida dictum fusce ut placerat orci nulla pellentesque. Pretium quam vulputate dignissim suspendisse in. Nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Risus viverra adipiscing at in tellus. Turpis nunc eget lorem dolor sed viverra ipsum. Senectus et netus et malesuada fames ac. Habitasse platea dictumst vestibulum rhoncus est. Nunc sed id semper risus in hendrerit gravida. Felis eget velit aliquet sagittis id. Eget felis eget nunc lobortis.\\nMaecenas pharetra convallis posuere morbi leo. Maecenas volutpat blandit aliquam etiam. A condimentum vitae sapien pellentesque habitant morbi tristique senectus et. Pulvinar mattis nunc sed blandit libero volutpat sed. Feugiat in ante metus dictum at tempor commodo ullamcorper. Vel pharetra vel turpis nunc eget lorem dolor. Est placerat in egestas erat imperdiet sed euismod. Quisque non tellus orci ac auctor augue mauris augue. Placerat vestibulum lectus mauris ultrices eros in cursus turpis. Enim nunc faucibus a pellentesque sit. Adipiscing vitae proin sagittis nisl. Iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Aliquam sem fringilla ut morbi.\\nArcu odio ut sem nulla pharetra diam sit amet nisl. Non diam phasellus vestibulum lorem sed. At erat pellentesque adipiscing commodo elit at. Lacus luctus accumsan tortor posuere ac ut consequat. Et malesuada fames ac turpis egestas integer. Tristique magna sit amet purus. A condimentum vitae sapien pellentesque habitant. Quis varius quam quisque id diam vel quam. Est ullamcorper eget nulla facilisi etiam dignissim diam quis. Augue interdum velit euismod in pellentesque massa. Elit scelerisque mauris pellentesque pulvinar pellentesque habitant. Vulputate eu scelerisque felis imperdiet. Nibh tellus molestie nunc non blandit massa. Velit euismod in pellentesque massa placerat. Sed cras ornare arcu dui. Ut sem viverra aliquet eget sit. Eu lobortis elementum nibh tellus molestie nunc non. Blandit libero volutpat sed cras ornare arcu dui vivamus.\\nSit amet aliquam id diam maecenas. Amet risus nullam eget felis eget nunc lobortis mattis aliquam. Magna sit amet purus gravida. Egestas purus viverra accumsan in nisl nisi. Leo duis ut diam quam. Ante metus dictum at tempor commodo ullamcorper. Ac turpis egestas integer eget. Fames ac turpis egestas integer eget aliquet nibh. Sem integer vitae justo eget magna fermentum. Semper auctor neque vitae tempus quam pellentesque nec nam aliquam. Vestibulum mattis ullamcorper velit sed. Consectetur adipiscing elit duis tristique sollicitudin nibh. Massa id neque aliquam vestibulum morbi blandit cursus risus.\\nCursus sit amet dictum sit amet justo donec enim diam. Egestas erat imperdiet sed euismod. Nullam vehicula ipsum a arcu cursus vitae congue mauris. Habitasse platea dictumst vestibulum rhoncus est pellentesque elit. Duis ultricies lacus sed turpis tincidunt id aliquet risus feugiat. Faucibus ornare suspendisse sed nisi lacus sed viverra. Pretium fusce id velit ut tortor pretium viverra. Fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel. Senectus et netus et malesuada. Tellus pellentesque eu tincidunt tortor aliquam. Aenean sed adipiscing diam donec adipiscing tristique risus nec feugiat. Quis vel eros donec ac odio. Id interdum velit laoreet id donec ultrices tincidunt.\\nMassa id neque aliquam vestibulum morbi blandit cursus risus at. Enim tortor at auctor urna nunc id cursus metus. Lorem ipsum dolor sit amet consectetur. At quis risus sed vulputate odio. Facilisis mauris sit amet massa vitae tortor condimentum lacinia quis. Et malesuada fames ac turpis egestas maecenas. Bibendum arcu vitae elementum curabitur vitae nunc sed velit dignissim. Viverra orci sagittis eu volutpat odio facilisis mauris. Adipiscing bibendum est ultricies integer quis auctor elit sed. Neque viverra justo nec ultrices dui sapien. Elementum nibh tellus molestie nunc non blandit massa enim. Euismod elementum nisi quis eleifend quam adipiscing vitae proin sagittis. Faucibus ornare suspendisse sed nisi. Quis viverra nibh cras pulvinar mattis nunc sed blandit. Tristique senectus et netus et. Magnis dis parturient montes nascetur ridiculus mus.\\nDolor magna eget est lorem ipsum dolor. Nibh sit amet commodo nulla. Donec pretium vulputate sapien nec sagittis aliquam malesuada. Cras adipiscing enim eu turpis egestas pretium. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Mus mauris vitae ultricies leo integer. In nulla posuere sollicitudin aliquam ultrices sagittis orci. Quis hendrerit dolor magna eget. Nisl tincidunt eget nullam non. Vitae congue eu consequat ac felis donec et odio. Vivamus at augue eget arcu dictum varius duis at. Ornare quam viverra orci sagittis.\\nErat nam at lectus urna duis convallis. Massa placerat duis ultricies lacus sed turpis tincidunt id aliquet. Est ullamcorper eget nulla facilisi etiam dignissim diam. Arcu vitae elementum curabitur vitae nunc sed velit dignissim sodales. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus. Neque viverra justo nec ultrices dui sapien eget mi proin. Viverra accumsan in nisl nisi scelerisque eu ultrices. Consequat interdum varius sit amet mattis. In aliquam sem fringilla ut morbi. Eget arcu dictum varius duis at. Nulla aliquet porttitor lacus luctus accumsan tortor posuere. Arcu bibendum at varius vel pharetra vel turpis. Hac habitasse platea dictumst quisque sagittis purus sit amet. Sapien eget mi proin sed libero enim sed. Quam elementum pulvinar etiam non quam lacus suspendisse faucibus interdum. Semper viverra nam libero justo. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Et malesuada fames ac turpis egestas maecenas pharetra convallis posuere.\\nTurpis egestas sed tempus urna et pharetra pharetra massa. Gravida in fermentum et sollicitudin ac orci phasellus. Ornare suspendisse sed nisi lacus sed viverra tellus in. Fames ac turpis egestas maecenas pharetra convallis posuere. Mi proin sed libero enim sed faucibus turpis. Sit amet mauris commodo quis imperdiet massa tincidunt nunc. Ut etiam sit amet nisl purus in mollis nunc. Habitasse platea dictumst quisque sagittis purus sit amet volutpat consequat. Eget aliquet nibh praesent tristique magna. Sit amet est placerat in egestas erat. Commodo sed egestas egestas fringilla. Enim nulla aliquet porttitor lacus luctus accumsan tortor posuere ac. Et molestie ac feugiat sed lectus vestibulum mattis ullamcorper. Dignissim convallis aenean et tortor at risus viverra. Morbi blandit cursus risus at ultrices mi. Ac turpis egestas integer eget aliquet nibh praesent tristique magna.\\nVolutpat sed cras ornare arcu dui. Egestas erat imperdiet sed euismod nisi porta lorem mollis aliquam. Viverra justo nec ultrices dui sapien. Amet risus nullam eget felis eget nunc lobortis. Metus aliquam eleifend mi in. Ut eu sem integer vitae. Auctor elit sed vulputate mi sit amet. Nisl nisi scelerisque eu ultrices. Dictum fusce ut placerat orci nulla. Pellentesque habitant morbi tristique senectus et. Auctor elit sed vulputate mi sit. Tincidunt arcu non sodales neque. Mi in nulla posuere sollicitudin aliquam. Morbi non arcu risus quis varius quam quisque id diam. Cras adipiscing enim eu turpis egestas pretium aenean pharetra magna. At auctor urna nunc id cursus metus aliquam. Mauris a diam maecenas sed enim ut sem viverra. Nunc scelerisque viverra mauris in. In iaculis nunc sed augue lacus viverra vitae congue eu. Volutpat blandit aliquam etiam erat velit scelerisque in dictum non.\"),\n                    CLAY_TEXT_CONFIG({.fontSize = 24, .textColor = {0, 0, 0, 255}}));\n            }\n\n            CLAY_AUTO_ID({.layout = {.sizing = {.width = CLAY_SIZING_GROW(0)}, .padding = {8, 8, 8, 8}, .childGap = 8}, .backgroundColor = {180, 180, 180, 255}})\n            {\n                char drawCallsText[200];\n                int drawCallsTextLen = snprintf(drawCallsText, sizeof(drawCallsText),\n                                                \"Last frame got: %llu draw calls\\n\", g_drawCallsDuringLastFrame);\n                if (drawCallsTextLen < 0)\n                    drawCallsTextLen = 0;\n                else if ((size_t)drawCallsTextLen >= sizeof(drawCallsText))\n                    drawCallsTextLen = (int)sizeof(drawCallsText) - 1;\n                Clay_String cs = {.isStaticallyAllocated = false, .length = (size_t)drawCallsTextLen, .chars = drawCallsText};\n                Clay_TextElementConfig drawCallsElementConfig = {.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {255, 255, 255, 255}};\n                CLAY_TEXT(cs, &drawCallsElementConfig);\n            }\n            CLAY_AUTO_ID({.layout = {.sizing = {.width = CLAY_SIZING_GROW(0)}, .padding = {8, 8, 8, 8}, .childGap = 8}, .backgroundColor = {180, 180, 180, 255}})\n            {\n                Clay_String cs = {.isStaticallyAllocated = false, .length = g_fpsTextLen, .chars = g_fpsText};\n                Clay_TextElementConfig fpsElementConfig = {.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {255, 255, 255, 255}};\n                CLAY_TEXT(cs, &fpsElementConfig);\n            }\n        }\n\n        CLAY(\n            CLAY_ID(\"Blob4Floating2\"),\n            {\n                .floating = {.attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING(\"SidebarBlob4\")).id},\n                .backgroundColor = {40, 80, 200, 200},\n                .border = {.width = {0, 10, 0, 10}, .color = {0, 0, 0, 80}},\n                .cornerRadius = CLAY_CORNER_RADIUS(18),\n                .layout = {\n                    .padding = {10, 10, 10, 10},\n                },\n            })\n        {\n            CLAY(CLAY_ID(\"ScrollContainer\"), {.layout = {.sizing = {.height = CLAY_SIZING_FIXED(200)}, .childGap = 2}, .clip = {.vertical = true, .childOffset = Clay_GetScrollOffset()}})\n            {\n                CLAY(CLAY_ID(\"FloatingContainer2\"), {.layout.sizing.height = CLAY_SIZING_GROW(), .floating = {.attachTo = CLAY_ATTACH_TO_PARENT, .zIndex = 1}})\n                {\n                    CLAY(CLAY_ID(\"FloatingContainerInner\"),\n                         {\n                             .layout = {.sizing = {.width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW()}, .padding = {16, 16, 16, 16}},\n                             .backgroundColor = {140, 80, 200, 200},\n                             .border = {.width = CLAY_BORDER_OUTSIDE(4), .color = {80, 80, 80, 255}},\n                             .cornerRadius = {30, 3, 3, 30},\n                         })\n                    {\n                        CLAY_TEXT(CLAY_STRING(\"I'm an inline floating container.\"), CLAY_TEXT_CONFIG({.fontSize = 24, .textColor = {255, 255, 0, 255}}));\n                    }\n                }\n                CLAY(CLAY_ID(\"ScrollContainerInner\"), {.layout = {.layoutDirection = CLAY_TOP_TO_BOTTOM}, .backgroundColor = {160, 160, 160, 255}})\n                {\n                    for (int i = 0; i < 100; i++)\n                    {\n                        RenderDropdownTextItem(i);\n                    }\n                }\n            }\n        }\n        Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING(\"MainContent\")));\n        if (scrollData.found)\n        {\n            CLAY(CLAY_ID(\"ScrollBar\"),\n                 {.floating = {\n                      .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID,\n                      .offset = {.y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height},\n                      .zIndex = 1,\n                      .parentId = Clay_GetElementId(CLAY_STRING(\"MainContent\")).id,\n                      .attachPoints = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP}}})\n            {\n                CLAY(\n                    CLAY_ID(\"ScrollBarButton\"),\n                    {.layout = {\n                         .sizing = {CLAY_SIZING_FIXED(12), CLAY_SIZING_FIXED((scrollData.scrollContainerDimensions.height / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height)}},\n                     .backgroundColor = Clay_PointerOver(Clay_GetElementId(CLAY_STRING(\"ScrollBar\"))) ? (Clay_Color){100, 100, 140, 150} : (Clay_Color){120, 120, 160, 150},\n                     .cornerRadius = CLAY_CORNER_RADIUS(6)}) {}\n            }\n        }\n    }\n    return Clay_EndLayout();\n}\n\nvoid loop()\n{\n    LAST = NOW;\n    NOW = SDL_GetPerformanceCounter();\n    deltaTime = (double)((NOW - LAST) * 1000 / (double)SDL_GetPerformanceFrequency());\n\n    glClearColor(0.1f, 0.2f, 0.1f, 1.0f);\n\n    Clay_Vector2 scrollDelta = {};\n    SDL_Event event;\n    while (SDL_PollEvent(&event))\n    {\n        switch (event.type)\n        {\n        case SDL_QUIT:\n        {\n            g_ctx.shouldContinue = false;\n        }\n        case SDL_MOUSEWHEEL:\n        {\n            scrollDelta.x = event.wheel.x;\n            scrollDelta.y = event.wheel.y;\n            break;\n        }\n        }\n    }\n\n    int mouseX = 0;\n    int mouseY = 0;\n    Uint32 mouseState = SDL_GetMouseState(&mouseX, &mouseY);\n    Clay_Vector2 mousePosition = (Clay_Vector2){(float)mouseX, (float)mouseY};\n    Clay_SetPointerState(mousePosition, mouseState & SDL_BUTTON(1));\n\n    Clay_UpdateScrollContainers(\n        true,\n        (Clay_Vector2){scrollDelta.x, scrollDelta.y},\n        deltaTime);\n\n    SDL_GL_GetDrawableSize(g_ctx.sdlWindow, &g_ctx.screenWidth, &g_ctx.screenHeight);\n    glViewport(0, 0, g_ctx.screenWidth, g_ctx.screenHeight);\n    Clay_SetLayoutDimensions((Clay_Dimensions){(float)g_ctx.screenWidth, (float)g_ctx.screenHeight});\n\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    glDisable(GL_DEPTH_TEST);\n    glDepthMask(GL_FALSE); // Clay renderer is simple and never writes to depth buffer\n\n    Clay_RenderCommandArray cmds = CreateLayout();\n    uint64_t drawCalls1 = g_gles3.totalDrawCallsToOpenGl;\n    Gles3_Render(&g_gles3, cmds, g_stbFonts);\n    uint64_t drawCalls2 = g_gles3.totalDrawCallsToOpenGl;\n    g_drawCallsDuringLastFrame = drawCalls2 - drawCalls1;\n\n    // update FPS counter\n    Uint64 NOW2 = SDL_GetPerformanceCounter();\n\n    /* FPS based on simulation delta */\n    g_timeAccumulator += deltaTime;\n    g_frameCount++;\n\n    /* wall-clock frame time */\n    double frameSeconds =\n        (double)(NOW2 - NOW) /\n        (double)SDL_GetPerformanceFrequency();\n\n    g_wallTimeAccumulator += frameSeconds;\n\n    /* update text ONLY every 5 seconds */\n    double measureInterval = 3000.0;\n    double measuresPerSecond = 1000.0 / measureInterval;\n    if (g_timeAccumulator >= measureInterval)\n    {\n        g_fps = (g_frameCount / g_timeAccumulator) * 1000.0;\n\n        g_avgFrameMs = (g_wallTimeAccumulator / g_frameCount) * 1000.0;\n\n        g_fpsTextLen = (size_t)snprintf(\n            (char *)g_fpsText,\n            sizeof(g_fpsText),\n            \"FPS: %.3f | Avg frame: %.3f ms\",\n            g_fps,\n            g_avgFrameMs);\n\n        g_timeAccumulator = 0.0;\n        g_wallTimeAccumulator = 0.0;\n        g_frameCount = 0;\n    }\n\n    SDL_GL_SwapWindow(g_ctx.sdlWindow);\n}\n\n// Just initializes and spins the animation loop\nint main()\n{\n    initVideo(&g_ctx, 1280, 720);\n    init();\n\n    g_ctx.shouldContinue = true;\n#ifdef __EMSCRIPTEN__\n    emscripten_set_main_loop(loop, 0, 1);\n#else\n    while (g_ctx.shouldContinue)\n    {\n        loop();\n    }\n#endif\n}\n"
  },
  {
    "path": "examples/GLES3-SDL2-video-demo/.gitignore",
    "content": "/build/\n/website-demo-macos-sdl2*\n"
  },
  {
    "path": "examples/GLES3-SDL2-video-demo/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nset(CMAKE_C_STANDARD 99)\nproject(GLES3_SDL2_video_demo C)\n\n\n# -------------------------------------------------\n# FetchContent\n# -------------------------------------------------\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\n\n# -------------------------------------------------\n# STB (header-only)\n# -------------------------------------------------\nFetchContent_Declare(\n    stb\n    GIT_REPOSITORY https://github.com/nothings/stb.git\n    GIT_TAG master\n)\nFetchContent_MakeAvailable(stb)\n\n# -------------------------------------------------\n# SDL2\n# -------------------------------------------------\nFetchContent_Declare(\n    SDL2\n    GIT_REPOSITORY \"https://github.com/libsdl-org/SDL.git\"\n    GIT_TAG \"release-2.30.10\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(SDL2)\n\n# -------------------------------------------------\n# Executable\n# -------------------------------------------------\nadd_executable(GLES3_SDL2_video_demo main.c)\ntarget_compile_options(GLES3_SDL2_video_demo PUBLIC)\ntarget_include_directories(GLES3_SDL2_video_demo\n    PUBLIC\n        .  # This renderer\n        ../.. # Clay\n        ${stb_SOURCE_DIR} # STB header only depencency that does not have its own CMake build\n\n)\n\n# -------------------------------------------------\n# Link libraries\n# -------------------------------------------------\ntarget_link_libraries(GLES3_SDL2_video_demo PUBLIC\n    SDL2::SDL2main\n    SDL2::SDL2-static\n)\n\n# -------------------------------------------------\n# Platform-specific OpenGL / GLES\n# -------------------------------------------------\nfind_package(SDL2 REQUIRED)\nfind_library(OPENGL_FRAMEWORK OpenGL)\ntarget_link_libraries(GLES3_SDL2_video_demo\n    PRIVATE\n        ${OPENGL_FRAMEWORK}\n)\n\n# -------------------------------------------------\n# Build flags (kept minimal)\n# -------------------------------------------------\nif(MSVC)\n    set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\nelse()\n    set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\n    set(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\nendif()\n\n# -------------------------------------------------\n# Copy resources\n# -------------------------------------------------\nadd_custom_command(\n    TARGET GLES3_SDL2_video_demo POST_BUILD\n    COMMAND ${CMAKE_COMMAND} -E copy_directory\n    ${CMAKE_CURRENT_SOURCE_DIR}/resources\n    ${CMAKE_CURRENT_BINARY_DIR}/resources\n)\n"
  },
  {
    "path": "examples/GLES3-SDL2-video-demo/Makefile.emscripten",
    "content": "# vim: set tabstop=4 shiftwidth=4 expandtab noexpandtab:\n\n#\n# PROGRAM COMPONENTS\n# \n\nCXX = emcc\n\nCXXFLAGS = -std=c99\nCXXFLAGS += -O0\nCXXFLAGS += -I../..\nCXXFLAGS += -I./build/_deps/stb-src\n\nLDLIBS += -s USE_ZLIB=1\nLDLIBS += -s USE_SDL=2\nLDLIBS += -s FULL_ES2=1 -s USE_WEBGL2=1\nLDLIBS += -s ALLOW_MEMORY_GROWTH=1 -s GL_UNSAFE_OPTS=0\nLDLIBS += -s STACK_SIZE=2048kb\nLDLIBS += -s EXPORTED_FUNCTIONS=['_main']\nLDLIBS += -s ASSERTIONS=1 -s SAFE_HEAP=1\nLDLIBS += --preload-file $(PWD)/resources/Roboto-Regular.ttf@resources/Roboto-Regular.ttf\n\nmain:\n\tmkdir -p build/emscripten\n\ttime $(CXX) $(CXXFLAGS) \\\n\t\t$(PWD)/main.c \\\n\t    $(LDLIBS) -o build/emscripten/index.html \n\ntest:\n\tmake -f Makefile.emscripten main \\\n\t\t&& (cd build/emscripten && python3 -mhttp.server)\n\t\n.PHONY: main \n"
  },
  {
    "path": "examples/GLES3-SDL2-video-demo/Makefile.macos",
    "content": "# vim: set tabstop=4 shiftwidth=4 expandtab noexpandtab:\n\nCXX = clang\n\nCXXFLAGS = -std=c99\nCXXFLAGS += -g -O0 -fno-omit-frame-pointer\nCXXFLAGS += -ferror-limit=1\nCXXFLAGS += -I../..\nCXXFLAGS += -I./build/_deps/stb-src\nCXXFLAGS += -DGL_SILENCE_DEPRECATION\n\n# SDL2 (Homebrew)\nCXXFLAGS += -I$(shell brew --prefix sdl2)/include/SDL2\nLDLIBS   += -L$(shell brew --prefix sdl2)/lib -lSDL2\n\n# macOS system frameworks (OpenGL needs these)\nLDLIBS += -framework OpenGL\nLDLIBS += -framework Cocoa\nLDLIBS += -framework IOKit\nLDLIBS += -framework CoreVideo\n\nmain:\n\tmkdir -p build\n\n\ttime $(CXX) $(CXXFLAGS) \\\n\t\t$(PWD)/main.c \\\n\t\t$(LDLIBS) \\\n\t\t-o build/website-demo-macos-sdl2\n"
  },
  {
    "path": "examples/GLES3-SDL2-video-demo/README.md",
    "content": "GLES3 Renderer Video Demo (Using SDL2)\n======================================\n\nThis directory contains a standard Video-Demo example\nusing work-in-progress GLES3 renderer.\nWhile it still needs refinement, the renderer is already functional and demonstrates the core rendering pipeline.\n\nCurrent features\n\n-\tSupports all draw commands except custom.\n-\tIn the best-case scenario (no clipping):\n-\tAll quad-based commands (Rectangle, Image, Border) are rendered in a single draw call.\n-\tAll glyphs belonging to the same font are rendered in one instanced draw call.\n-\tWhen clipping (scissoring) is used:\n-\tThe renderer flushes draw calls before and after each scissor region.\n-\tSupports up to 4 fonts and 4 image textures.\n-\tImage textures may also be used as texture atlases.\n-\tCustom UserData provides per-image UV coordinates, allowing multiple images to share a single OpenGL texture.\n-\tUses stb_image.h and stb_truetype.h as single-header dependencies for asset loading.\n-\tThe loading layer is modular and can be replaced with a different asset pipeline if needed.\n\nCurrently builds on:\n-\tEmscripten\n-\tclang++ / macOS\n-\tCMake support is not available yet.\n\nWindowing and platform support\n\nThis example uses SDL2, but the renderer is framework agnostic.\n\nFor sake of example you can also build it with\nhand-crafted Makefile.macos\n\n    make -f Makefile.emscripten test\n\nand then navigate to http://localhost:8080\nOn Emscripten it works well.\n\n"
  },
  {
    "path": "examples/GLES3-SDL2-video-demo/main.c",
    "content": "#include <SDL.h>\n\n#define STB_IMAGE_IMPLEMENTATION\n#define STB_TRUETYPE_IMPLEMENTATION\n#define CLAY_IMPLEMENTATION\n#define CLAY_RENDERER_GLES3_IMPLEMENTATION\n\n#include <clay.h>\n\n#include \"../../renderers/GLES3/clay_renderer_gles3.h\"\n#include \"../shared-layouts/clay-video-demo.c\"\n#include \"../../renderers/GLES3/clay_renderer_gles3_loader_stb.c\"\n\ntypedef struct VideoCtx\n{\n    int shouldContinue;\n    SDL_Window *sdlWindow;\n    SDL_GLContext sdlContext;\n    int screenWidth, screenHeight;\n} VideoCtx;\n\nVideoCtx g_ctx;\n\nstatic int initVideo(VideoCtx *ctx, const int initialWidth, const int initialHeight)\n{\n    SDL_Init(SDL_INIT_VIDEO);\n\n#if defined(__EMSCRIPTEN__)\n    // OpenGL ES 3 profile\n    SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, \"1\");\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);\n#else\n    // Apple MacOs will use it own legacy desktop GL instead\n    // I know, I lied, I said this was an GLES3\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);\n    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);\n#endif\n\n    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);\n    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);\n    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);\n    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);\n    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);\n\n    g_ctx.sdlWindow = SDL_CreateWindow(\n        \"SDL2 GLES3\",\n        SDL_WINDOWPOS_CENTERED,\n        SDL_WINDOWPOS_CENTERED,\n        initialWidth,\n        initialHeight,\n        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN\n    );\n    g_ctx.sdlContext = SDL_GL_CreateContext(g_ctx.sdlWindow);\n\n    SDL_ShowWindow(g_ctx.sdlWindow);\n    SDL_Delay(1);\n    SDL_GL_GetDrawableSize(g_ctx.sdlWindow, &g_ctx.screenWidth, &g_ctx.screenHeight);\n    glViewport(0, 0, g_ctx.screenWidth, g_ctx.screenHeight);\n\n    glEnable(GL_BLEND);\n    // Enables blending, which allows transparent textures to be rendered properly.\n    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n    // Sets the blending function.\n    // - `GL_SRC_ALPHA`: Uses the alpha value of the source (texture or color).\n    // - `GL_ONE_MINUS_SRC_ALPHA`: Makes the destination color blend with the background based on alpha.\n    // This is commonly used for standard transparency effects.\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    glEnable(GL_DEPTH_TEST);\n    // Enables depth testing, ensuring that objects closer to the camera are drawn in front of those farther away.\n    // This prevents objects from rendering incorrectly based on draw order.\n\n    return 1;\n}\n\nvoid My_ErrorHandler(Clay_ErrorData errorData)\n{\n    printf(\"[ClaY ErroR] %s\", errorData.errorText.chars);\n}\n\nStb_FontData g_stbFonts[MAX_FONTS]; // Fonts userData\nGles3_Renderer g_gles3;             // The renderer itself\n\nUint64 NOW = 0;\nUint64 LAST = 0;\ndouble deltaTime = 0;\n\n// is executed before everything\nvoid init()\n{\n    size_t clayRequiredMemory = Clay_MinMemorySize();\n    g_gles3.clayMemory = (Clay_Arena){\n        .capacity = clayRequiredMemory,\n        .memory = (char *)malloc(clayRequiredMemory),\n    };\n    Clay_Context *clayCtx = Clay_Initialize(\n        g_gles3.clayMemory,\n        (Clay_Dimensions){\n            .width = (float)g_ctx.screenWidth,\n            .height = (float)g_ctx.screenHeight,\n        },\n        (Clay_ErrorHandler){\n            .errorHandlerFunction = My_ErrorHandler,\n        });\n\n    // Note that MeasureText has to be set after the Context is set!\n    Clay_SetCurrentContext(clayCtx);\n    Clay_SetMeasureTextFunction(Stb_MeasureText, &g_stbFonts);\n    Gles3_SetRenderTextFunction(&g_gles3, Stb_RenderText, &g_stbFonts);\n\n    Gles3_Initialize(&g_gles3, 4096);\n\n    int atlasW = 1024;\n    int atlasH = 1024;\n    if (!Stb_LoadFont(\n            &g_gles3.fontTextures[0],\n            &g_stbFonts[0],\n            \"resources/Roboto-Regular.ttf\",\n            24.0f, // bake pixel height\n            atlasW,\n            atlasH))\n        abort();\n\n    Clay_SetDebugModeEnabled(true);\n}\n\nvoid loop()\n{\n\n    glClearColor(0.1f, 0.2f, 0.1f, 1.0f);\n\n    Clay_Vector2 scrollDelta = {};\n    SDL_Event event;\n    while (SDL_PollEvent(&event))\n    {\n        switch (event.type)\n        {\n        case SDL_QUIT:\n        {\n            g_ctx.shouldContinue = false;\n        }\n        case SDL_MOUSEWHEEL:\n        {\n            scrollDelta.x = event.wheel.x;\n            scrollDelta.y = event.wheel.y;\n            break;\n        }\n        }\n    }\n    LAST = NOW;\n    NOW = SDL_GetPerformanceCounter();\n    deltaTime = (double)((NOW - LAST) * 1000 / (double)SDL_GetPerformanceFrequency());\n\n    int mouseX = 0;\n    int mouseY = 0;\n    Uint32 mouseState = SDL_GetMouseState(&mouseX, &mouseY);\n    Clay_Vector2 mousePosition = (Clay_Vector2){(float)mouseX, (float)mouseY};\n    Clay_SetPointerState(mousePosition, mouseState & SDL_BUTTON(1));\n\n    Clay_UpdateScrollContainers(\n        true,\n        (Clay_Vector2){scrollDelta.x, scrollDelta.y},\n        deltaTime);\n\n    SDL_GL_GetDrawableSize(g_ctx.sdlWindow, &g_ctx.screenWidth, &g_ctx.screenHeight);\n    glViewport(0, 0, g_ctx.screenWidth, g_ctx.screenHeight);\n    Clay_SetLayoutDimensions((Clay_Dimensions){(float)g_ctx.screenWidth, (float)g_ctx.screenHeight});\n\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    glDisable(GL_DEPTH_TEST);\n    glDepthMask(GL_FALSE); // Clay renderer is simple and never writes to depth buffer\n\n    ClayVideoDemo_Data data = ClayVideoDemo_Initialize();\n    Clay_RenderCommandArray cmds = ClayVideoDemo_CreateLayout(&data);\n\n    Gles3_Render(&g_gles3, cmds, g_stbFonts);\n\n    SDL_GL_SwapWindow(g_ctx.sdlWindow);\n}\n\n// Just initializes and spins the animation loop\nint main()\n{\n    initVideo(&g_ctx, 1280, 720);\n    init();\n\n    g_ctx.shouldContinue = true;\n#ifdef __EMSCRIPTEN__\n    emscripten_set_main_loop(loop, 0, 1);\n#else\n    while (g_ctx.shouldContinue)\n    {\n        loop();\n    }\n#endif\n}"
  },
  {
    "path": "examples/SDL2-video-demo/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(SDL2_video_demo C)\nset(CMAKE_C_STANDARD 99)\n\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\n\nFetchContent_Declare(\n    SDL2\n    GIT_REPOSITORY \"https://github.com/libsdl-org/SDL.git\"\n    GIT_TAG \"release-2.30.10\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(SDL2)\n\nFetchContent_Declare(\n    SDL2_ttf\n    GIT_REPOSITORY \"https://github.com/libsdl-org/SDL_ttf.git\"\n    GIT_TAG \"release-2.22.0\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(SDL2_ttf)\n\nFetchContent_Declare(\n    SDL2_image\n    GIT_REPOSITORY \"https://github.com/libsdl-org/SDL_image.git\"\n    GIT_TAG \"release-2.8.4\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(SDL2_image)\n\nadd_executable(SDL2_video_demo main.c)\n\ntarget_compile_options(SDL2_video_demo PUBLIC)\ntarget_include_directories(SDL2_video_demo PUBLIC .)\n\ntarget_link_libraries(SDL2_video_demo PUBLIC\n    SDL2::SDL2main\n    SDL2::SDL2-static\n    SDL2_ttf::SDL2_ttf-static\n    SDL2_image::SDL2_image-static\n)\n\nif(MSVC)\n    set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\nelse()\n    set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\n    set(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\nendif()\n\nadd_custom_command(\n    TARGET SDL2_video_demo POST_BUILD\n    COMMAND ${CMAKE_COMMAND} -E copy_directory\n    ${CMAKE_CURRENT_SOURCE_DIR}/resources\n    ${CMAKE_CURRENT_BINARY_DIR}/resources\n)\n"
  },
  {
    "path": "examples/SDL2-video-demo/main.c",
    "content": "#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n#include \"../../renderers/SDL2/clay_renderer_SDL2.c\"\n\n#include <SDL.h>\n#include <SDL_ttf.h>\n\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include \"../shared-layouts/clay-video-demo.c\"\n\nSDL_Surface *sample_image;\n\nvoid HandleClayErrors(Clay_ErrorData errorData) {\n    printf(\"%s\", errorData.errorText.chars);\n}\n\n\nstruct ResizeRenderData_ {\n    SDL_Window* window;\n    int windowWidth;\n    int windowHeight;\n    ClayVideoDemo_Data demoData;\n    SDL_Renderer* renderer;\n    SDL2_Font* fonts;\n};\ntypedef struct ResizeRenderData_ ResizeRenderData;\n\nint resizeRendering(void* userData, SDL_Event* event) {\n    ResizeRenderData *actualData = userData;\n    if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_EXPOSED) {\n        SDL_Window* window          = actualData->window;\n        int windowWidth             = actualData->windowWidth;\n        int windowHeight            = actualData->windowHeight;\n        ClayVideoDemo_Data demoData = actualData->demoData;\n        SDL_Renderer* renderer      = actualData->renderer;\n        SDL2_Font* fonts            = actualData->fonts;\n\n        SDL_GetWindowSize(window, &windowWidth, &windowHeight);\n        Clay_SetLayoutDimensions((Clay_Dimensions) { (float)windowWidth, (float)windowHeight });\n\n        Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData);\n        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);\n        SDL_RenderClear(renderer);\n\n        Clay_SDL2_Render(renderer, renderCommands, fonts);\n\n        SDL_RenderPresent(renderer);\n    }\n    return 0;\n}\n\nint main(int argc, char *argv[]) {\n    if (SDL_Init(SDL_INIT_VIDEO) < 0) {\n        fprintf(stderr, \"Error: could not initialize SDL: %s\\n\", SDL_GetError());\n        return 1;\n    }\n    if (TTF_Init() < 0) {\n        fprintf(stderr, \"Error: could not initialize TTF: %s\\n\", TTF_GetError());\n        return 1;\n    }\n    if (IMG_Init(IMG_INIT_PNG) < 0) {\n        fprintf(stderr, \"Error: could not initialize IMG: %s\\n\", IMG_GetError());\n        return 1;\n    }\n\n    TTF_Font *font = TTF_OpenFont(\"resources/Roboto-Regular.ttf\", 16);\n    if (!font) {\n        fprintf(stderr, \"Error: could not load font: %s\\n\", TTF_GetError());\n        return 1;\n    }\n\n    SDL2_Font fonts[1] = {};\n\n    fonts[FONT_ID_BODY_16] = (SDL2_Font) {\n        .fontId = FONT_ID_BODY_16,\n        .font = font,\n    };\n\n    sample_image = IMG_Load(\"resources/sample.png\");\n\n    SDL_Window *window = NULL;\n    SDL_Renderer *renderer = NULL;\n  \n    SDL_SetHint(SDL_HINT_RENDER_DRIVER, \"opengl\"); //for antialiasing\n    window = SDL_CreateWindow(\"SDL\", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);\n    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); //for antialiasing\n\n    bool enableVsync = false;\n    if(enableVsync){ renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);} //\"SDL_RENDERER_ACCELERATED\" is for antialiasing\n    else{renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);}\n    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); //for alpha blending\n\n    uint64_t totalMemorySize = Clay_MinMemorySize();\n    Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));\n\n    int windowWidth = 0;\n    int windowHeight = 0;\n    SDL_GetWindowSize(window, &windowWidth, &windowHeight);\n    Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)windowWidth, (float)windowHeight }, (Clay_ErrorHandler) { HandleClayErrors });\n\n    Clay_SetMeasureTextFunction(SDL2_MeasureText, &fonts);\n\n    Uint64 NOW = SDL_GetPerformanceCounter();\n    Uint64 LAST = 0;\n    double deltaTime = 0;\n    ClayVideoDemo_Data demoData = ClayVideoDemo_Initialize();\n\n    \n    ResizeRenderData userData = {\n        window, // SDL_Window*\n        windowWidth, // int\n        windowHeight, // int\n        demoData, // CustomShit\n        renderer, // SDL_Renderer*\n        fonts // SDL2_Font[1]\n    };\n    // add an event watcher that will render the screen while youre dragging the window to different sizes\n    SDL_AddEventWatch(resizeRendering, &userData);\n    \n    while (true) {\n        Clay_Vector2 scrollDelta = {};\n        SDL_Event event;\n        while (SDL_PollEvent(&event)) {\n            switch (event.type) {\n                case SDL_QUIT: { goto quit; }\n                case SDL_MOUSEWHEEL: {\n                    scrollDelta.x = event.wheel.x;\n                    scrollDelta.y = event.wheel.y;\n                    break;\n                }\n            }\n        }\n        LAST = NOW;\n        NOW = SDL_GetPerformanceCounter();\n        deltaTime = (double)((NOW - LAST)*1000 / (double)SDL_GetPerformanceFrequency() );\n        printf(\"%f\\n\", deltaTime);\n\n        int mouseX = 0;\n        int mouseY = 0;\n        Uint32 mouseState = SDL_GetMouseState(&mouseX, &mouseY);\n        Clay_Vector2 mousePosition = (Clay_Vector2){ (float)mouseX, (float)mouseY };\n        Clay_SetPointerState(mousePosition, mouseState & SDL_BUTTON(1));\n\n        Clay_UpdateScrollContainers(\n            true,\n            (Clay_Vector2) { scrollDelta.x, scrollDelta.y },\n            deltaTime\n        );\n        \n        SDL_GetWindowSize(window, &windowWidth, &windowHeight);\n        Clay_SetLayoutDimensions((Clay_Dimensions) { (float)windowWidth, (float)windowHeight });\n\n        Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData);\n        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);\n        SDL_RenderClear(renderer);\n\n        Clay_SDL2_Render(renderer, renderCommands, fonts);\n\n        SDL_RenderPresent(renderer);\n    }\n\nquit:\n    SDL_DestroyRenderer(renderer);\n    SDL_DestroyWindow(window);\n    IMG_Quit();\n    TTF_Quit();\n    SDL_Quit();\n    return 0;\n}\n\n"
  },
  {
    "path": "examples/SDL3-simple-demo/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\n\n# Project setup\nproject(clay_examples_sdl3_simple_demo C)\nset(CMAKE_C_STANDARD 99)\n\nset(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\nset(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\n\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\n\n# Download SDL3\nFetchContent_Declare(\n\tSDL\n\tGIT_REPOSITORY https://github.com/libsdl-org/SDL.git\n\tGIT_TAG release-3.2.4\n\tGIT_SHALLOW TRUE\n\tGIT_PROGRESS TRUE\n)\nmessage(STATUS \"Using SDL via FetchContent\")\nFetchContent_MakeAvailable(SDL)\nset_property(DIRECTORY \"${sdl_SOURCE_DIR}\" PROPERTY EXCLUDE_FROM_ALL TRUE)\n\n# Download SDL_ttf\nFetchContent_Declare(\n\tSDL_ttf\n\tGIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git\n\tGIT_TAG release-3.2.2\n\tGIT_SHALLOW TRUE\n\tGIT_PROGRESS TRUE\n)\nmessage(STATUS \"Using SDL_ttf via FetchContent\")\nFetchContent_MakeAvailable(SDL_ttf)\nset_property(DIRECTORY \"${sdl_ttf_SOURCE_DIR}\" PROPERTY EXCLUDE_FROM_ALL TRUE)\n\n# Download SDL_image\nFetchContent_Declare(\n\tSDL_image\n\tGIT_REPOSITORY \"https://github.com/libsdl-org/SDL_image.git\"\n\tGIT_TAG release-3.2.0\n\tGIT_SHALLOW TRUE\n\tGIT_PROGRESS TRUE\n)\nmessage(STATUS \"Using SDL_image via FetchContent\")\nFetchContent_MakeAvailable(SDL_image)\nset_property(DIRECTORY \"${SDL_image_SOURCE_DIR}\" PROPERTY EXCLUDE_FROM_ALL TRUE)\n\n# Example executable\nadd_executable(${PROJECT_NAME} main.c)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE\n\tSDL3::SDL3\n\tSDL3_ttf::SDL3_ttf\n\tSDL3_image::SDL3_image\n)\n\nadd_custom_command(\n\tTARGET ${PROJECT_NAME} POST_BUILD\n\tCOMMAND ${CMAKE_COMMAND} -E copy_directory\n\t\t${CMAKE_CURRENT_SOURCE_DIR}/resources\n\t\t${CMAKE_CURRENT_BINARY_DIR}/resources\n)\n"
  },
  {
    "path": "examples/SDL3-simple-demo/main.c",
    "content": "#define SDL_MAIN_USE_CALLBACKS\n#include <SDL3/SDL_main.h>\n#include <SDL3/SDL.h>\n#include <SDL3_ttf/SDL_ttf.h>\n\n#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n\n#include <stdio.h>\n\n#include \"../../renderers/SDL3/clay_renderer_SDL3.c\"\n#include \"../shared-layouts/clay-video-demo.c\"\n\nstatic const Uint32 FONT_ID = 0;\n\nstatic const Clay_Color COLOR_ORANGE    = (Clay_Color) {225, 138, 50, 255};\nstatic const Clay_Color COLOR_BLUE      = (Clay_Color) {111, 173, 162, 255};\nstatic const Clay_Color COLOR_LIGHT     = (Clay_Color) {224, 215, 210, 255};\n\ntypedef struct app_state {\n    SDL_Window *window;\n    Clay_SDL3RendererData rendererData;\n    ClayVideoDemo_Data demoData;\n} AppState;\n\nSDL_Texture *sample_image;\nbool show_demo = true;\n\nstatic inline Clay_Dimensions SDL_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData)\n{\n    TTF_Font **fonts = userData;\n    TTF_Font *font = fonts[config->fontId];\n    int width, height;\n\n    TTF_SetFontSize(font, config->fontSize);\n    if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) {\n        SDL_LogError(SDL_LOG_CATEGORY_ERROR, \"Failed to measure text: %s\", SDL_GetError());\n    }\n\n    return (Clay_Dimensions) { (float) width, (float) height };\n}\n\nvoid HandleClayErrors(Clay_ErrorData errorData) {\n    printf(\"%s\", errorData.errorText.chars);\n}\n\n\nClay_RenderCommandArray ClayImageSample_CreateLayout() {\n    Clay_BeginLayout();\n\n    Clay_Sizing layoutExpand = {\n        .width = CLAY_SIZING_GROW(0),\n        .height = CLAY_SIZING_GROW(0)\n    };\n\n    CLAY(CLAY_ID(\"OuterContainer\"), {\n        .layout = {\n            .layoutDirection = CLAY_TOP_TO_BOTTOM,\n            .sizing = layoutExpand,\n            .padding = CLAY_PADDING_ALL(16),\n            .childGap = 16\n        }\n    }) {\n        CLAY(CLAY_ID(\"SampleImage\"), {\n            .layout = {\n                .sizing = layoutExpand\n            },\n            .aspectRatio = { 23.0 / 42.0 },\n            .image = {\n                .imageData = sample_image,\n            }\n        });\n    }\n\n    return Clay_EndLayout();\n}\n\n\nSDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])\n{\n    (void) argc;\n    (void) argv;\n\n    if (!TTF_Init()) {\n        return SDL_APP_FAILURE;\n    }\n\n    AppState *state = SDL_calloc(1, sizeof(AppState));\n    if (!state) {\n        return SDL_APP_FAILURE;\n    }\n    *appstate = state;\n\n    if (!SDL_CreateWindowAndRenderer(\"Clay Demo\", 640, 480, 0, &state->window, &state->rendererData.renderer)) {\n        SDL_LogError(SDL_LOG_CATEGORY_ERROR, \"Failed to create window and renderer: %s\", SDL_GetError());\n        return SDL_APP_FAILURE;\n    }\n    SDL_SetWindowResizable(state->window, true);\n\n    state->rendererData.textEngine = TTF_CreateRendererTextEngine(state->rendererData.renderer);\n    if (!state->rendererData.textEngine) {\n        SDL_LogError(SDL_LOG_CATEGORY_ERROR, \"Failed to create text engine from renderer: %s\", SDL_GetError());\n        return SDL_APP_FAILURE;\n    }\n\n    state->rendererData.fonts = SDL_calloc(1, sizeof(TTF_Font *));\n    if (!state->rendererData.fonts) {\n        SDL_LogError(SDL_LOG_CATEGORY_ERROR, \"Failed to allocate memory for the font array: %s\", SDL_GetError());\n        return SDL_APP_FAILURE;\n    }\n\n    TTF_Font *font = TTF_OpenFont(\"resources/Roboto-Regular.ttf\", 24);\n    if (!font) {\n        SDL_LogError(SDL_LOG_CATEGORY_ERROR, \"Failed to load font: %s\", SDL_GetError());\n        return SDL_APP_FAILURE;\n    }\n\n    state->rendererData.fonts[FONT_ID] = font;\n\n    sample_image = IMG_LoadTexture(state->rendererData.renderer, \"resources/sample.png\");\n    if (!sample_image) {\n        SDL_LogError(SDL_LOG_CATEGORY_ERROR, \"Failed to load image: %s\", SDL_GetError());\n        return SDL_APP_FAILURE;\n    }\n\n    /* Initialize Clay */\n    uint64_t totalMemorySize = Clay_MinMemorySize();\n    Clay_Arena clayMemory = (Clay_Arena) {\n        .memory = SDL_malloc(totalMemorySize),\n        .capacity = totalMemorySize\n    };\n\n    int width, height;\n    SDL_GetWindowSize(state->window, &width, &height);\n    Clay_Initialize(clayMemory, (Clay_Dimensions) { (float) width, (float) height }, (Clay_ErrorHandler) { HandleClayErrors });\n    Clay_SetMeasureTextFunction(SDL_MeasureText, state->rendererData.fonts);\n\n    state->demoData = ClayVideoDemo_Initialize();\n\n    *appstate = state;\n    return SDL_APP_CONTINUE;\n}\n\nSDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)\n{\n    SDL_AppResult ret_val = SDL_APP_CONTINUE;\n\n    switch (event->type) {\n        case SDL_EVENT_QUIT:\n            ret_val = SDL_APP_SUCCESS;\n            break;\n        case SDL_EVENT_KEY_UP:\n            if (event->key.scancode == SDL_SCANCODE_SPACE) {\n                show_demo = !show_demo;\n            }\n            break;\n        case SDL_EVENT_WINDOW_RESIZED:\n            Clay_SetLayoutDimensions((Clay_Dimensions) { (float) event->window.data1, (float) event->window.data2 });\n            break;\n        case SDL_EVENT_MOUSE_MOTION:\n            Clay_SetPointerState((Clay_Vector2) { event->motion.x, event->motion.y },\n                                 event->motion.state & SDL_BUTTON_LMASK);\n            break;\n        case SDL_EVENT_MOUSE_BUTTON_DOWN:\n            Clay_SetPointerState((Clay_Vector2) { event->button.x, event->button.y },\n                                 event->button.button == SDL_BUTTON_LEFT);\n            break;\n        case SDL_EVENT_MOUSE_WHEEL:\n            Clay_UpdateScrollContainers(true, (Clay_Vector2) { event->wheel.x, event->wheel.y }, 0.01f);\n            break;\n        default:\n            break;\n    };\n\n    return ret_val;\n}\n\nSDL_AppResult SDL_AppIterate(void *appstate)\n{\n    AppState *state = appstate;\n\n    Clay_RenderCommandArray render_commands = (show_demo\n        ? ClayVideoDemo_CreateLayout(&state->demoData)\n        : ClayImageSample_CreateLayout()\n    );\n\n    SDL_SetRenderDrawColor(state->rendererData.renderer, 0, 0, 0, 255);\n    SDL_RenderClear(state->rendererData.renderer);\n\n    SDL_Clay_RenderClayCommands(&state->rendererData, &render_commands);\n\n    SDL_RenderPresent(state->rendererData.renderer);\n\n    return SDL_APP_CONTINUE;\n}\n\nvoid SDL_AppQuit(void *appstate, SDL_AppResult result)\n{\n    (void) result;\n\n    if (result != SDL_APP_SUCCESS) {\n        SDL_LogError(SDL_LOG_CATEGORY_ERROR, \"Application failed to run\");\n    }\n\n    AppState *state = appstate;\n\n    if (sample_image) {\n        SDL_DestroyTexture(sample_image);\n    }\n\n    if (state) {\n        if (state->rendererData.renderer)\n            SDL_DestroyRenderer(state->rendererData.renderer);\n\n        if (state->window)\n            SDL_DestroyWindow(state->window);\n\n        if (state->rendererData.fonts) {\n            for(size_t i = 0; i < sizeof(state->rendererData.fonts) / sizeof(*state->rendererData.fonts); i++) {\n                TTF_CloseFont(state->rendererData.fonts[i]);\n            }\n\n            SDL_free(state->rendererData.fonts);\n        }\n\n        if (state->rendererData.textEngine)\n            TTF_DestroyRendererTextEngine(state->rendererData.textEngine);\n\n        SDL_free(state);\n    }\n    TTF_Quit();\n}\n"
  },
  {
    "path": "examples/cairo-pdf-rendering/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(clay_examples_cairo_pdf_rendering C)\nset(CMAKE_C_STANDARD 99)\n\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/../../cmake\")\n\nadd_executable(clay_examples_cairo_pdf_rendering main.c)\n\nfind_package(Cairo REQUIRED)\n\ntarget_compile_options(clay_examples_cairo_pdf_rendering PUBLIC)\ntarget_include_directories(clay_examples_cairo_pdf_rendering PUBLIC . ${CAIRO_INCLUDE_DIRS})\n\ntarget_link_libraries(clay_examples_cairo_pdf_rendering PUBLIC Cairo::Cairo)\nset(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\nset(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\n\nadd_custom_command(\n        TARGET clay_examples_cairo_pdf_rendering POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy_directory\n        ${CMAKE_CURRENT_SOURCE_DIR}/resources\n        ${CMAKE_CURRENT_BINARY_DIR}/resources)\n"
  },
  {
    "path": "examples/cairo-pdf-rendering/main.c",
    "content": "// Copyright (c) 2024 Justin Andreas Lacoste (@27justin)\n//\n// This software is provided 'as-is', without any express or implied warranty.\n// In no event will the authors be held liable for any damages arising from the\n// use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n//     1. The origin of this software must not be misrepresented; you must not\n//     claim that you wrote the original software. If you use this software in a\n//     product, an acknowledgment in the product documentation would be\n//     appreciated but is not required.\n//\n//     2. Altered source versions must be plainly marked as such, and must not\n//     be misrepresented as being the original software.\n//\n//     3. This notice may not be removed or altered from any source\n//     distribution.\n//\n// SPDX-License-Identifier: Zlib\n\n#include <stdlib.h>\n\n// The renderer includes clay.h while also providing the\n// CLAY_IMPLEMENTATION\n#include \"../../renderers/cairo/clay_renderer_cairo.c\"\n\n// cairo-pdf, though this is optional and not required if you,\n// e.g. render PNGs.\n#include <cairo/cairo-pdf.h>\n\nconst uint16_t FONT_CALLISTOGA = 0;\nconst uint16_t FONT_QUICKSAND = 0;\n\n// Layout the first page.\nvoid Layout() {\n\tstatic Clay_Color PRIMARY = { 0xa8, 0x42, 0x1c, 255 };\n\tstatic Clay_Color BACKGROUND = { 0xF4, 0xEB, 0xE6, 255 };\n\tstatic Clay_Color ACCENT = { 0xFA, 0xE0, 0xD4, 255 };\n\n\tCLAY_AUTO_ID({\n        .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) },\n\t\t.layoutDirection = CLAY_TOP_TO_BOTTOM },\n\t\t.backgroundColor = BACKGROUND\n    }) {\n\t\tCLAY(CLAY_ID(\"PageMargins\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) },\n             .padding = { 70, 70, 50, 50 }, // Some nice looking page margins\n             .layoutDirection = CLAY_TOP_TO_BOTTOM,\n             .childGap = 10}\n        }) {\n\t\t\t// Section Title\n\t\t\tCLAY_TEXT(CLAY_STRING(\"Features Overview\"), CLAY_TEXT_CONFIG({ .fontId = FONT_CALLISTOGA, .textColor = PRIMARY, .fontSize = 24 }));\n\n\t\t\t// Feature Box\n\t\t\tCLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }, .childGap = 10 }}) {\n\t\t\t\tCLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }}, .backgroundColor = ACCENT, .cornerRadius = CLAY_CORNER_RADIUS(12) }) {\n\t\t\t\t\tCLAY_AUTO_ID({ .layout = {.padding = CLAY_PADDING_ALL(20), .childGap = 4, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {\n\t\t\t\t\t\tCLAY_TEXT(CLAY_STRING(\"- High performance\"),\n\t\t\t\t\t\t\t\t  CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));\n\t\t\t\t\t\tCLAY_TEXT(CLAY_STRING(\"- Declarative syntax\"),\n\t\t\t\t\t\t\t\t  CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));\n\t\t\t\t\t\tCLAY_TEXT(CLAY_STRING(\"- Flexbox-style responsive layout\"),\n\t\t\t\t\t\t\t\t  CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));\n\t\t\t\t\t\tCLAY_TEXT(CLAY_STRING(\"- Single .h file for C/C++\"),\n\t\t\t\t\t\t\t\t  CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));\n\t\t\t\t\t\tCLAY_TEXT(CLAY_STRING(\"- And now with cairo!\"),\n\t\t\t\t\t\t\t\t  CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tCLAY_AUTO_ID({\n                    .layout = {\n                        .sizing = {CLAY_SIZING_FIT(0), CLAY_SIZING_GROW(0)},\n                        .padding = CLAY_PADDING_ALL(10),\n                        .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                        .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER },\n                        .childGap = 4\n                    },\n                    .backgroundColor = ACCENT,\n                    .cornerRadius = CLAY_CORNER_RADIUS(8)\n                }) {\n\t\t\t\t\t// Profile picture\n\t\t\t\t\tCLAY_AUTO_ID({ .layout = {\n                        .sizing = {CLAY_SIZING_FIT(0), CLAY_SIZING_GROW(0)},\n                        .padding = { 30, 30, 0, 0 },\n                        .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                        .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }},\n                        .border = { .color = PRIMARY, .width = 2, 2, 2, 2 }, .cornerRadius = 10\n                    }) {\n\t\t\t\t\t\tCLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_FIXED(32), CLAY_SIZING_FIXED(32) } }, .image = { .imageData = \"resources/check.png\" }});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tCLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(16) } }});\n\n\t\t\tCLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childGap = 10, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {\n\t\t\t\tCLAY_TEXT(CLAY_STRING(\"Cairo\"), CLAY_TEXT_CONFIG({ .fontId = FONT_CALLISTOGA, .fontSize = 24, .textColor = PRIMARY }));\n\t\t\t\tCLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(10) }, .backgroundColor = ACCENT, .cornerRadius = 10 }) {\n\t\t\t\t\tCLAY_TEXT(CLAY_STRING(\"Officiis quia quia qui inventore ratione voluptas et. Quidem sunt unde similique. Qui est et exercitationem cumque harum illum. Numquam placeat aliquid quo voluptatem. \"\n\t\t\t\t\t\t\t\t\t\t  \"Deleniti saepe nihil exercitationem nemo illo. Consequatur beatae repellat provident similique. Provident qui exercitationem deserunt sapiente. Quam qui dolor corporis odit. \"\n\t\t\t\t\t\t\t\t\t\t  \"Assumenda corrupti sunt culpa pariatur. Vero sit ut minima. In est consequatur minus et cum sint illum aperiam. Qui ipsa quas nisi omnis aut quia nobis. \"\n\t\t\t\t\t\t\t\t\t\t  \"Corporis deserunt eum mollitia modi rerum voluptas. Expedita non ab esse. Sit voluptates eos voluptatem labore aspernatur quia eum. Modi cumque atque non. Sunt officiis corrupti neque ut inventore excepturi rem minima. Possimus sed soluta qui ea aut ipsum laborum fugit. \"\n\t\t\t\t\t\t\t\t\t\t  \"Voluptate eum consectetur non. Quo autem voluptate soluta atque dolorum maxime. Officiis inventore omnis eveniet beatae ipsa optio. Unde voluptatum ut autem quia sit sit et. Ut inventore qui quia totam consequatur. Sit ea consequatur omnis rerum nulla aspernatur deleniti.\"), CLAY_TEXT_CONFIG({ .fontId = FONT_QUICKSAND, .fontSize = 16, .textColor = PRIMARY, .lineHeight = 16 }));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid HandleClayErrors(Clay_ErrorData errorData) {\n    printf(\"%s\", errorData.errorText.chars);\n}\n\nint main(void) {\n\t// First we set up our cairo surface.\n\t// In this example we will use the PDF backend,\n\t// but you should be able to use any of them.\n\t// Guaranteed to be working are: PDF, PNG\n\n\t// Create a PDF surface that is the same size as a DIN A4 sheet\n\t// When using the PDF backend, cairo calculates in points (1 point == 1/72.0 inch)\n\tdouble width = (21.0 / 2.54) * 72,  // cm in points\n\t\t   height = (29.7 / 2.54) * 72;\n\n\tcairo_surface_t *surface = cairo_pdf_surface_create(\"output.pdf\", width, height);\n\tcairo_t *cr = cairo_create(surface);\n\tcairo_surface_destroy(surface); // Drop reference\n\n\t// Initialize internal global variable with `cr`.\n\t// We require some kind of global reference to a valid\n\t// cairo instance to properly measure text.\n\t// Note that due to this, this interface is not thread-safe!\n\tClay_Cairo_Initialize(cr);\n\n\tuint64_t totalMemorySize = Clay_MinMemorySize();\n\tClay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));\n\n\t// We initialize Clay with the same size\n\tClay_Initialize(clayMemory, (Clay_Dimensions) { width, height }, (Clay_ErrorHandler) { HandleClayErrors });\n\n    char** fonts = (char*[]) {\n        \"Callistoga\",\n        \"Quicksand Semibold\"\n    };\n\n    Clay_SetMeasureTextFunction(Clay_Cairo_MeasureText, fonts);\n\n\tClay_BeginLayout();\n\n\t// Here you can now create the declarative clay layout.\n\t// Moved into a separate function for brevity.\n\tLayout();\n\n\tClay_RenderCommandArray commands = Clay_EndLayout();\n\t// Pass our layout to the cairo backend\n\tClay_Cairo_Render(commands, fonts);\n\n\t// To keep this example short, we will not emit a second page in the PDF.\n\t// But to do so, you have to\n\t// 1. cairo_show_page(cr)\n\t// 2. Clay_BeginLayout();\n\t// 3. Create your layout\n\t// 4. commands = Clay_EndLayout();\n\t// 5. Clay_Cairo_Render(commands);\n\n\tcairo_destroy(cr);\n\treturn 0;\n}\n\n"
  },
  {
    "path": "examples/clay-official-website/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(clay_official_website C)\n\nset(CMAKE_C_STANDARD 99)\n\nadd_executable(clay_official_website main.c)\n\ntarget_compile_options(clay_official_website PUBLIC)\ntarget_include_directories(clay_official_website PUBLIC .)\n"
  },
  {
    "path": "examples/clay-official-website/build/clay/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"preload\" href=\"/clay/fonts/Calistoga-Regular.ttf\" as=\"font\" type=\"font/ttf\" crossorigin>\n    <link rel=\"preload\" href=\"/clay/fonts/Quicksand-Semibold.ttf\" as=\"font\" type=\"font/ttf\" crossorigin>\n    <title>Clay - UI Layout Library</title>\n    <style>\n        html, body {\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            padding: 0;\n            margin: 0;\n            pointer-events: none;\n            background: rgb(244, 235, 230);\n        }\n        /* Import the font using @font-face */\n        @font-face {\n          font-family: 'Calistoga';\n          font-style: normal;\n          font-weight: 400;\n          src: url('/clay/fonts/Calistoga-Regular.ttf') format('truetype');\n        }\n\n        @font-face {\n          font-family: 'Quicksand';\n          font-style: normal;\n          font-weight: 400;\n          src: url('/clay/fonts/Quicksand-Semibold.ttf') format('truetype');\n        }\n\n        body > canvas {\n            width: 100%;\n            height: 100%;\n            touch-action: none;\n        }\n\n        div, a, img {\n            position: absolute;\n            box-sizing: border-box;\n            -webkit-backface-visibility: hidden;\n            pointer-events: none;\n        }\n\n        a {\n            cursor: pointer;\n            pointer-events: all;\n        }\n\n        .text {\n            pointer-events: all;\n            white-space: pre;\n        }\n\n        /* TODO special exception for text selection in debug tools */\n        [id='2067877626'] > * {\n            pointer-events: none !important;\n        }\n    </style>\n</head>\n<script type=\"module\">\n    const CLAY_RENDER_COMMAND_TYPE_NONE = 0;\n    const CLAY_RENDER_COMMAND_TYPE_RECTANGLE = 1;\n    const CLAY_RENDER_COMMAND_TYPE_BORDER = 2;\n    const CLAY_RENDER_COMMAND_TYPE_TEXT = 3;\n    const CLAY_RENDER_COMMAND_TYPE_IMAGE = 4;\n    const CLAY_RENDER_COMMAND_TYPE_SCISSOR_START = 5;\n    const CLAY_RENDER_COMMAND_TYPE_SCISSOR_END = 6;\n    const CLAY_RENDER_COMMAND_TYPE_CUSTOM = 7;\n    const GLOBAL_FONT_SCALING_FACTOR = 0.8;\n    let renderCommandSize = 0;\n    let scratchSpaceAddress = 8;\n    let heapSpaceAddress = 0;\n    let memoryDataView;\n    let textDecoder = new TextDecoder(\"utf-8\");\n    let previousFrameTime;\n    let fontsById = [\n        'Quicksand',\n        'Calistoga',\n        'Quicksand',\n        'Quicksand',\n        'Quicksand',\n    ];\n    let elementCache = {};\n    let imageCache = {};\n    let dimensionsDefinition = { type: 'struct', members: [\n        {name: 'width', type: 'float'},\n        {name: 'height', type: 'float'},\n    ]};\n    let colorDefinition = { type: 'struct', members: [\n        {name: 'r', type: 'float' },\n        {name: 'g', type: 'float' },\n        {name: 'b', type: 'float' },\n        {name: 'a', type: 'float' },\n    ]};\n    let stringDefinition = { type: 'struct', members: [\n        {name: 'isStaticallyAllocated', type: 'uint32_t'},\n        {name: 'length', type: 'uint32_t' },\n        {name: 'chars', type: 'uint32_t' },\n    ]};\n    let stringSliceDefinition = { type: 'struct', members: [\n        {name: 'length', type: 'uint32_t' },\n        {name: 'chars', type: 'uint32_t' },\n        {name: 'baseChars', type: 'uint32_t' },\n    ]};\n    let borderWidthDefinition = { type: 'struct', members: [\n        {name: 'left', type: 'uint16_t'},\n        {name: 'right', type: 'uint16_t'},\n        {name: 'top', type: 'uint16_t'},\n        {name: 'bottom', type: 'uint16_t'},\n        {name: 'betweenChildren', type: 'uint16_t'},\n    ]};\n    let cornerRadiusDefinition = { type: 'struct', members: [\n        {name: 'topLeft', type: 'float'},\n        {name: 'topRight', type: 'float'},\n        {name: 'bottomLeft', type: 'float'},\n        {name: 'bottomRight', type: 'float'},\n    ]};\n    let textConfigDefinition = { name: 'text', type: 'struct', members: [\n        { name: 'userData', type: 'uint32_t' },\n        { name: 'textColor', ...colorDefinition },\n        { name: 'fontId', type: 'uint16_t' },\n        { name: 'fontSize', type: 'uint16_t' },\n        { name: 'letterSpacing', type: 'uint16_t' },\n        { name: 'lineSpacing', type: 'uint16_t' },\n        { name: 'wrapMode', type: 'uint8_t' },\n        { name: 'disablePointerEvents', type: 'uint8_t' },\n        { name: '_padding', type: 'uint16_t' },\n    ]};\n    let textRenderDataDefinition = { type: 'struct', members: [\n        { name: 'stringContents', ...stringSliceDefinition },\n        { name: 'textColor', ...colorDefinition },\n        { name: 'fontId', type: 'uint16_t' },\n        { name: 'fontSize', type: 'uint16_t' },\n        { name: 'letterSpacing', type: 'uint16_t' },\n        { name: 'lineHeight', type: 'uint16_t' },\n    ]};\n    let rectangleRenderDataDefinition = { type: 'struct', members: [\n        { name: 'backgroundColor', ...colorDefinition },\n        { name: 'cornerRadius', ...cornerRadiusDefinition },\n    ]};\n    let imageRenderDataDefinition = { type: 'struct', members: [\n        { name: 'backgroundColor', ...colorDefinition },\n        { name: 'cornerRadius', ...cornerRadiusDefinition },\n        { name: 'imageData', type: 'uint32_t' },\n    ]};\n    let customRenderDataDefinition = { type: 'struct', members: [\n        { name: 'backgroundColor', ...colorDefinition },\n        { name: 'cornerRadius', ...cornerRadiusDefinition },\n        { name: 'customData', type: 'uint32_t' },\n    ]};\n    let borderRenderDataDefinition = { type: 'struct', members: [\n        { name: 'color', ...colorDefinition },\n        { name: 'cornerRadius', ...cornerRadiusDefinition },\n        { name: 'width', ...borderWidthDefinition },\n        { name: 'padding', type: 'uint16_t'}\n    ]};\n    let clipRenderDataDefinition = { type: 'struct', members: [\n        { name: 'horizontal', type: 'bool' },\n        { name: 'vertical', type: 'bool' },\n    ]};\n    let customHTMLDataDefinition = { type: 'struct', members: [\n        { name: 'link', ...stringDefinition },\n        { name: 'cursorPointer', type: 'uint8_t' },\n        { name: 'disablePointerEvents', type: 'uint8_t' },\n        { name: 'padding', type: 'uint16_t'}\n    ]};\n    let renderCommandDefinition = {\n        name: 'Clay_RenderCommand',\n        type: 'struct',\n        members: [\n            { name: 'boundingBox', type: 'struct', members: [\n                { name: 'x', type: 'float' },\n                { name: 'y', type: 'float' },\n                { name: 'width', type: 'float' },\n                { name: 'height', type: 'float' },\n            ]},\n            { name: 'renderData', type: 'union', members: [\n                { name: 'rectangle', ...rectangleRenderDataDefinition },\n                { name: 'text', ...textRenderDataDefinition },\n                { name: 'image', ...imageRenderDataDefinition },\n                { name: 'custom', ...customRenderDataDefinition },\n                { name: 'border', ...borderRenderDataDefinition },\n                { name: 'clip', ...clipRenderDataDefinition },\n            ]},\n            { name: 'userData', type: 'uint32_t'},\n            { name: 'id', type: 'uint32_t' },\n            { name: 'zIndex', type: 'int16_t' },\n            { name: 'commandType', type: 'uint8_t' },\n            { name: '_padding', type: 'uint8_t' },\n        ]\n    };\n\n    function getStructTotalSize(definition) {\n        switch(definition.type) {\n            case 'union':\n            case 'struct': {\n                let totalSize = 0;\n                for (const member of definition.members) {\n                    let result = getStructTotalSize(member);\n                    if (definition.type === 'struct') {\n                        totalSize += result;\n                    } else {\n                        totalSize = Math.max(totalSize, result);\n                    }\n                }\n                return totalSize;\n            }\n            case 'float': return 4;\n            case 'uint32_t': return 4;\n            case 'int32_t': return 4;\n            case 'uint16_t': return 2;\n            case 'int16_t': return 2;\n            case 'uint8_t': return 1;\n            case 'bool': return 1;\n            default: {\n                throw \"Unimplemented C data type \" + definition.type\n            }\n        }\n    }\n\n    function readStructAtAddress(address, definition) {\n        switch(definition.type) {\n            case 'union':\n            case 'struct': {\n                let struct = { __size: 0 };\n                for (const member of definition.members) {\n                    let result = readStructAtAddress(address, member);\n                    struct[member.name] = result;\n                    if (definition.type === 'struct') {\n                        struct.__size += result.__size;\n                        address += result.__size;\n                    } else {\n                        struct.__size = Math.max(struct.__size, result.__size);\n                    }\n                }\n                return struct;\n            }\n            case 'float': return { value: memoryDataView.getFloat32(address, true), __size: 4 };\n            case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 };\n            case 'int32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 };\n            case 'uint16_t': return { value: memoryDataView.getUint16(address, true), __size: 2 };\n            case 'int16_t': return { value: memoryDataView.getInt16(address, true), __size: 2 };\n            case 'uint8_t': return { value: memoryDataView.getUint8(address, true), __size: 1 };\n            case 'bool': return { value: memoryDataView.getUint8(address, true), __size: 1 };\n            default: {\n                throw \"Unimplemented C data type \" + definition.type\n            }\n        }\n    }\n\n    function getTextDimensions(text, font) {\n        // re-use canvas object for better performance\n        window.canvasContext.font = font;\n        let metrics = window.canvasContext.measureText(text);\n        return { width: metrics.width, height: metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent };\n    }\n\n    function createMainArena(arenaStructAddress, arenaMemoryAddress) {\n        let memorySize = instance.exports.Clay_MinMemorySize();\n        // Last arg is address to store return value\n        instance.exports.Clay_CreateArenaWithCapacityAndMemory(arenaStructAddress, memorySize, arenaMemoryAddress);\n    }\n    async function init() {\n        await Promise.all(fontsById.map(f => document.fonts.load(`12px \"${f}\"`)));\n        window.htmlRoot = document.body.appendChild(document.createElement('div'));\n        window.canvasRoot = document.body.appendChild(document.createElement('canvas'));\n        window.canvasContext = window.canvasRoot.getContext(\"2d\");\n        window.mousePositionXThisFrame = 0;\n        window.mousePositionYThisFrame = 0;\n        window.mouseWheelXThisFrame = 0;\n        window.mouseWheelYThisFrame = 0;\n        window.touchDown = false;\n        window.arrowKeyDownPressedThisFrame = false;\n        window.arrowKeyUpPressedThisFrame = false;\n        let zeroTimeout = null;\n        document.addEventListener(\"wheel\", (event) => {\n            window.mouseWheelXThisFrame = event.deltaX * -0.1;\n            window.mouseWheelYThisFrame = event.deltaY * -0.1;\n            clearTimeout(zeroTimeout);\n            zeroTimeout = setTimeout(() => {\n                window.mouseWheelXThisFrame = 0;\n                window.mouseWheelYThisFrame = 0;\n            }, 10);\n        });\n\n        function handleTouch (event) {\n            if (event.touches.length === 1) {\n                window.touchDown = true;\n                let target = event.target;\n                let scrollTop = 0;\n                let scrollLeft = 0;\n                let activeRendererIndex = memoryDataView.getUint32(instance.exports.ACTIVE_RENDERER_INDEX.value, true);\n                while (activeRendererIndex !== 1 && target) {\n                    scrollLeft += target.scrollLeft;\n                    scrollTop += target.scrollTop;\n                    target = target.parentElement;\n                }\n                window.mousePositionXThisFrame = event.changedTouches[0].pageX + scrollLeft;\n                window.mousePositionYThisFrame = event.changedTouches[0].pageY + scrollTop;\n            }\n        }\n\n        document.addEventListener(\"touchstart\", handleTouch);\n        document.addEventListener(\"touchmove\", handleTouch);\n        document.addEventListener(\"touchend\", () => {\n            window.touchDown = false;\n            window.mousePositionXThisFrame = 0;\n            window.mousePositionYThisFrame = 0;\n        })\n\n        document.addEventListener(\"mousemove\", (event) => {\n            let target = event.target;\n            let scrollTop = 0;\n            let scrollLeft = 0;\n            let activeRendererIndex = memoryDataView.getUint32(instance.exports.ACTIVE_RENDERER_INDEX.value, true);\n            while (activeRendererIndex !== 1 && target) {\n                scrollLeft += target.scrollLeft;\n                scrollTop += target.scrollTop;\n                target = target.parentElement;\n            }\n            window.mousePositionXThisFrame = event.x + scrollLeft;\n            window.mousePositionYThisFrame = event.y + scrollTop;\n        });\n\n        document.addEventListener(\"mousedown\", (event) => {\n            window.mouseDown = true;\n            window.mouseDownThisFrame = true;\n        });\n\n        document.addEventListener(\"mouseup\", (event) => {\n            window.mouseDown = false;\n        });\n\n        document.addEventListener(\"keydown\", (event) => {\n            if (event.key === \"ArrowDown\") {\n                window.arrowKeyDownPressedThisFrame = true;\n            }\n            if (event.key === \"ArrowUp\") {\n                window.arrowKeyUpPressedThisFrame = true;\n            }\n            if (event.key === \"d\") {\n                window.dKeyPressedThisFrame = true;\n            }\n        });\n\n        const importObject = {\n            clay: {\n                measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig, userData) => {\n                    let stringLength = memoryDataView.getUint32(textToMeasure, true);\n                    let pointerToString = memoryDataView.getUint32(textToMeasure + 4, true);\n                    let textConfig = readStructAtAddress(addressOfConfig, textConfigDefinition);\n                    let textDecoder = new TextDecoder(\"utf-8\");\n                    let text = textDecoder.decode(memoryDataView.buffer.slice(pointerToString, pointerToString + stringLength));\n                    let sourceDimensions = getTextDimensions(text, `${Math.round(textConfig.fontSize.value * GLOBAL_FONT_SCALING_FACTOR)}px ${fontsById[textConfig.fontId.value]}`);\n                    memoryDataView.setFloat32(addressOfDimensions, sourceDimensions.width, true);\n                    memoryDataView.setFloat32(addressOfDimensions + 4, sourceDimensions.height, true);\n                },\n                queryScrollOffsetFunction: (addressOfOffset, elementId) => {\n                    let container = document.getElementById(elementId.toString());\n                    if (container) {\n                        memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true);\n                        memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true);\n                    }\n                },\n            },\n        };\n        const { instance } = await WebAssembly.instantiateStreaming(\n            fetch(\"/clay/index.wasm\"), importObject\n        );\n        memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer);\n        scratchSpaceAddress = instance.exports.__heap_base.value;\n        let clayScratchSpaceAddress = instance.exports.__heap_base.value + 1024;\n        heapSpaceAddress = instance.exports.__heap_base.value + 2048;\n        let arenaAddress = scratchSpaceAddress + 8;\n        window.instance = instance;\n        createMainArena(arenaAddress, heapSpaceAddress);\n        memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true);\n        memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true);\n        instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value);\n        instance.exports.SetScratchMemory(clayScratchSpaceAddress);\n        renderCommandSize = getStructTotalSize(renderCommandDefinition);\n        renderLoop();\n    }\n\n    function MemoryIsDifferent(one, two, length) {\n        for (let i = 0; i < length; i++) {\n            if (one[i] !== two[i]) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    function SetElementBackgroundColorAndRadius(element, cornerRadius, backgroundColor) {\n        element.style.backgroundColor = `rgba(${backgroundColor.r.value}, ${backgroundColor.g.value}, ${backgroundColor.b.value}, ${backgroundColor.a.value / 255})`;\n        if (cornerRadius.topLeft.value > 0) {\n            element.style.borderTopLeftRadius = cornerRadius.topLeft.value + 'px';\n        }\n        if (cornerRadius.topRight.value > 0) {\n            element.style.borderTopRightRadius = cornerRadius.topRight.value + 'px';\n        }\n        if (cornerRadius.bottomLeft.value > 0) {\n            element.style.borderBottomLeftRadius = cornerRadius.bottomLeft.value + 'px';\n        }\n        if (cornerRadius.bottomRight.value > 0) {\n            element.style.borderBottomRightRadius = cornerRadius.bottomRight.value + 'px';\n        }\n    }\n\n    function renderLoopHTML() {\n        let capacity = memoryDataView.getInt32(scratchSpaceAddress, true);\n        let length = memoryDataView.getInt32(scratchSpaceAddress + 4, true);\n        let arrayOffset = memoryDataView.getUint32(scratchSpaceAddress + 8, true);\n        let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }];\n        let previousId = 0;\n        for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) {\n            let entireRenderCommandMemory = new Uint8Array(memoryDataView.buffer.slice(arrayOffset, arrayOffset + renderCommandSize));\n            let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition);\n            let parentElement = scissorStack[scissorStack.length - 1];\n            let element = null;\n            let isMultiConfigElement = previousId === renderCommand.id.value;\n            if (!elementCache[renderCommand.id.value]) {\n                let elementType = 'div';\n                switch (renderCommand.commandType.value & 0xff) {\n                    case CLAY_RENDER_COMMAND_TYPE_TEXT:\n                    case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                        if (renderCommand.userData.value !== 0) {\n                            if (readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition).link.length.value > 0) {\n                                elementType = 'a';\n                            }\n                        }\n                        break;\n                    }\n                    case CLAY_RENDER_COMMAND_TYPE_IMAGE: {\n                        elementType = 'img'; break;\n                    }\n                    default: break;\n                }\n                element = document.createElement(elementType);\n                element.id = renderCommand.id.value;\n                if (renderCommand.commandType.value === CLAY_RENDER_COMMAND_TYPE_SCISSOR_START) {\n                    element.style.overflow = 'hidden';\n                }\n                elementCache[renderCommand.id.value] = {\n                    exists: true,\n                    element: element,\n                    previousMemoryCommand: new Uint8Array(0),\n                    previousMemoryConfig: new Uint8Array(0),\n                    previousMemoryText: new Uint8Array(0)\n                };\n            }\n\n            let elementData = elementCache[renderCommand.id.value];\n            element = elementData.element;\n            if (!isMultiConfigElement && Array.prototype.indexOf.call(parentElement.element.children, element) !== parentElement.nextElementIndex) {\n                if (parentElement.nextElementIndex === 0) {\n                    parentElement.element.insertAdjacentElement('afterbegin', element);\n                } else {\n                    parentElement.element.childNodes[Math.min(parentElement.nextElementIndex - 1, parentElement.element.childNodes.length - 1)].insertAdjacentElement('afterend', element);\n                }\n            }\n\n            elementData.exists = true;\n            // Don't get me started. Cheaper to compare the render command memory than to update HTML elements\n            let dirty = MemoryIsDifferent(elementData.previousMemoryCommand, entireRenderCommandMemory, renderCommandSize) && !isMultiConfigElement;\n            if (!isMultiConfigElement) {\n                parentElement.nextElementIndex++;\n            }\n\n            previousId = renderCommand.id.value;\n\n            elementData.previousMemoryCommand = entireRenderCommandMemory;\n            let offsetX = scissorStack.length > 0 ? scissorStack[scissorStack.length - 1].nextAllocation.x : 0;\n            let offsetY = scissorStack.length > 0 ? scissorStack[scissorStack.length - 1].nextAllocation.y : 0;\n            if (dirty) {\n                element.style.transform = `translate(${Math.round(renderCommand.boundingBox.x.value - offsetX)}px, ${Math.round(renderCommand.boundingBox.y.value - offsetY)}px)`\n                element.style.width = Math.round(renderCommand.boundingBox.width.value) + 'px';\n                element.style.height = Math.round(renderCommand.boundingBox.height.value) + 'px';\n            }\n\n            // note: commandType is packed to uint8_t and has 3 garbage bytes of padding\n            switch(renderCommand.commandType.value & 0xff) {\n                case (CLAY_RENDER_COMMAND_TYPE_NONE): {\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {\n                    let config = renderCommand.renderData.rectangle;\n                    let configMemory = JSON.stringify(config);\n                    if (configMemory === elementData.previousMemoryConfig) {\n                        break;\n                    }\n\n                    SetElementBackgroundColorAndRadius(element, config.cornerRadius, config.backgroundColor);\n                    if (renderCommand.userData.value !== 0) {\n                        let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);\n\n                        let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0;\n                        memoryDataView.setUint32(0, renderCommand.id.value, true);\n                        if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {\n                            window.location.href = linkContents;\n                        }\n                        if (linkContents.length > 0) {\n                            element.href = linkContents;\n                        }\n\n                        if (linkContents.length > 0 || customData.cursorPointer.value) {\n                            element.style.pointerEvents = 'all';\n                            element.style.cursor = 'pointer';\n                        }\n                    }\n\n                    elementData.previousMemoryConfig = configMemory;\n\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_BORDER): {\n                    let config = renderCommand.renderData.border;\n                    let configMemory = JSON.stringify(config);\n                    if (configMemory === elementData.previousMemoryConfig) {\n                        break;\n                    }\n                    let color = config.color;\n                    elementData.previousMemoryConfig = configMemory;\n                    if (config.width.left.value > 0) {\n                        element.style.borderLeft = `${config.width.left.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.width.right.value > 0) {\n                        element.style.borderRight = `${config.width.right.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.width.top.value > 0) {\n                        element.style.borderTop = `${config.width.top.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.width.bottom.value > 0) {\n                        element.style.borderBottom = `${config.width.bottom.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.cornerRadius.topLeft.value > 0) {\n                        element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';\n                    }\n                    if (config.cornerRadius.topRight.value > 0) {\n                        element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px';\n                    }\n                    if (config.cornerRadius.bottomLeft.value > 0) {\n                        element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px';\n                    }\n                    if (config.cornerRadius.bottomRight.value > 0) {\n                        element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px';\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_TEXT): {\n                    let config = renderCommand.renderData.text;\n                    let configMemory = JSON.stringify(config);\n                    let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value));\n                    if (configMemory !== elementData.previousMemoryConfig) {\n                        element.className = 'text';\n                        let textColor = config.textColor;\n                        let fontSize = Math.round(config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR);\n                        element.style.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`;\n                        element.style.fontFamily = fontsById[config.fontId.value];\n                        element.style.fontSize = fontSize + 'px';\n                        if (renderCommand.userData.value !== 0) {\n                            let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);\n                            element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all';\n                            let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0;\n                            memoryDataView.setUint32(0, renderCommand.id.value, true);\n                            if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {\n                                window.location.href = linkContents;\n                            }\n                            if (linkContents.length > 0) {\n                                element.href = linkContents;\n                            }\n\n                            if (linkContents.length > 0 || customData.cursorPointer.value) {\n                                element.style.pointerEvents = 'all';\n                                element.style.cursor = 'pointer';\n                            }\n                        }\n                        elementData.previousMemoryConfig = configMemory;\n                    }\n                    if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) {\n                        element.innerHTML = textDecoder.decode(stringContents);\n                    }\n                    elementData.previousMemoryText = stringContents;\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {\n                    scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 });\n                    let config = renderCommand.renderData.clip;\n                    let configMemory = JSON.stringify(config);\n                    if (configMemory === elementData.previousMemoryConfig) {\n                        break;\n                    }\n                    if (config.horizontal.value) {\n                        element.style.overflowX = 'scroll';\n                        element.style.pointerEvents = 'auto';\n                    }\n                    if (config.vertical.value) {\n                        element.style.overflowY = 'scroll';\n                        element.style.pointerEvents = 'auto';\n                    }\n                    elementData.previousMemoryConfig = configMemory;\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {\n                    scissorStack.splice(scissorStack.length - 1, 1);\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {\n                    let config = renderCommand.renderData.image;\n                    let imageURL = readStructAtAddress(config.imageData.value, stringDefinition);\n                    let srcContents = new Uint8Array(memoryDataView.buffer.slice(imageURL.chars.value, imageURL.chars.value + imageURL.length.value));\n                    if (srcContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(srcContents, elementData.previousMemoryText, srcContents.length)) {\n                        element.src = textDecoder.decode(srcContents);\n                    }\n                    elementData.previousMemoryText = srcContents;\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;\n                default: {\n                    console.log(\"Error: unhandled render command\");\n                }\n            }\n        }\n\n        for (const key of Object.keys(elementCache)) {\n            if (elementCache[key].exists) {\n                elementCache[key].exists = false;\n            } else {\n                elementCache[key].element.remove();\n                delete elementCache[key];\n            }\n        }\n    }\n\n    function renderLoopCanvas() {\n    // Note: Rendering to canvas needs to be scaled up by window.devicePixelRatio in both width and height.\n    // e.g. if we're working on a device where devicePixelRatio is 2, we need to render\n    // everything at width^2 x height^2 resolution, then scale back down with css to get the correct pixel density.\n        let capacity = memoryDataView.getUint32(scratchSpaceAddress, true);\n        let length = memoryDataView.getUint32(scratchSpaceAddress + 4, true);\n        let arrayOffset = memoryDataView.getUint32(scratchSpaceAddress + 8, true);\n        window.canvasRoot.width = window.innerWidth * window.devicePixelRatio;\n        window.canvasRoot.height = window.innerHeight * window.devicePixelRatio;\n        window.canvasRoot.style.width = window.innerWidth + 'px';\n        window.canvasRoot.style.height = window.innerHeight + 'px';\n        let ctx = window.canvasContext;\n        let scale = window.devicePixelRatio;\n        for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) {\n            let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition);\n            let boundingBox = renderCommand.boundingBox;\n            \n            // note: commandType is packed to uint8_t and has 3 garbage bytes of padding\n            switch(renderCommand.commandType.value & 0xff) {\n                case (CLAY_RENDER_COMMAND_TYPE_NONE): {\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {\n                    let config = renderCommand.renderData.rectangle;\n                    let color = config.backgroundColor;\n                    ctx.beginPath();\n                    window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                    window.canvasContext.roundRect(\n                        boundingBox.x.value * scale, // x\n                        boundingBox.y.value * scale, // y\n                        boundingBox.width.value * scale, // width\n                        boundingBox.height.value * scale,\n                        [config.cornerRadius.topLeft.value * scale, config.cornerRadius.topRight.value * scale, config.cornerRadius.bottomRight.value * scale, config.cornerRadius.bottomLeft.value * scale]) // height;\n                    ctx.fill();\n                    ctx.closePath();\n                    // Handle link clicks\n                    if (renderCommand.userData.value !== 0) {\n                        let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);\n                        let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0;\n                        memoryDataView.setUint32(0, renderCommand.id.value, true);\n                        if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {\n                            window.location.href = linkContents;\n                        }\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_BORDER): {\n                    let config = renderCommand.renderData.border;\n                    let color = config.color;\n                    ctx.beginPath();\n                    ctx.moveTo(boundingBox.x.value * scale, boundingBox.y.value * scale);\n                    // Top Left Corner\n                    if (config.cornerRadius.topLeft.value > 0) {\n                        let lineWidth = config.width.top.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, config.cornerRadius.topLeft.value * scale);\n                        ctx.stroke();\n                    }\n                    // Top border\n                    if (config.width.top.value > 0) {\n                        let lineWidth = config.width.top.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    // Top Right Corner\n                    if (config.cornerRadius.topRight.value > 0) {\n                        let lineWidth = config.width.top.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale, config.cornerRadius.topRight.value * scale);\n                        ctx.stroke();\n                    }\n                    // Right border\n                    if (config.width.right.value > 0) {\n                        let lineWidth = config.width.right.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.topRight.value - halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    // Bottom Right Corner\n                    if (config.cornerRadius.bottomRight.value > 0) {\n                        let lineWidth = config.width.top.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, config.cornerRadius.bottomRight.value * scale);\n                        ctx.stroke();\n                    }\n                    // Bottom Border\n                    if (config.width.bottom.value > 0) {\n                        let lineWidth = config.width.bottom.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    // Bottom Left Corner\n                    if (config.cornerRadius.bottomLeft.value > 0) {\n                        let lineWidth = config.width.bottom.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, (boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomLeft.value - halfLineWidth) * scale, config.cornerRadius.bottomLeft.value * scale);\n                        ctx.stroke();\n                    }\n                    // Left Border\n                    if (config.width.left.value > 0) {\n                        let lineWidth = config.width.left.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomLeft.value - halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.bottomRight.value + halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    ctx.closePath();\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_TEXT): {\n                    let config = renderCommand.renderData.text;\n                    let textContents = config.stringContents;\n                    let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value));\n                    let fontSize = config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR * scale;\n                    ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`;\n                    let color = config.textColor;\n                    ctx.textBaseline = 'middle';\n                    ctx.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                    ctx.fillText(textDecoder.decode(stringContents), boundingBox.x.value * scale, (boundingBox.y.value + boundingBox.height.value / 2 + 1) * scale);\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {\n                    window.canvasContext.save();\n                    window.canvasContext.beginPath();\n                    window.canvasContext.rect(boundingBox.x.value * scale, boundingBox.y.value * scale, boundingBox.width.value * scale, boundingBox.height.value * scale);\n                    window.canvasContext.clip();\n                    window.canvasContext.closePath();\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {\n                    window.canvasContext.restore();\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {\n                    let config = renderCommand.renderData.image;\n                    let imageURL = readStructAtAddress(config.imageData.value, stringDefinition);\n                    let src = textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(imageURL.chars.value, imageURL.chars.value + imageURL.length.value)));\n                    if (!imageCache[src]) {\n                        imageCache[src] = {\n                            image: new Image(),\n                            loaded: false,\n                        }\n                        imageCache[src].image.onload = () => imageCache[src].loaded = true;\n                        imageCache[src].image.src = src;\n                    } else if (imageCache[src].loaded) {\n                        ctx.drawImage(imageCache[src].image, boundingBox.x.value * scale, boundingBox.y.value * scale, boundingBox.width.value * scale, boundingBox.height.value * scale);\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;\n            }\n        }\n    }\n\n    function renderLoop(currentTime) {\n        const elapsed = currentTime - previousFrameTime;\n        previousFrameTime = currentTime;\n        let activeRendererIndex = memoryDataView.getUint32(instance.exports.ACTIVE_RENDERER_INDEX.value, true);\n        if (activeRendererIndex === 0) {\n            instance.exports.UpdateDrawFrame(scratchSpaceAddress, window.innerWidth, window.innerHeight, 0, 0, window.mousePositionXThisFrame, window.mousePositionYThisFrame, window.touchDown, window.mouseDown, 0, 0, window.dKeyPressedThisFrame, elapsed / 1000);\n        } else {\n            instance.exports.UpdateDrawFrame(scratchSpaceAddress, window.innerWidth, window.innerHeight, window.mouseWheelXThisFrame, window.mouseWheelYThisFrame, window.mousePositionXThisFrame, window.mousePositionYThisFrame, window.touchDown, window.mouseDown, window.arrowKeyDownPressedThisFrame, window.arrowKeyUpPressedThisFrame, window.dKeyPressedThisFrame, elapsed / 1000);\n        }\n        let rendererChanged = activeRendererIndex !== window.previousActiveRendererIndex;\n        switch (activeRendererIndex) {\n            case 0: {\n                renderLoopHTML();\n                if (rendererChanged) {\n                    window.htmlRoot.style.display = 'block';\n                    window.canvasRoot.style.display = 'none';\n                }\n                break;\n            }\n            case 1: {\n                renderLoopCanvas();\n                if (rendererChanged) {\n                    window.htmlRoot.style.display = 'none';\n                    window.canvasRoot.style.display = 'block';\n                }\n                break;\n            }\n        }\n        window.previousActiveRendererIndex = activeRendererIndex;\n        requestAnimationFrame(renderLoop);\n        window.mouseDownThisFrame = false;\n        window.arrowKeyUpPressedThisFrame = false;\n        window.arrowKeyDownPressedThisFrame = false;\n        window.dKeyPressedThisFrame = false;\n    }\n    init();\n</script>\n<body>\n</body>\n</html>"
  },
  {
    "path": "examples/clay-official-website/build.sh",
    "content": "mkdir -p build/clay                                                       \\\n&& clang                                                                  \\\n-Wall                                                                     \\\n-Werror                                                                   \\\n-Os                                                                       \\\n-DCLAY_WASM                                                               \\\n-mbulk-memory                                                             \\\n--target=wasm32                                                           \\\n-nostdlib                                                                 \\\n-Wl,--strip-all                                                           \\\n-Wl,--export-dynamic                                                      \\\n-Wl,--no-entry                                                            \\\n-Wl,--export=__heap_base                                                  \\\n-Wl,--export=ACTIVE_RENDERER_INDEX                                        \\\n-Wl,--initial-memory=6553600                                              \\\n-o build/clay/index.wasm                                                  \\\nmain.c                                                                    \\\n&& cp index.html build/index.html && cp -r fonts/ build/clay/fonts   \\\n&& cp -r images/ build/clay/images\n"
  },
  {
    "path": "examples/clay-official-website/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"preload\" href=\"/clay/fonts/Calistoga-Regular.ttf\" as=\"font\" type=\"font/ttf\" crossorigin>\n    <link rel=\"preload\" href=\"/clay/fonts/Quicksand-Semibold.ttf\" as=\"font\" type=\"font/ttf\" crossorigin>\n    <title>Clay - UI Layout Library</title>\n    <style>\n        html, body {\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            padding: 0;\n            margin: 0;\n            pointer-events: none;\n            background: rgb(244, 235, 230);\n        }\n        /* Import the font using @font-face */\n        @font-face {\n          font-family: 'Calistoga';\n          font-style: normal;\n          font-weight: 400;\n          src: url('/clay/fonts/Calistoga-Regular.ttf') format('truetype');\n        }\n\n        @font-face {\n          font-family: 'Quicksand';\n          font-style: normal;\n          font-weight: 400;\n          src: url('/clay/fonts/Quicksand-Semibold.ttf') format('truetype');\n        }\n\n        body > canvas {\n            width: 100%;\n            height: 100%;\n            touch-action: none;\n        }\n\n        div, a, img {\n            position: absolute;\n            box-sizing: border-box;\n            -webkit-backface-visibility: hidden;\n            pointer-events: none;\n        }\n\n        a {\n            cursor: pointer;\n            pointer-events: all;\n        }\n\n        .text {\n            pointer-events: all;\n            white-space: pre;\n        }\n\n        /* TODO special exception for text selection in debug tools */\n        [id='2067877626'] > * {\n            pointer-events: none !important;\n        }\n    </style>\n</head>\n<script type=\"module\">\n    const CLAY_RENDER_COMMAND_TYPE_NONE = 0;\n    const CLAY_RENDER_COMMAND_TYPE_RECTANGLE = 1;\n    const CLAY_RENDER_COMMAND_TYPE_BORDER = 2;\n    const CLAY_RENDER_COMMAND_TYPE_TEXT = 3;\n    const CLAY_RENDER_COMMAND_TYPE_IMAGE = 4;\n    const CLAY_RENDER_COMMAND_TYPE_SCISSOR_START = 5;\n    const CLAY_RENDER_COMMAND_TYPE_SCISSOR_END = 6;\n    const CLAY_RENDER_COMMAND_TYPE_CUSTOM = 7;\n    const GLOBAL_FONT_SCALING_FACTOR = 0.8;\n    let renderCommandSize = 0;\n    let scratchSpaceAddress = 8;\n    let heapSpaceAddress = 0;\n    let memoryDataView;\n    let textDecoder = new TextDecoder(\"utf-8\");\n    let previousFrameTime;\n    let fontsById = [\n        'Quicksand',\n        'Calistoga',\n        'Quicksand',\n        'Quicksand',\n        'Quicksand',\n    ];\n    let elementCache = {};\n    let imageCache = {};\n    let dimensionsDefinition = { type: 'struct', members: [\n        {name: 'width', type: 'float'},\n        {name: 'height', type: 'float'},\n    ]};\n    let colorDefinition = { type: 'struct', members: [\n        {name: 'r', type: 'float' },\n        {name: 'g', type: 'float' },\n        {name: 'b', type: 'float' },\n        {name: 'a', type: 'float' },\n    ]};\n    let stringDefinition = { type: 'struct', members: [\n        {name: 'isStaticallyAllocated', type: 'uint32_t'},\n        {name: 'length', type: 'uint32_t' },\n        {name: 'chars', type: 'uint32_t' },\n    ]};\n    let stringSliceDefinition = { type: 'struct', members: [\n        {name: 'length', type: 'uint32_t' },\n        {name: 'chars', type: 'uint32_t' },\n        {name: 'baseChars', type: 'uint32_t' },\n    ]};\n    let borderWidthDefinition = { type: 'struct', members: [\n        {name: 'left', type: 'uint16_t'},\n        {name: 'right', type: 'uint16_t'},\n        {name: 'top', type: 'uint16_t'},\n        {name: 'bottom', type: 'uint16_t'},\n        {name: 'betweenChildren', type: 'uint16_t'},\n    ]};\n    let cornerRadiusDefinition = { type: 'struct', members: [\n        {name: 'topLeft', type: 'float'},\n        {name: 'topRight', type: 'float'},\n        {name: 'bottomLeft', type: 'float'},\n        {name: 'bottomRight', type: 'float'},\n    ]};\n    let textConfigDefinition = { name: 'text', type: 'struct', members: [\n        { name: 'userData', type: 'uint32_t' },\n        { name: 'textColor', ...colorDefinition },\n        { name: 'fontId', type: 'uint16_t' },\n        { name: 'fontSize', type: 'uint16_t' },\n        { name: 'letterSpacing', type: 'uint16_t' },\n        { name: 'lineSpacing', type: 'uint16_t' },\n        { name: 'wrapMode', type: 'uint8_t' },\n        { name: 'disablePointerEvents', type: 'uint8_t' },\n        { name: '_padding', type: 'uint16_t' },\n    ]};\n    let textRenderDataDefinition = { type: 'struct', members: [\n        { name: 'stringContents', ...stringSliceDefinition },\n        { name: 'textColor', ...colorDefinition },\n        { name: 'fontId', type: 'uint16_t' },\n        { name: 'fontSize', type: 'uint16_t' },\n        { name: 'letterSpacing', type: 'uint16_t' },\n        { name: 'lineHeight', type: 'uint16_t' },\n    ]};\n    let rectangleRenderDataDefinition = { type: 'struct', members: [\n        { name: 'backgroundColor', ...colorDefinition },\n        { name: 'cornerRadius', ...cornerRadiusDefinition },\n    ]};\n    let imageRenderDataDefinition = { type: 'struct', members: [\n        { name: 'backgroundColor', ...colorDefinition },\n        { name: 'cornerRadius', ...cornerRadiusDefinition },\n        { name: 'imageData', type: 'uint32_t' },\n    ]};\n    let customRenderDataDefinition = { type: 'struct', members: [\n        { name: 'backgroundColor', ...colorDefinition },\n        { name: 'cornerRadius', ...cornerRadiusDefinition },\n        { name: 'customData', type: 'uint32_t' },\n    ]};\n    let borderRenderDataDefinition = { type: 'struct', members: [\n        { name: 'color', ...colorDefinition },\n        { name: 'cornerRadius', ...cornerRadiusDefinition },\n        { name: 'width', ...borderWidthDefinition },\n        { name: 'padding', type: 'uint16_t'}\n    ]};\n    let clipRenderDataDefinition = { type: 'struct', members: [\n        { name: 'horizontal', type: 'bool' },\n        { name: 'vertical', type: 'bool' },\n    ]};\n    let customHTMLDataDefinition = { type: 'struct', members: [\n        { name: 'link', ...stringDefinition },\n        { name: 'cursorPointer', type: 'uint8_t' },\n        { name: 'disablePointerEvents', type: 'uint8_t' },\n        { name: 'padding', type: 'uint16_t'}\n    ]};\n    let renderCommandDefinition = {\n        name: 'Clay_RenderCommand',\n        type: 'struct',\n        members: [\n            { name: 'boundingBox', type: 'struct', members: [\n                { name: 'x', type: 'float' },\n                { name: 'y', type: 'float' },\n                { name: 'width', type: 'float' },\n                { name: 'height', type: 'float' },\n            ]},\n            { name: 'renderData', type: 'union', members: [\n                { name: 'rectangle', ...rectangleRenderDataDefinition },\n                { name: 'text', ...textRenderDataDefinition },\n                { name: 'image', ...imageRenderDataDefinition },\n                { name: 'custom', ...customRenderDataDefinition },\n                { name: 'border', ...borderRenderDataDefinition },\n                { name: 'clip', ...clipRenderDataDefinition },\n            ]},\n            { name: 'userData', type: 'uint32_t'},\n            { name: 'id', type: 'uint32_t' },\n            { name: 'zIndex', type: 'int16_t' },\n            { name: 'commandType', type: 'uint8_t' },\n            { name: '_padding', type: 'uint8_t' },\n        ]\n    };\n\n    function getStructTotalSize(definition) {\n        switch(definition.type) {\n            case 'union':\n            case 'struct': {\n                let totalSize = 0;\n                for (const member of definition.members) {\n                    let result = getStructTotalSize(member);\n                    if (definition.type === 'struct') {\n                        totalSize += result;\n                    } else {\n                        totalSize = Math.max(totalSize, result);\n                    }\n                }\n                return totalSize;\n            }\n            case 'float': return 4;\n            case 'uint32_t': return 4;\n            case 'int32_t': return 4;\n            case 'uint16_t': return 2;\n            case 'int16_t': return 2;\n            case 'uint8_t': return 1;\n            case 'bool': return 1;\n            default: {\n                throw \"Unimplemented C data type \" + definition.type\n            }\n        }\n    }\n\n    function readStructAtAddress(address, definition) {\n        switch(definition.type) {\n            case 'union':\n            case 'struct': {\n                let struct = { __size: 0 };\n                for (const member of definition.members) {\n                    let result = readStructAtAddress(address, member);\n                    struct[member.name] = result;\n                    if (definition.type === 'struct') {\n                        struct.__size += result.__size;\n                        address += result.__size;\n                    } else {\n                        struct.__size = Math.max(struct.__size, result.__size);\n                    }\n                }\n                return struct;\n            }\n            case 'float': return { value: memoryDataView.getFloat32(address, true), __size: 4 };\n            case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 };\n            case 'int32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 };\n            case 'uint16_t': return { value: memoryDataView.getUint16(address, true), __size: 2 };\n            case 'int16_t': return { value: memoryDataView.getInt16(address, true), __size: 2 };\n            case 'uint8_t': return { value: memoryDataView.getUint8(address, true), __size: 1 };\n            case 'bool': return { value: memoryDataView.getUint8(address, true), __size: 1 };\n            default: {\n                throw \"Unimplemented C data type \" + definition.type\n            }\n        }\n    }\n\n    function getTextDimensions(text, font) {\n        // re-use canvas object for better performance\n        window.canvasContext.font = font;\n        let metrics = window.canvasContext.measureText(text);\n        return { width: metrics.width, height: metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent };\n    }\n\n    function createMainArena(arenaStructAddress, arenaMemoryAddress) {\n        let memorySize = instance.exports.Clay_MinMemorySize();\n        // Last arg is address to store return value\n        instance.exports.Clay_CreateArenaWithCapacityAndMemory(arenaStructAddress, memorySize, arenaMemoryAddress);\n    }\n    async function init() {\n        await Promise.all(fontsById.map(f => document.fonts.load(`12px \"${f}\"`)));\n        window.htmlRoot = document.body.appendChild(document.createElement('div'));\n        window.canvasRoot = document.body.appendChild(document.createElement('canvas'));\n        window.canvasContext = window.canvasRoot.getContext(\"2d\");\n        window.mousePositionXThisFrame = 0;\n        window.mousePositionYThisFrame = 0;\n        window.mouseWheelXThisFrame = 0;\n        window.mouseWheelYThisFrame = 0;\n        window.touchDown = false;\n        window.arrowKeyDownPressedThisFrame = false;\n        window.arrowKeyUpPressedThisFrame = false;\n        let zeroTimeout = null;\n        document.addEventListener(\"wheel\", (event) => {\n            window.mouseWheelXThisFrame = event.deltaX * -0.1;\n            window.mouseWheelYThisFrame = event.deltaY * -0.1;\n            clearTimeout(zeroTimeout);\n            zeroTimeout = setTimeout(() => {\n                window.mouseWheelXThisFrame = 0;\n                window.mouseWheelYThisFrame = 0;\n            }, 10);\n        });\n\n        function handleTouch (event) {\n            if (event.touches.length === 1) {\n                window.touchDown = true;\n                let target = event.target;\n                let scrollTop = 0;\n                let scrollLeft = 0;\n                let activeRendererIndex = memoryDataView.getUint32(instance.exports.ACTIVE_RENDERER_INDEX.value, true);\n                while (activeRendererIndex !== 1 && target) {\n                    scrollLeft += target.scrollLeft;\n                    scrollTop += target.scrollTop;\n                    target = target.parentElement;\n                }\n                window.mousePositionXThisFrame = event.changedTouches[0].pageX + scrollLeft;\n                window.mousePositionYThisFrame = event.changedTouches[0].pageY + scrollTop;\n            }\n        }\n\n        document.addEventListener(\"touchstart\", handleTouch);\n        document.addEventListener(\"touchmove\", handleTouch);\n        document.addEventListener(\"touchend\", () => {\n            window.touchDown = false;\n            window.mousePositionXThisFrame = 0;\n            window.mousePositionYThisFrame = 0;\n        })\n\n        document.addEventListener(\"mousemove\", (event) => {\n            let target = event.target;\n            let scrollTop = 0;\n            let scrollLeft = 0;\n            let activeRendererIndex = memoryDataView.getUint32(instance.exports.ACTIVE_RENDERER_INDEX.value, true);\n            while (activeRendererIndex !== 1 && target) {\n                scrollLeft += target.scrollLeft;\n                scrollTop += target.scrollTop;\n                target = target.parentElement;\n            }\n            window.mousePositionXThisFrame = event.x + scrollLeft;\n            window.mousePositionYThisFrame = event.y + scrollTop;\n        });\n\n        document.addEventListener(\"mousedown\", (event) => {\n            window.mouseDown = true;\n            window.mouseDownThisFrame = true;\n        });\n\n        document.addEventListener(\"mouseup\", (event) => {\n            window.mouseDown = false;\n        });\n\n        document.addEventListener(\"keydown\", (event) => {\n            if (event.key === \"ArrowDown\") {\n                window.arrowKeyDownPressedThisFrame = true;\n            }\n            if (event.key === \"ArrowUp\") {\n                window.arrowKeyUpPressedThisFrame = true;\n            }\n            if (event.key === \"d\") {\n                window.dKeyPressedThisFrame = true;\n            }\n        });\n\n        const importObject = {\n            clay: {\n                measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig, userData) => {\n                    let stringLength = memoryDataView.getUint32(textToMeasure, true);\n                    let pointerToString = memoryDataView.getUint32(textToMeasure + 4, true);\n                    let textConfig = readStructAtAddress(addressOfConfig, textConfigDefinition);\n                    let textDecoder = new TextDecoder(\"utf-8\");\n                    let text = textDecoder.decode(memoryDataView.buffer.slice(pointerToString, pointerToString + stringLength));\n                    let sourceDimensions = getTextDimensions(text, `${Math.round(textConfig.fontSize.value * GLOBAL_FONT_SCALING_FACTOR)}px ${fontsById[textConfig.fontId.value]}`);\n                    memoryDataView.setFloat32(addressOfDimensions, sourceDimensions.width, true);\n                    memoryDataView.setFloat32(addressOfDimensions + 4, sourceDimensions.height, true);\n                },\n                queryScrollOffsetFunction: (addressOfOffset, elementId) => {\n                    let container = document.getElementById(elementId.toString());\n                    if (container) {\n                        memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true);\n                        memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true);\n                    }\n                },\n            },\n        };\n        const { instance } = await WebAssembly.instantiateStreaming(\n            fetch(\"/clay/index.wasm\"), importObject\n        );\n        memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer);\n        scratchSpaceAddress = instance.exports.__heap_base.value;\n        let clayScratchSpaceAddress = instance.exports.__heap_base.value + 1024;\n        heapSpaceAddress = instance.exports.__heap_base.value + 2048;\n        let arenaAddress = scratchSpaceAddress + 8;\n        window.instance = instance;\n        createMainArena(arenaAddress, heapSpaceAddress);\n        memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true);\n        memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true);\n        instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value);\n        instance.exports.SetScratchMemory(clayScratchSpaceAddress);\n        renderCommandSize = getStructTotalSize(renderCommandDefinition);\n        renderLoop();\n    }\n\n    function MemoryIsDifferent(one, two, length) {\n        for (let i = 0; i < length; i++) {\n            if (one[i] !== two[i]) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    function SetElementBackgroundColorAndRadius(element, cornerRadius, backgroundColor) {\n        element.style.backgroundColor = `rgba(${backgroundColor.r.value}, ${backgroundColor.g.value}, ${backgroundColor.b.value}, ${backgroundColor.a.value / 255})`;\n        if (cornerRadius.topLeft.value > 0) {\n            element.style.borderTopLeftRadius = cornerRadius.topLeft.value + 'px';\n        }\n        if (cornerRadius.topRight.value > 0) {\n            element.style.borderTopRightRadius = cornerRadius.topRight.value + 'px';\n        }\n        if (cornerRadius.bottomLeft.value > 0) {\n            element.style.borderBottomLeftRadius = cornerRadius.bottomLeft.value + 'px';\n        }\n        if (cornerRadius.bottomRight.value > 0) {\n            element.style.borderBottomRightRadius = cornerRadius.bottomRight.value + 'px';\n        }\n    }\n\n    function renderLoopHTML() {\n        let capacity = memoryDataView.getInt32(scratchSpaceAddress, true);\n        let length = memoryDataView.getInt32(scratchSpaceAddress + 4, true);\n        let arrayOffset = memoryDataView.getUint32(scratchSpaceAddress + 8, true);\n        let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }];\n        let previousId = 0;\n        for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) {\n            let entireRenderCommandMemory = new Uint8Array(memoryDataView.buffer.slice(arrayOffset, arrayOffset + renderCommandSize));\n            let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition);\n            let parentElement = scissorStack[scissorStack.length - 1];\n            let element = null;\n            let isMultiConfigElement = previousId === renderCommand.id.value;\n            if (!elementCache[renderCommand.id.value]) {\n                let elementType = 'div';\n                switch (renderCommand.commandType.value & 0xff) {\n                    case CLAY_RENDER_COMMAND_TYPE_TEXT:\n                    case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                        if (renderCommand.userData.value !== 0) {\n                            if (readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition).link.length.value > 0) {\n                                elementType = 'a';\n                            }\n                        }\n                        break;\n                    }\n                    case CLAY_RENDER_COMMAND_TYPE_IMAGE: {\n                        elementType = 'img'; break;\n                    }\n                    default: break;\n                }\n                element = document.createElement(elementType);\n                element.id = renderCommand.id.value;\n                if (renderCommand.commandType.value === CLAY_RENDER_COMMAND_TYPE_SCISSOR_START) {\n                    element.style.overflow = 'hidden';\n                }\n                elementCache[renderCommand.id.value] = {\n                    exists: true,\n                    element: element,\n                    previousMemoryCommand: new Uint8Array(0),\n                    previousMemoryConfig: new Uint8Array(0),\n                    previousMemoryText: new Uint8Array(0)\n                };\n            }\n\n            let elementData = elementCache[renderCommand.id.value];\n            element = elementData.element;\n            if (!isMultiConfigElement && Array.prototype.indexOf.call(parentElement.element.children, element) !== parentElement.nextElementIndex) {\n                if (parentElement.nextElementIndex === 0) {\n                    parentElement.element.insertAdjacentElement('afterbegin', element);\n                } else {\n                    parentElement.element.childNodes[Math.min(parentElement.nextElementIndex - 1, parentElement.element.childNodes.length - 1)].insertAdjacentElement('afterend', element);\n                }\n            }\n\n            elementData.exists = true;\n            // Don't get me started. Cheaper to compare the render command memory than to update HTML elements\n            let dirty = MemoryIsDifferent(elementData.previousMemoryCommand, entireRenderCommandMemory, renderCommandSize) && !isMultiConfigElement;\n            if (!isMultiConfigElement) {\n                parentElement.nextElementIndex++;\n            }\n\n            previousId = renderCommand.id.value;\n\n            elementData.previousMemoryCommand = entireRenderCommandMemory;\n            let offsetX = scissorStack.length > 0 ? scissorStack[scissorStack.length - 1].nextAllocation.x : 0;\n            let offsetY = scissorStack.length > 0 ? scissorStack[scissorStack.length - 1].nextAllocation.y : 0;\n            if (dirty) {\n                element.style.transform = `translate(${Math.round(renderCommand.boundingBox.x.value - offsetX)}px, ${Math.round(renderCommand.boundingBox.y.value - offsetY)}px)`\n                element.style.width = Math.round(renderCommand.boundingBox.width.value) + 'px';\n                element.style.height = Math.round(renderCommand.boundingBox.height.value) + 'px';\n            }\n\n            // note: commandType is packed to uint8_t and has 3 garbage bytes of padding\n            switch(renderCommand.commandType.value & 0xff) {\n                case (CLAY_RENDER_COMMAND_TYPE_NONE): {\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {\n                    let config = renderCommand.renderData.rectangle;\n                    let configMemory = JSON.stringify(config);\n                    if (configMemory === elementData.previousMemoryConfig) {\n                        break;\n                    }\n\n                    SetElementBackgroundColorAndRadius(element, config.cornerRadius, config.backgroundColor);\n                    if (renderCommand.userData.value !== 0) {\n                        let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);\n\n                        let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0;\n                        memoryDataView.setUint32(0, renderCommand.id.value, true);\n                        if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {\n                            window.location.href = linkContents;\n                        }\n                        if (linkContents.length > 0) {\n                            element.href = linkContents;\n                        }\n\n                        if (linkContents.length > 0 || customData.cursorPointer.value) {\n                            element.style.pointerEvents = 'all';\n                            element.style.cursor = 'pointer';\n                        }\n                    }\n\n                    elementData.previousMemoryConfig = configMemory;\n\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_BORDER): {\n                    let config = renderCommand.renderData.border;\n                    let configMemory = JSON.stringify(config);\n                    if (configMemory === elementData.previousMemoryConfig) {\n                        break;\n                    }\n                    let color = config.color;\n                    elementData.previousMemoryConfig = configMemory;\n                    if (config.width.left.value > 0) {\n                        element.style.borderLeft = `${config.width.left.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.width.right.value > 0) {\n                        element.style.borderRight = `${config.width.right.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.width.top.value > 0) {\n                        element.style.borderTop = `${config.width.top.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.width.bottom.value > 0) {\n                        element.style.borderBottom = `${config.width.bottom.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.cornerRadius.topLeft.value > 0) {\n                        element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';\n                    }\n                    if (config.cornerRadius.topRight.value > 0) {\n                        element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px';\n                    }\n                    if (config.cornerRadius.bottomLeft.value > 0) {\n                        element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px';\n                    }\n                    if (config.cornerRadius.bottomRight.value > 0) {\n                        element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px';\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_TEXT): {\n                    let config = renderCommand.renderData.text;\n                    let configMemory = JSON.stringify(config);\n                    let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value));\n                    if (configMemory !== elementData.previousMemoryConfig) {\n                        element.className = 'text';\n                        let textColor = config.textColor;\n                        let fontSize = Math.round(config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR);\n                        element.style.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`;\n                        element.style.fontFamily = fontsById[config.fontId.value];\n                        element.style.fontSize = fontSize + 'px';\n                        if (renderCommand.userData.value !== 0) {\n                            let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);\n                            element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all';\n                            let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0;\n                            memoryDataView.setUint32(0, renderCommand.id.value, true);\n                            if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {\n                                window.location.href = linkContents;\n                            }\n                            if (linkContents.length > 0) {\n                                element.href = linkContents;\n                            }\n\n                            if (linkContents.length > 0 || customData.cursorPointer.value) {\n                                element.style.pointerEvents = 'all';\n                                element.style.cursor = 'pointer';\n                            }\n                        }\n                        elementData.previousMemoryConfig = configMemory;\n                    }\n                    if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) {\n                        element.innerHTML = textDecoder.decode(stringContents);\n                    }\n                    elementData.previousMemoryText = stringContents;\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {\n                    scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 });\n                    let config = renderCommand.renderData.clip;\n                    let configMemory = JSON.stringify(config);\n                    if (configMemory === elementData.previousMemoryConfig) {\n                        break;\n                    }\n                    if (config.horizontal.value) {\n                        element.style.overflowX = 'scroll';\n                        element.style.pointerEvents = 'auto';\n                    }\n                    if (config.vertical.value) {\n                        element.style.overflowY = 'scroll';\n                        element.style.pointerEvents = 'auto';\n                    }\n                    elementData.previousMemoryConfig = configMemory;\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {\n                    scissorStack.splice(scissorStack.length - 1, 1);\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {\n                    let config = renderCommand.renderData.image;\n                    let imageURL = readStructAtAddress(config.imageData.value, stringDefinition);\n                    let srcContents = new Uint8Array(memoryDataView.buffer.slice(imageURL.chars.value, imageURL.chars.value + imageURL.length.value));\n                    if (srcContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(srcContents, elementData.previousMemoryText, srcContents.length)) {\n                        element.src = textDecoder.decode(srcContents);\n                    }\n                    elementData.previousMemoryText = srcContents;\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;\n                default: {\n                    console.log(\"Error: unhandled render command\");\n                }\n            }\n        }\n\n        for (const key of Object.keys(elementCache)) {\n            if (elementCache[key].exists) {\n                elementCache[key].exists = false;\n            } else {\n                elementCache[key].element.remove();\n                delete elementCache[key];\n            }\n        }\n    }\n\n    function renderLoopCanvas() {\n    // Note: Rendering to canvas needs to be scaled up by window.devicePixelRatio in both width and height.\n    // e.g. if we're working on a device where devicePixelRatio is 2, we need to render\n    // everything at width^2 x height^2 resolution, then scale back down with css to get the correct pixel density.\n        let capacity = memoryDataView.getUint32(scratchSpaceAddress, true);\n        let length = memoryDataView.getUint32(scratchSpaceAddress + 4, true);\n        let arrayOffset = memoryDataView.getUint32(scratchSpaceAddress + 8, true);\n        window.canvasRoot.width = window.innerWidth * window.devicePixelRatio;\n        window.canvasRoot.height = window.innerHeight * window.devicePixelRatio;\n        window.canvasRoot.style.width = window.innerWidth + 'px';\n        window.canvasRoot.style.height = window.innerHeight + 'px';\n        let ctx = window.canvasContext;\n        let scale = window.devicePixelRatio;\n        for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) {\n            let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition);\n            let boundingBox = renderCommand.boundingBox;\n            \n            // note: commandType is packed to uint8_t and has 3 garbage bytes of padding\n            switch(renderCommand.commandType.value & 0xff) {\n                case (CLAY_RENDER_COMMAND_TYPE_NONE): {\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {\n                    let config = renderCommand.renderData.rectangle;\n                    let color = config.backgroundColor;\n                    ctx.beginPath();\n                    window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                    window.canvasContext.roundRect(\n                        boundingBox.x.value * scale, // x\n                        boundingBox.y.value * scale, // y\n                        boundingBox.width.value * scale, // width\n                        boundingBox.height.value * scale,\n                        [config.cornerRadius.topLeft.value * scale, config.cornerRadius.topRight.value * scale, config.cornerRadius.bottomRight.value * scale, config.cornerRadius.bottomLeft.value * scale]) // height;\n                    ctx.fill();\n                    ctx.closePath();\n                    // Handle link clicks\n                    if (renderCommand.userData.value !== 0) {\n                        let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);\n                        let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0;\n                        memoryDataView.setUint32(0, renderCommand.id.value, true);\n                        if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {\n                            window.location.href = linkContents;\n                        }\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_BORDER): {\n                    let config = renderCommand.renderData.border;\n                    let color = config.color;\n                    ctx.beginPath();\n                    ctx.moveTo(boundingBox.x.value * scale, boundingBox.y.value * scale);\n                    // Top Left Corner\n                    if (config.cornerRadius.topLeft.value > 0) {\n                        let lineWidth = config.width.top.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, config.cornerRadius.topLeft.value * scale);\n                        ctx.stroke();\n                    }\n                    // Top border\n                    if (config.width.top.value > 0) {\n                        let lineWidth = config.width.top.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    // Top Right Corner\n                    if (config.cornerRadius.topRight.value > 0) {\n                        let lineWidth = config.width.top.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale, config.cornerRadius.topRight.value * scale);\n                        ctx.stroke();\n                    }\n                    // Right border\n                    if (config.width.right.value > 0) {\n                        let lineWidth = config.width.right.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.topRight.value - halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    // Bottom Right Corner\n                    if (config.cornerRadius.bottomRight.value > 0) {\n                        let lineWidth = config.width.top.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, config.cornerRadius.bottomRight.value * scale);\n                        ctx.stroke();\n                    }\n                    // Bottom Border\n                    if (config.width.bottom.value > 0) {\n                        let lineWidth = config.width.bottom.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    // Bottom Left Corner\n                    if (config.cornerRadius.bottomLeft.value > 0) {\n                        let lineWidth = config.width.bottom.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, (boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomLeft.value - halfLineWidth) * scale, config.cornerRadius.bottomLeft.value * scale);\n                        ctx.stroke();\n                    }\n                    // Left Border\n                    if (config.width.left.value > 0) {\n                        let lineWidth = config.width.left.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomLeft.value - halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.bottomRight.value + halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    ctx.closePath();\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_TEXT): {\n                    let config = renderCommand.renderData.text;\n                    let textContents = config.stringContents;\n                    let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value));\n                    let fontSize = config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR * scale;\n                    ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`;\n                    let color = config.textColor;\n                    ctx.textBaseline = 'middle';\n                    ctx.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                    ctx.fillText(textDecoder.decode(stringContents), boundingBox.x.value * scale, (boundingBox.y.value + boundingBox.height.value / 2 + 1) * scale);\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {\n                    window.canvasContext.save();\n                    window.canvasContext.beginPath();\n                    window.canvasContext.rect(boundingBox.x.value * scale, boundingBox.y.value * scale, boundingBox.width.value * scale, boundingBox.height.value * scale);\n                    window.canvasContext.clip();\n                    window.canvasContext.closePath();\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {\n                    window.canvasContext.restore();\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {\n                    let config = renderCommand.renderData.image;\n                    let imageURL = readStructAtAddress(config.imageData.value, stringDefinition);\n                    let src = textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(imageURL.chars.value, imageURL.chars.value + imageURL.length.value)));\n                    if (!imageCache[src]) {\n                        imageCache[src] = {\n                            image: new Image(),\n                            loaded: false,\n                        }\n                        imageCache[src].image.onload = () => imageCache[src].loaded = true;\n                        imageCache[src].image.src = src;\n                    } else if (imageCache[src].loaded) {\n                        ctx.drawImage(imageCache[src].image, boundingBox.x.value * scale, boundingBox.y.value * scale, boundingBox.width.value * scale, boundingBox.height.value * scale);\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;\n            }\n        }\n    }\n\n    function renderLoop(currentTime) {\n        const elapsed = currentTime - previousFrameTime;\n        previousFrameTime = currentTime;\n        let activeRendererIndex = memoryDataView.getUint32(instance.exports.ACTIVE_RENDERER_INDEX.value, true);\n        if (activeRendererIndex === 0) {\n            instance.exports.UpdateDrawFrame(scratchSpaceAddress, window.innerWidth, window.innerHeight, 0, 0, window.mousePositionXThisFrame, window.mousePositionYThisFrame, window.touchDown, window.mouseDown, 0, 0, window.dKeyPressedThisFrame, elapsed / 1000);\n        } else {\n            instance.exports.UpdateDrawFrame(scratchSpaceAddress, window.innerWidth, window.innerHeight, window.mouseWheelXThisFrame, window.mouseWheelYThisFrame, window.mousePositionXThisFrame, window.mousePositionYThisFrame, window.touchDown, window.mouseDown, window.arrowKeyDownPressedThisFrame, window.arrowKeyUpPressedThisFrame, window.dKeyPressedThisFrame, elapsed / 1000);\n        }\n        let rendererChanged = activeRendererIndex !== window.previousActiveRendererIndex;\n        switch (activeRendererIndex) {\n            case 0: {\n                renderLoopHTML();\n                if (rendererChanged) {\n                    window.htmlRoot.style.display = 'block';\n                    window.canvasRoot.style.display = 'none';\n                }\n                break;\n            }\n            case 1: {\n                renderLoopCanvas();\n                if (rendererChanged) {\n                    window.htmlRoot.style.display = 'none';\n                    window.canvasRoot.style.display = 'block';\n                }\n                break;\n            }\n        }\n        window.previousActiveRendererIndex = activeRendererIndex;\n        requestAnimationFrame(renderLoop);\n        window.mouseDownThisFrame = false;\n        window.arrowKeyUpPressedThisFrame = false;\n        window.arrowKeyDownPressedThisFrame = false;\n        window.dKeyPressedThisFrame = false;\n    }\n    init();\n</script>\n<body>\n</body>\n</html>"
  },
  {
    "path": "examples/clay-official-website/main.c",
    "content": "#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n\ndouble windowWidth = 1024, windowHeight = 768;\nfloat modelPageOneZRotation = 0;\nuint32_t ACTIVE_RENDERER_INDEX = 0;\n\nconst uint32_t FONT_ID_BODY_16 = 0;\nconst uint32_t FONT_ID_TITLE_56 = 1;\nconst uint32_t FONT_ID_BODY_24 = 2;\nconst uint32_t FONT_ID_BODY_36 = 3;\nconst uint32_t FONT_ID_TITLE_36 = 4;\nconst uint32_t FONT_ID_MONOSPACE_24 = 5;\n\nconst Clay_Color COLOR_LIGHT = (Clay_Color) {244, 235, 230, 255};\nconst Clay_Color COLOR_LIGHT_HOVER = (Clay_Color) {224, 215, 210, 255};\nconst Clay_Color COLOR_RED = (Clay_Color) {168, 66, 28, 255};\nconst Clay_Color COLOR_RED_HOVER = (Clay_Color) {148, 46, 8, 255};\nconst Clay_Color COLOR_ORANGE = (Clay_Color) {225, 138, 50, 255};\nconst Clay_Color COLOR_BLUE = (Clay_Color) {111, 173, 162, 255};\n\n// Colors for top stripe\nconst Clay_Color COLOR_TOP_BORDER_1 = (Clay_Color) {168, 66, 28, 255};\nconst Clay_Color COLOR_TOP_BORDER_2 = (Clay_Color) {223, 110, 44, 255};\nconst Clay_Color COLOR_TOP_BORDER_3 = (Clay_Color) {225, 138, 50, 255};\nconst Clay_Color COLOR_TOP_BORDER_4 = (Clay_Color) {236, 189, 80, 255};\nconst Clay_Color COLOR_TOP_BORDER_5 = (Clay_Color) {240, 213, 137, 255};\n\nconst Clay_Color COLOR_BLOB_BORDER_1 = (Clay_Color) {168, 66, 28, 255};\nconst Clay_Color COLOR_BLOB_BORDER_2 = (Clay_Color) {203, 100, 44, 255};\nconst Clay_Color COLOR_BLOB_BORDER_3 = (Clay_Color) {225, 138, 50, 255};\nconst Clay_Color COLOR_BLOB_BORDER_4 = (Clay_Color) {236, 159, 70, 255};\nconst Clay_Color COLOR_BLOB_BORDER_5 = (Clay_Color) {240, 189, 100, 255};\n\n#define RAYLIB_VECTOR2_TO_CLAY_VECTOR2(vector) (Clay_Vector2) { .x = (vector).x, .y = (vector).y }\n\nClay_TextElementConfig headerTextConfig = (Clay_TextElementConfig) { .fontId = 2, .fontSize = 24, .textColor = {61, 26, 5, 255} };\nClay_TextElementConfig blobTextConfig = (Clay_TextElementConfig) { .fontId = 2, .fontSize = 30, .textColor = {244, 235, 230, 255} };\n\ntypedef struct {\n    void* memory;\n    uintptr_t offset;\n} Arena;\n\nArena frameArena = {};\n\ntypedef struct d {\n    Clay_String link;\n    bool cursorPointer;\n    bool disablePointerEvents;\n} CustomHTMLData;\n\nCustomHTMLData* FrameAllocateCustomData(CustomHTMLData data) {\n    CustomHTMLData *customData = (CustomHTMLData *)(frameArena.memory + frameArena.offset);\n    *customData = data;\n    frameArena.offset += sizeof(CustomHTMLData);\n    return customData;\n}\n\nClay_String* FrameAllocateString(Clay_String string) {\n    Clay_String *allocated = (Clay_String *)(frameArena.memory + frameArena.offset);\n    *allocated = string;\n    frameArena.offset += sizeof(Clay_String);\n    return allocated;\n}\n\nvoid LandingPageBlob(int index, int fontSize, Clay_Color color, Clay_String text, Clay_String imageURL) {\n    CLAY(CLAY_IDI(\"HeroBlob\", index), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 480) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }, .border = { .color = color, .width = { 2, 2, 2, 2 }}, .cornerRadius = CLAY_CORNER_RADIUS(10) }) {\n        CLAY(CLAY_IDI(\"CheckImage\", index), { .layout = { .sizing = { CLAY_SIZING_FIXED(32) } }, .aspectRatio = { 1 }, .image = { .imageData = FrameAllocateString(imageURL) } }) {}\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = fontSize, .fontId = FONT_ID_BODY_24, .textColor = color }));\n    }\n}\n\nvoid LandingPageDesktop() {\n    CLAY(CLAY_ID(\"LandingPage1Desktop\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {\n        CLAY(CLAY_ID(\"LandingPage1\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED } }) {\n            CLAY(CLAY_ID(\"LeftText\"), { .layout = { .sizing = { .width = CLAY_SIZING_PERCENT(0.55f) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {\n                CLAY_TEXT(CLAY_STRING(\"Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.\"), CLAY_TEXT_CONFIG({ .fontSize = 56, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));\n                CLAY(CLAY_ID(\"LandingPageSpacer\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}\n                CLAY_TEXT(CLAY_STRING(\"Clay is laying out this webpage right now!\"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE }));\n            }\n            CLAY(CLAY_ID(\"HeroImageOuter\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_PERCENT(0.45f) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 } }) {\n                LandingPageBlob(1, 32, COLOR_BLOB_BORDER_5, CLAY_STRING(\"High performance\"), CLAY_STRING(\"/clay/images/check_5.png\"));\n                LandingPageBlob(2, 32, COLOR_BLOB_BORDER_4, CLAY_STRING(\"Flexbox-style responsive layout\"), CLAY_STRING(\"/clay/images/check_4.png\"));\n                LandingPageBlob(3, 32, COLOR_BLOB_BORDER_3, CLAY_STRING(\"Declarative syntax\"), CLAY_STRING(\"/clay/images/check_3.png\"));\n                LandingPageBlob(4, 32, COLOR_BLOB_BORDER_2, CLAY_STRING(\"Single .h file for C/C++\"), CLAY_STRING(\"/clay/images/check_2.png\"));\n                LandingPageBlob(5, 32, COLOR_BLOB_BORDER_1, CLAY_STRING(\"Compile to 15kb .wasm\"), CLAY_STRING(\"/clay/images/check_1.png\"));\n            }\n        }\n    }\n}\n\nvoid LandingPageMobile() {\n    CLAY(CLAY_ID(\"LandingPage1Mobile\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32 }, .childGap = 32 } }) {\n        CLAY(CLAY_ID(\"LeftText\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {\n            CLAY_TEXT(CLAY_STRING(\"Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.\"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));\n            CLAY(CLAY_ID(\"LandingPageSpacer\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}\n            CLAY_TEXT(CLAY_STRING(\"Clay is laying out this webpage right now!\"), CLAY_TEXT_CONFIG({ .fontSize = 32, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE }));\n        }\n        CLAY(CLAY_ID(\"HeroImageOuter\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 } }) {\n            LandingPageBlob(1, 28, COLOR_BLOB_BORDER_5, CLAY_STRING(\"High performance\"), CLAY_STRING(\"/clay/images/check_5.png\"));\n            LandingPageBlob(2, 28, COLOR_BLOB_BORDER_4, CLAY_STRING(\"Flexbox-style responsive layout\"), CLAY_STRING(\"/clay/images/check_4.png\"));\n            LandingPageBlob(3, 28, COLOR_BLOB_BORDER_3, CLAY_STRING(\"Declarative syntax\"), CLAY_STRING(\"/clay/images/check_3.png\"));\n            LandingPageBlob(4, 28, COLOR_BLOB_BORDER_2, CLAY_STRING(\"Single .h file for C/C++\"), CLAY_STRING(\"/clay/images/check_2.png\"));\n            LandingPageBlob(5, 28, COLOR_BLOB_BORDER_1, CLAY_STRING(\"Compile to 15kb .wasm\"), CLAY_STRING(\"/clay/images/check_1.png\"));\n        }\n    }\n}\n\nvoid FeatureBlocksDesktop() {\n    CLAY(CLAY_ID(\"FeatureBlocksOuter\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) } } }) {\n        CLAY(CLAY_ID(\"FeatureBlocksInner\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED } }) {\n            Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED });\n            CLAY(CLAY_ID(\"HFileBoxOuter\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 } }) {\n                CLAY(CLAY_ID(\"HFileIncludeOuter\"), { .layout = { .padding = {8, 4} }, .backgroundColor = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {\n                    CLAY_TEXT(CLAY_STRING(\"#include clay.h\"), CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT }));\n                }\n                CLAY_TEXT(CLAY_STRING(\"~2000 lines of C99.\"), textConfig);\n                CLAY_TEXT(CLAY_STRING(\"Zero dependencies, including no C standard library.\"), textConfig);\n            }\n            CLAY(CLAY_ID(\"BringYourOwnRendererOuter\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 } }) {\n                CLAY_TEXT(CLAY_STRING(\"Renderer agnostic.\"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE }));\n                CLAY_TEXT(CLAY_STRING(\"Layout with clay, then render with Raylib, WebGL Canvas or even as HTML.\"), textConfig);\n                CLAY_TEXT(CLAY_STRING(\"Flexible output for easy compositing in your custom engine or environment.\"), textConfig);\n            }\n        }\n    }\n}\n\nvoid FeatureBlocksMobile() {\n    CLAY(CLAY_ID(\"FeatureBlocksInner\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) } }, .border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED } }) {\n        Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED });\n        CLAY(CLAY_ID(\"HFileBoxOuter\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 } }) {\n            CLAY(CLAY_ID(\"HFileIncludeOuter\"), { .layout = { .padding = {8, 4} }, .backgroundColor = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {\n                CLAY_TEXT(CLAY_STRING(\"#include clay.h\"), CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT }));\n            }\n            CLAY_TEXT(CLAY_STRING(\"~2000 lines of C99.\"), textConfig);\n            CLAY_TEXT(CLAY_STRING(\"Zero dependencies, including no C standard library.\"), textConfig);\n        }\n        CLAY(CLAY_ID(\"BringYourOwnRendererOuter\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 } }) {\n            CLAY_TEXT(CLAY_STRING(\"Renderer agnostic.\"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE }));\n            CLAY_TEXT(CLAY_STRING(\"Layout with clay, then render with Raylib, WebGL Canvas or even as HTML.\"), textConfig);\n            CLAY_TEXT(CLAY_STRING(\"Flexible output for easy compositing in your custom engine or environment.\"), textConfig);\n        }\n    }\n}\n\nvoid DeclarativeSyntaxPageDesktop() {\n    CLAY(CLAY_ID(\"SyntaxPageDesktop\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {\n        CLAY(CLAY_ID(\"SyntaxPage\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED }}) {\n            CLAY(CLAY_ID(\"SyntaxPageLeftText\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {\n                CLAY_TEXT(CLAY_STRING(\"Declarative Syntax\"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));\n                CLAY(CLAY_ID(\"SyntaxSpacer\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) } } }) {}\n                CLAY_TEXT(CLAY_STRING(\"Flexible and readable declarative syntax with nested UI element hierarchies.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n                CLAY_TEXT(CLAY_STRING(\"Mix elements with standard C code like loops, conditionals and functions.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n                CLAY_TEXT(CLAY_STRING(\"Create your own library of re-usable components from UI primitives like text, images and rectangles.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n            }\n            CLAY(CLAY_ID(\"SyntaxPageRightImage\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) {\n                CLAY(CLAY_ID(\"SyntaxPageRightImageInner\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .aspectRatio = { 1136.0 / 1194.0 }, .image = { .imageData = FrameAllocateString(CLAY_STRING(\"/clay/images/declarative.png\")) } }) {}\n            }\n        }\n    }\n}\n\nvoid DeclarativeSyntaxPageMobile() {\n    CLAY(CLAY_ID(\"SyntaxPageDesktop\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 16 } }) {\n        CLAY(CLAY_ID(\"SyntaxPageLeftText\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {\n            CLAY_TEXT(CLAY_STRING(\"Declarative Syntax\"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));\n            CLAY(CLAY_ID(\"SyntaxSpacer\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) } } }) {}\n            CLAY_TEXT(CLAY_STRING(\"Flexible and readable declarative syntax with nested UI element hierarchies.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n            CLAY_TEXT(CLAY_STRING(\"Mix elements with standard C code like loops, conditionals and functions.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n            CLAY_TEXT(CLAY_STRING(\"Create your own library of re-usable components from UI primitives like text, images and rectangles.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n        }\n        CLAY(CLAY_ID(\"SyntaxPageRightImage\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) {\n            CLAY(CLAY_ID(\"SyntaxPageRightImageInner\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .aspectRatio = { 1136.0 / 1194.0 }, .image = { .imageData = FrameAllocateString(CLAY_STRING(\"/clay/images/declarative.png\")) } }) {}\n        }\n    }\n}\n\nClay_Color ColorLerp(Clay_Color a, Clay_Color b, float amount) {\n    return (Clay_Color) {\n        .r = a.r + (b.r - a.r) * amount,\n        .g = a.g + (b.g - a.g) * amount,\n        .b = a.b + (b.b - a.b) * amount,\n        .a = a.a + (b.a - a.a) * amount,\n    };\n}\n\nClay_String LOREM_IPSUM_TEXT = CLAY_STRING(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\");\n\nvoid HighPerformancePageDesktop(float lerpValue) {\n    CLAY(CLAY_ID(\"PerformanceOuter\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {82, 82, 32, 32}, .childGap = 64 }, .backgroundColor = COLOR_RED }) {\n        CLAY(CLAY_ID(\"PerformanceLeftText\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {\n            CLAY_TEXT(CLAY_STRING(\"High Performance\"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));\n            CLAY(CLAY_ID(\"PerformanceSpacer\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}\n            CLAY_TEXT(CLAY_STRING(\"Fast enough to recompute your entire UI every frame.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));\n            CLAY_TEXT(CLAY_STRING(\"Small memory footprint (3.5mb default) with static allocation & reuse. No malloc / free.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));\n            CLAY_TEXT(CLAY_STRING(\"Simplify animations and reactive UI design by avoiding the standard performance hacks.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));\n        }\n        CLAY(CLAY_ID(\"PerformanceRightImageOuter\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {\n            CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }, .border = { .width = {2, 2, 2, 2}, .color = COLOR_LIGHT } }) {\n                CLAY(CLAY_ID(\"AnimationDemoContainerLeft\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.3f + 0.4f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32) }, .backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) }) {\n                    CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));\n                }\n                CLAY(CLAY_ID(\"AnimationDemoContainerRight\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER},  .padding = CLAY_PADDING_ALL(32) }, .backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) }) {\n                    CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));\n                }\n            }\n        }\n    }\n}\n\nvoid HighPerformancePageMobile(float lerpValue) {\n    CLAY(CLAY_ID(\"PerformanceOuter\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 32 }, .backgroundColor = COLOR_RED }) {\n        CLAY(CLAY_ID(\"PerformanceLeftText\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {\n            CLAY_TEXT(CLAY_STRING(\"High Performance\"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));\n            CLAY(CLAY_ID(\"PerformanceSpacer\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}\n            CLAY_TEXT(CLAY_STRING(\"Fast enough to recompute your entire UI every frame.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));\n            CLAY_TEXT(CLAY_STRING(\"Small memory footprint (3.5mb default) with static allocation & reuse. No malloc / free.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));\n            CLAY_TEXT(CLAY_STRING(\"Simplify animations and reactive UI design by avoiding the standard performance hacks.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));\n        }\n        CLAY(CLAY_ID(\"PerformanceRightImageOuter\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {\n            CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }, .border = { .width = { 2, 2, 2, 2 }, .color = COLOR_LIGHT }}) {\n                CLAY(CLAY_ID(\"AnimationDemoContainerLeft\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.35f + 0.3f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) }) {\n                    CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));\n                }\n                CLAY(CLAY_ID(\"AnimationDemoContainerRight\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) }) {\n                    CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));\n                }\n            }\n        }\n    }\n}\n\nvoid HandleRendererButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData) {\n    if (pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n        ACTIVE_RENDERER_INDEX = (uint32_t)userData;\n        Clay_SetCullingEnabled(ACTIVE_RENDERER_INDEX == 1);\n        Clay_SetExternalScrollHandlingEnabled(ACTIVE_RENDERER_INDEX == 0);\n    }\n}\n\nvoid RendererButtonActive(Clay_String text) {\n    CLAY_AUTO_ID({\n        .layout = { .sizing = {CLAY_SIZING_FIXED(300) }, .padding = CLAY_PADDING_ALL(16) },\n        .backgroundColor = Clay_Hovered() ? COLOR_RED_HOVER : COLOR_RED,\n        .cornerRadius = CLAY_CORNER_RADIUS(10),\n        .userData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true, .cursorPointer = true })\n    }) {\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));\n    }\n}\n\nvoid RendererButtonInactive(Clay_String text, size_t rendererIndex) {\n    CLAY_AUTO_ID({\n        .layout = { .sizing = {CLAY_SIZING_FIXED(300)}, .padding = CLAY_PADDING_ALL(16) },\n        .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },\n        .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,\n        .cornerRadius = CLAY_CORNER_RADIUS(10),\n        .userData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true, .cursorPointer = true })\n    }) {\n        Clay_OnHover(HandleRendererButtonInteraction, (void *)rendererIndex);\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n    }\n}\n\nvoid RendererPageDesktop() {\n    CLAY(CLAY_ID(\"RendererPageDesktop\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {\n        CLAY(CLAY_ID(\"RendererPage\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED } }) {\n            CLAY(CLAY_ID(\"RendererLeftText\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {\n                CLAY_TEXT(CLAY_STRING(\"Renderer & Platform Agnostic\"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));\n                CLAY(CLAY_ID(\"RendererSpacerLeft\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}\n                CLAY_TEXT(CLAY_STRING(\"Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n                CLAY_TEXT(CLAY_STRING(\"Write your own renderer in a few hundred lines of code, or use the provided examples for Raylib, WebGL canvas and more.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n                CLAY_TEXT(CLAY_STRING(\"There's even an HTML renderer - you're looking at it right now!\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n            }\n            CLAY(CLAY_ID(\"RendererRightText\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .childAlignment = {CLAY_ALIGN_X_CENTER}, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {\n                CLAY_TEXT(CLAY_STRING(\"Try changing renderer!\"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE }));\n                CLAY(CLAY_ID(\"RendererSpacerRight\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 32) } } }) {}\n                if (ACTIVE_RENDERER_INDEX == 0) {\n                    RendererButtonActive(CLAY_STRING(\"HTML Renderer\"));\n                    RendererButtonInactive(CLAY_STRING(\"Canvas Renderer\"), 1);\n                } else {\n                    RendererButtonInactive(CLAY_STRING(\"HTML Renderer\"), 0);\n                    RendererButtonActive(CLAY_STRING(\"Canvas Renderer\"));\n                }\n            }\n        }\n    }\n}\n\nvoid RendererPageMobile() {\n    CLAY(CLAY_ID(\"RendererMobile\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32}, .childGap = 32 }, .backgroundColor = COLOR_LIGHT }) {\n        CLAY(CLAY_ID(\"RendererLeftText\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {\n            CLAY_TEXT(CLAY_STRING(\"Renderer & Platform Agnostic\"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));\n            CLAY(CLAY_ID(\"RendererSpacerLeft\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}\n            CLAY_TEXT(CLAY_STRING(\"Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n            CLAY_TEXT(CLAY_STRING(\"Write your own renderer in a few hundred lines of code, or use the provided examples for Raylib, WebGL canvas and more.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n            CLAY_TEXT(CLAY_STRING(\"There's even an HTML renderer - you're looking at it right now!\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));\n        }\n        CLAY(CLAY_ID(\"RendererRightText\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {\n            CLAY_TEXT(CLAY_STRING(\"Try changing renderer!\"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE }));\n            CLAY(CLAY_ID(\"RendererSpacerRight\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 32) }} }) {}\n            if (ACTIVE_RENDERER_INDEX == 0) {\n                RendererButtonActive(CLAY_STRING(\"HTML Renderer\"));\n                RendererButtonInactive(CLAY_STRING(\"Canvas Renderer\"), 1);\n            } else {\n                RendererButtonInactive(CLAY_STRING(\"HTML Renderer\"), 0);\n                RendererButtonActive(CLAY_STRING(\"Canvas Renderer\"));\n            }\n        }\n    }\n}\n\nvoid DebuggerPageDesktop() {\n    CLAY(CLAY_ID(\"DebuggerDesktop\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 82, 82, 32, 32 }, .childGap = 64 }, .backgroundColor = COLOR_RED }) {\n        CLAY(CLAY_ID(\"DebuggerLeftText\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {\n            CLAY_TEXT(CLAY_STRING(\"Integrated Debug Tools\"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));\n            CLAY(CLAY_ID(\"DebuggerSpacer\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}\n            CLAY_TEXT(CLAY_STRING(\"Clay includes built in \\\"Chrome Inspector\\\"-style debug tooling.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));\n            CLAY_TEXT(CLAY_STRING(\"View your layout hierarchy and config in real time.\"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));\n            CLAY(CLAY_ID(\"DebuggerPageSpacer\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}\n            CLAY_TEXT(CLAY_STRING(\"Press the \\\"d\\\" key to try it out now!\"), CLAY_TEXT_CONFIG({ .fontSize = 32, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE }));\n        }\n        CLAY(CLAY_ID(\"DebuggerRightImageOuter\"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {\n            CLAY(CLAY_ID(\"DebuggerPageRightImageInner\"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 558) } }, .aspectRatio = { 1620.0 / 1474.0 }, .image = {.imageData = FrameAllocateString(CLAY_STRING(\"/clay/images/debugger.png\")) } }) {}\n        }\n    }\n}\n\ntypedef struct\n{\n    Clay_Vector2 clickOrigin;\n    Clay_Vector2 positionOrigin;\n    bool mouseDown;\n} ScrollbarData;\n\nScrollbarData scrollbarData = (ScrollbarData) {};\nfloat animationLerpValue = -1.0f;\n\nClay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) {\n    Clay_BeginLayout();\n    CLAY(CLAY_ID(\"OuterContainer\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {\n        CLAY(CLAY_ID(\"Header\"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(50) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .childGap = 16, .padding = { 32, 32 } } }) {\n            CLAY_TEXT(CLAY_STRING(\"Clay\"), &headerTextConfig);\n            CLAY(CLAY_ID(\"Spacer\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}\n            if (!mobileScreen) {\n                CLAY(CLAY_ID(\"LinkExamplesOuter\"), { .layout = { .padding = {8, 8} } }) {\n                    CLAY_TEXT(CLAY_STRING(\"Examples\"), CLAY_TEXT_CONFIG({\n                        .userData = FrameAllocateCustomData((CustomHTMLData) {\n                            .link = CLAY_STRING(\"https://github.com/nicbarker/clay/tree/main/examples\")\n                        }),\n                        .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));\n                }\n                CLAY(CLAY_ID(\"LinkDocsOuter\"), { .layout = { .padding = {8, 8} } }) {\n                    CLAY_TEXT(CLAY_STRING(\"Docs\"), CLAY_TEXT_CONFIG({\n                        .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING(\"https://github.com/nicbarker/clay/blob/main/README.md\") }),\n                        .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })\n                    );\n                }\n            }\n            CLAY_AUTO_ID({\n                .layout = { .padding = {16, 16, 6, 6} },\n                .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,\n                .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },\n                .cornerRadius = CLAY_CORNER_RADIUS(10),\n                .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING(\"https://discord.gg/b4FTWkxdvT\") }),\n            }) {\n                CLAY_TEXT(CLAY_STRING(\"Discord\"), CLAY_TEXT_CONFIG({\n                    .userData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true }),\n                    .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));\n            }\n            CLAY_AUTO_ID({\n                .layout = { .padding = {16, 16, 6, 6} },\n                .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,\n                .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },\n                .cornerRadius = CLAY_CORNER_RADIUS(10),\n                .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING(\"https://github.com/nicbarker/clay\") }),\n            }) {\n                CLAY_TEXT(CLAY_STRING(\"Github\"), CLAY_TEXT_CONFIG({\n                    .userData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true }),\n                    .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));\n            }\n        }\n        Clay_LayoutConfig topBorderConfig = (Clay_LayoutConfig) { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(4) }};\n        CLAY(CLAY_ID(\"TopBorder1\"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_5 }) {}\n        CLAY(CLAY_ID(\"TopBorder2\"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_4 }) {}\n        CLAY(CLAY_ID(\"TopBorder3\"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_3 }) {}\n        CLAY(CLAY_ID(\"TopBorder4\"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_2 }) {}\n        CLAY(CLAY_ID(\"TopBorder5\"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_1 }) {}\n        CLAY(CLAY_ID(\"OuterScrollContainer\"), {\n            .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM },\n            .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },\n            .border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED }\n        }) {\n            if (mobileScreen) {\n                LandingPageMobile();\n                FeatureBlocksMobile();\n                DeclarativeSyntaxPageMobile();\n                HighPerformancePageMobile(lerpValue);\n                RendererPageMobile();\n            } else {\n                LandingPageDesktop();\n                FeatureBlocksDesktop();\n                DeclarativeSyntaxPageDesktop();\n                HighPerformancePageDesktop(lerpValue);\n                RendererPageDesktop();\n                DebuggerPageDesktop();\n            }\n        }\n    }\n\n    if (!mobileScreen && ACTIVE_RENDERER_INDEX == 1) {\n        Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING(\"OuterScrollContainer\")));\n        Clay_Color scrollbarColor = (Clay_Color){225, 138, 50, 120};\n        if (scrollbarData.mouseDown) {\n            scrollbarColor = (Clay_Color){225, 138, 50, 200};\n        } else if (Clay_PointerOver(Clay_GetElementId(CLAY_STRING(\"ScrollBar\")))) {\n            scrollbarColor = (Clay_Color){225, 138, 50, 160};\n        }\n        float scrollHeight = scrollData.scrollContainerDimensions.height - 12;\n        CLAY(CLAY_ID(\"ScrollBar\"), {\n            .floating = { .offset = { .x = -6, .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollHeight + 6}, .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING(\"OuterScrollContainer\")).id, .attachPoints = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP }, .attachTo = CLAY_ATTACH_TO_PARENT },\n            .layout = { .sizing = {CLAY_SIZING_FIXED(10), CLAY_SIZING_FIXED((scrollHeight / scrollData.contentDimensions.height) * scrollHeight)} },\n            .backgroundColor = scrollbarColor,\n            .cornerRadius = CLAY_CORNER_RADIUS(5)\n        }) {}\n    }\n    return Clay_EndLayout();\n}\n\nbool debugModeEnabled = false;\n\nCLAY_WASM_EXPORT(\"SetScratchMemory\") void SetScratchMemory(void * memory) {\n    frameArena.memory = memory;\n}\n\nCLAY_WASM_EXPORT(\"UpdateDrawFrame\") Clay_RenderCommandArray UpdateDrawFrame(float width, float height, float mouseWheelX, float mouseWheelY, float mousePositionX, float mousePositionY, bool isTouchDown, bool isMouseDown, bool arrowKeyDownPressedThisFrame, bool arrowKeyUpPressedThisFrame, bool dKeyPressedThisFrame, float deltaTime) {\n    frameArena.offset = 0;\n    windowWidth = width;\n    windowHeight = height;\n    Clay_SetLayoutDimensions((Clay_Dimensions) { width, height });\n    Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING(\"OuterScrollContainer\")));\n    Clay_LayoutElementHashMapItem *perfPage = Clay__GetHashMapItem(Clay_GetElementId(CLAY_STRING(\"PerformanceOuter\")).id);\n    // NaN propagation can cause pain here\n    float perfPageYOffset = perfPage->boundingBox.y + scrollContainerData.scrollPosition->y;\n    if (deltaTime == deltaTime && (ACTIVE_RENDERER_INDEX == 1 || (perfPageYOffset < height && perfPageYOffset + perfPage->boundingBox.height > 0))) {\n        animationLerpValue += deltaTime;\n        if (animationLerpValue > 1) {\n            animationLerpValue -= 2;\n        }\n    }\n\n    if (dKeyPressedThisFrame) {\n        debugModeEnabled = !debugModeEnabled;\n        Clay_SetDebugModeEnabled(debugModeEnabled);\n    }\n    Clay_SetCullingEnabled(ACTIVE_RENDERER_INDEX == 1);\n    Clay_SetExternalScrollHandlingEnabled(ACTIVE_RENDERER_INDEX == 0);\n\n    Clay__debugViewHighlightColor = (Clay_Color) {105,210,231, 120};\n\n    Clay_SetPointerState((Clay_Vector2) {mousePositionX, mousePositionY}, isMouseDown || isTouchDown);\n\n    if (!isMouseDown) {\n        scrollbarData.mouseDown = false;\n    }\n\n    if (isMouseDown && !scrollbarData.mouseDown && Clay_PointerOver(Clay_GetElementId(CLAY_STRING(\"ScrollBar\")))) {\n        scrollbarData.clickOrigin = (Clay_Vector2) { mousePositionX, mousePositionY };\n        scrollbarData.positionOrigin = *scrollContainerData.scrollPosition;\n        scrollbarData.mouseDown = true;\n    } else if (scrollbarData.mouseDown) {\n        if (scrollContainerData.contentDimensions.height > 0) {\n            Clay_Vector2 ratio = (Clay_Vector2) {\n                scrollContainerData.contentDimensions.width / scrollContainerData.scrollContainerDimensions.width,\n                scrollContainerData.contentDimensions.height / scrollContainerData.scrollContainerDimensions.height,\n            };\n            if (scrollContainerData.config.vertical) {\n                scrollContainerData.scrollPosition->y = scrollbarData.positionOrigin.y + (scrollbarData.clickOrigin.y - mousePositionY) * ratio.y;\n            }\n            if (scrollContainerData.config.horizontal) {\n                scrollContainerData.scrollPosition->x = scrollbarData.positionOrigin.x + (scrollbarData.clickOrigin.x - mousePositionX) * ratio.x;\n            }\n        }\n    }\n\n    if (arrowKeyDownPressedThisFrame) {\n        if (scrollContainerData.contentDimensions.height > 0) {\n            scrollContainerData.scrollPosition->y = scrollContainerData.scrollPosition->y - 50;\n        }\n    } else if (arrowKeyUpPressedThisFrame) {\n        if (scrollContainerData.contentDimensions.height > 0) {\n            scrollContainerData.scrollPosition->y = scrollContainerData.scrollPosition->y + 50;\n        }\n    }\n\n    Clay_UpdateScrollContainers(isTouchDown, (Clay_Vector2) {mouseWheelX, mouseWheelY}, deltaTime);\n    bool isMobileScreen = windowWidth < 750;\n    if (debugModeEnabled) {\n        isMobileScreen = windowWidth < 950;\n    }\n    return CreateLayout(isMobileScreen, animationLerpValue < 0 ? (animationLerpValue + 1) : (1 - animationLerpValue));\n    //----------------------------------------------------------------------------------\n}\n\n// Dummy main() to please cmake - TODO get wasm working with cmake on this example\nint main() {\n    return 0;\n}\n"
  },
  {
    "path": "examples/cpp-project-example/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(clay_examples_cpp_project_example CXX)\n\nset(CMAKE_CXX_STANDARD 20)\nif(NOT MSVC)\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -g\")\nendif()\n\nadd_executable(clay_examples_cpp_project_example main.cpp)\n\ntarget_include_directories(clay_examples_cpp_project_example PUBLIC .)\n\nif(NOT MSVC)\n  set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\n  set(CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\nendif()\n"
  },
  {
    "path": "examples/cpp-project-example/main.cpp",
    "content": "#include <iostream>\n#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n\nClay_LayoutConfig layoutElement = Clay_LayoutConfig { .padding = {5} };\n\nvoid HandleClayErrors(Clay_ErrorData errorData) {\n    printf(\"%s\", errorData.errorText.chars);\n}\n\nint main(void) {\n    uint64_t totalMemorySize = Clay_MinMemorySize();\n    Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, (char *)malloc(totalMemorySize));\n    Clay_Initialize(clayMemory, Clay_Dimensions {1024,768}, Clay_ErrorHandler { HandleClayErrors });\n    Clay_BeginLayout();\n    CLAY_AUTO_ID({ .layout = layoutElement, .backgroundColor = {255,255,255,0} }) {\n        CLAY_TEXT(CLAY_STRING(\"\"), CLAY_TEXT_CONFIG({ .fontId = 0 }));\n    }\n    Clay_EndLayout();\n    return 0;\n}\n"
  },
  {
    "path": "examples/introducing-clay-video-demo/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(clay_examples_introducing_clay_video_demo C)\nset(CMAKE_C_STANDARD 99)\n\n# Adding Raylib\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\nset(BUILD_EXAMPLES OFF CACHE BOOL \"\" FORCE) # don't build the supplied examples\nset(BUILD_GAMES    OFF CACHE BOOL \"\" FORCE) # don't build the supplied example games\n\nFetchContent_Declare(\n    raylib\n    GIT_REPOSITORY \"https://github.com/raysan5/raylib.git\"\n    GIT_TAG \"5.5\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\n\nFetchContent_MakeAvailable(raylib)\n\nadd_executable(clay_examples_introducing_clay_video_demo main.c)\n\ntarget_compile_options(clay_examples_introducing_clay_video_demo PUBLIC)\ntarget_include_directories(clay_examples_introducing_clay_video_demo PUBLIC .)\n\ntarget_link_libraries(clay_examples_introducing_clay_video_demo PUBLIC raylib)\n\nif(MSVC)\n  set(CMAKE_C_FLAGS_DEBUG \"/D CLAY_DEBUG\")\nelse()\n  set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\n  set(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\nendif()\n\nadd_custom_command(\n        TARGET clay_examples_introducing_clay_video_demo POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy_directory\n        ${CMAKE_CURRENT_SOURCE_DIR}/resources\n        ${CMAKE_CURRENT_BINARY_DIR}/resources)\n"
  },
  {
    "path": "examples/introducing-clay-video-demo/main.c",
    "content": "#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n#include \"../../renderers/raylib/clay_renderer_raylib.c\"\n#include \"../shared-layouts/clay-video-demo.c\"\n\n// This function is new since the video was published\nvoid HandleClayErrors(Clay_ErrorData errorData) {\n    printf(\"%s\", errorData.errorText.chars);\n}\n\nint main(void) {\n    Clay_Raylib_Initialize(1024, 768, \"Introducing Clay Demo\", FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); // Extra parameters to this function are new since the video was published\n\n    uint64_t clayRequiredMemory = Clay_MinMemorySize();\n    Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(clayRequiredMemory, malloc(clayRequiredMemory));\n    Clay_Initialize(clayMemory, (Clay_Dimensions) {\n       .width = GetScreenWidth(),\n       .height = GetScreenHeight()\n    }, (Clay_ErrorHandler) { HandleClayErrors }); // This final argument is new since the video was published\n    Font fonts[1];\n    fonts[FONT_ID_BODY_16] = LoadFontEx(\"resources/Roboto-Regular.ttf\", 48, 0, 400);\n    SetTextureFilter(fonts[FONT_ID_BODY_16].texture, TEXTURE_FILTER_BILINEAR);\n    Clay_SetMeasureTextFunction(Raylib_MeasureText, fonts);\n\n    ClayVideoDemo_Data data = ClayVideoDemo_Initialize();\n\n    while (!WindowShouldClose()) {\n        // Run once per frame\n        Clay_SetLayoutDimensions((Clay_Dimensions) {\n            .width = GetScreenWidth(),\n            .height = GetScreenHeight()\n        });\n\n        Vector2 mousePosition = GetMousePosition();\n        Vector2 scrollDelta = GetMouseWheelMoveV();\n        Clay_SetPointerState(\n            (Clay_Vector2) { mousePosition.x, mousePosition.y },\n            IsMouseButtonDown(0)\n        );\n        Clay_UpdateScrollContainers(\n            true,\n            (Clay_Vector2) { scrollDelta.x, scrollDelta.y },\n            GetFrameTime()\n        );\n\n        Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&data);\n\n        BeginDrawing();\n        ClearBackground(BLACK);\n        Clay_Raylib_Render(renderCommands, fonts);\n        EndDrawing();\n    }\n    // This function is new since the video was published\n    Clay_Raylib_Close();\n}\n"
  },
  {
    "path": "examples/playdate-project-example/.gitignore",
    "content": "clay_playdate_example.pdx\nSource/pdex.dylib\nSource/pdex.elf\n"
  },
  {
    "path": "examples/playdate-project-example/CmakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nset(CMAKE_C_STANDARD 99)\n\nset(ENVSDK $ENV{PLAYDATE_SDK_PATH})\n\nif (NOT ${ENVSDK} STREQUAL \"\")\n        # Convert path from Windows\n        file(TO_CMAKE_PATH ${ENVSDK} SDK)\nelse()\n        execute_process(\n                        COMMAND bash -c \"egrep '^\\\\s*SDKRoot' $HOME/.Playdate/config\"\n                        COMMAND head -n 1\n                        COMMAND cut -c9-\n                        OUTPUT_VARIABLE SDK\n                        OUTPUT_STRIP_TRAILING_WHITESPACE\n        )\nendif()\n\nif (NOT EXISTS ${SDK})\n        message(FATAL_ERROR \"SDK Path not found; set ENV value PLAYDATE_SDK_PATH\")\n        return()\nendif()\n\nset(CMAKE_CONFIGURATION_TYPES \"Debug;Release\")\nset(CMAKE_XCODE_GENERATE_SCHEME TRUE)\n\n# Game Name Customization\nset(PLAYDATE_GAME_NAME clay_playdate_example)\nset(PLAYDATE_GAME_DEVICE clay_playdate_example_DEVICE)\n\nproject(${PLAYDATE_GAME_NAME} C ASM)\n\nif (TOOLCHAIN STREQUAL \"armgcc\")\n        add_executable(${PLAYDATE_GAME_DEVICE} main.c)\nelse()\n        add_library(${PLAYDATE_GAME_NAME} SHARED main.c)\nendif()\n\ninclude(${SDK}/C_API/buildsupport/playdate_game.cmake)\n\n"
  },
  {
    "path": "examples/playdate-project-example/README.md",
    "content": "# Playdate console example\n\nThis example uses a modified version of the document viewer application from the Clay video demo. The Playdate console has a very small black and white screen, so some of the fixed sizes and styles needed to be modified to make the application usable on the console. The selected document can be changed using up/down on the D-pad, and the selected document can be scrolled with the crank.\n\n## Building\n\nYou need to have the (Playdate SDK)[https://play.date/dev/] installed to be able to build this example. Once it's installed you can build it by adding -DCLAY_INCLUDE_PLAYDATE_EXAMPLES=ON when initialising a directory with cmake.\n\ne.g.\n\n```\ncmake -DCLAY_INCLUDE_PLAYDATE_EXAMPLES=ON cmake-build-debug\n```\n\nAnd then build it:\n\n```\ncmake --build cmake-build-debug\n```\n\nThe pdx file will be located at examples/playdate-project-example/clay_playdate_example.pdx. You can then open it with the Playdate simulator.\n\n## Building for the playdate device\n\nBy default building this example will produce a pdx which can only run on the Playdate simulator application. To build a pdx that can run on the Playdate hardware you need to set the toolchain to use armgcc, toolchain file to the arm.cmake provided in the playdate SDK and make sure to disable the other examples. The Playdate hardware requires threads to be disabled which is not compatible with some of the other examples.\n\ne.g. To setup the cmake-build-release directory for device builds:\n\n```\ncmake -DTOOLCHAIN=armgcc -DCMAKE_TOOLCHAIN_FILE=/Users/mattahj/Developer/PlaydateSDK/C_API/buildsupport/arm.cmake -DCLAY_INCLUDE_ALL_EXAMPLES=OFF -DCLAY_INCLUDE_PLAYDATE_EXAMPLES=ON -B cmake-build-release\n```\n\nAnd then build it:\n\n```\ncmake --build cmake-build-release\n```\n"
  },
  {
    "path": "examples/playdate-project-example/Source/pdxinfo",
    "content": "name=Clay Playdate Example\nauthor=Matthew Jennings\ndescription=A small demo of Clay running on the Playdate\nbundleID=dev.mattahj.clay_example\nimagePath=\n"
  },
  {
    "path": "examples/playdate-project-example/clay-video-demo-playdate.c",
    "content": "// This is the video demo with some adjustments so it works on the playdate\n// console The playdate screen is only 400x240 pixels and it can only display\n// black and white, so some fixed sizes and colours needed tweaking! The file\n// menu was also removed as it does not really make sense when there is no\n// pointer\n//\n// Note: The playdate console also does not support dynamic font sizes - fonts must be\n// created at a specific size with the pdc tool - so any font size set in the clay layout\n// will have no effect.\n#include \"pd_api.h\"\n#include \"../../clay.h\"\n#include <stdlib.h>\n\nconst int FONT_ID_BODY = 0;\nconst int FONT_ID_BUTTON = 1;\n\nClay_Color COLOR_WHITE = { 255, 255, 255, 255 };\nClay_Color COLOR_BLACK = { 0, 0, 0, 255 };\n\nvoid RenderHeaderButton(Clay_String text) {\n    CLAY_AUTO_ID({\n        .layout = { .padding = { 8, 8, 4, 4 } },\n        .backgroundColor = COLOR_BLACK,\n        .cornerRadius = CLAY_CORNER_RADIUS(4)\n    }) {\n        CLAY_TEXT(\n            text,\n            CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BUTTON, .textColor = COLOR_WHITE })\n        );\n    }\n}\n\ntypedef struct {\n    Clay_String title;\n    Clay_String contents;\n    LCDBitmap* image;\n} Document;\n\ntypedef struct {\n    Document *documents;\n    uint32_t length;\n} DocumentArray;\n\n#define MAX_DOCUMENTS 3\nDocument documentsRaw[MAX_DOCUMENTS];\n\nDocumentArray documents = { .length = MAX_DOCUMENTS, .documents = documentsRaw };\n\nvoid ClayVideoDemoPlaydate_Initialize(PlaydateAPI* pd) {\n    documents.documents[0] = (Document){\n        .title = CLAY_STRING(\"Squirrels\"),\n        .image = pd->graphics->loadBitmap(\"star.png\", NULL),\n        .contents = CLAY_STRING(\n            \"The Secret Life of Squirrels: Nature's Clever Acrobats\\n\"\n            \"Squirrels are often overlooked creatures, dismissed as mere park \"\n            \"inhabitants or backyard nuisances. Yet, beneath their fluffy tails \"\n            \"and twitching noses lies an intricate world of cunning, agility, \"\n            \"and survival tactics that are nothing short of fascinating. As one \"\n            \"of the most common mammals in North America, squirrels have adapted \"\n            \"to a wide range of environments from bustling urban centers to \"\n            \"tranquil forests and have developed a variety of unique behaviors \"\n            \"that continue to intrigue scientists and nature enthusiasts alike.\\n\"\n            \"\\n\"\n            \"Master Tree Climbers\\n\"\n            \"At the heart of a squirrel's skill set is its impressive ability to \"\n            \"navigate trees with ease. Whether they're darting from branch to \"\n            \"branch or leaping across wide gaps, squirrels possess an innate \"\n            \"talent for acrobatics. Their powerful hind legs, which are longer \"\n            \"than their front legs, give them remarkable jumping power. With a \"\n            \"tail that acts as a counterbalance, squirrels can leap distances of \"\n            \"up to ten times the length of their body, making them some of the \"\n            \"best aerial acrobats in the animal kingdom.\\n\"\n            \"But it's not just their agility that makes them exceptional \"\n            \"climbers. Squirrels' sharp, curved claws allow them to grip tree \"\n            \"bark with precision, while the soft pads on their feet provide \"\n            \"traction on slippery surfaces. Their ability to run at high speeds \"\n            \"and scale vertical trunks with ease is a testament to the \"\n            \"evolutionary adaptations that have made them so successful in their \"\n            \"arboreal habitats.\\n\"\n            \"\\n\"\n            \"Food Hoarders Extraordinaire\\n\"\n            \"Squirrels are often seen frantically gathering nuts, seeds, and \"\n            \"even fungi in preparation for winter. While this behavior may seem \"\n            \"like instinctual hoarding, it is actually a survival strategy that \"\n            \"has been honed over millions of years. Known as \\\"scatter \"\n            \"hoarding,\\\" squirrels store their food in a variety of hidden \"\n            \"locations, often burying it deep in the soil or stashing it in \"\n            \"hollowed-out tree trunks.\\n\"\n            \"Interestingly, squirrels have an incredible memory for the \"\n            \"locations of their caches. Research has shown that they can \"\n            \"remember thousands of hiding spots, often returning to them months \"\n            \"later when food is scarce. However, they don't always recover every \"\n            \"stash some forgotten caches eventually sprout into new trees, \"\n            \"contributing to forest regeneration. This unintentional role as \"\n            \"forest gardeners highlights the ecological importance of squirrels \"\n            \"in their ecosystems.\\n\"\n            \"\\n\"\n            \"The Great Squirrel Debate: Urban vs. Wild\\n\"\n            \"While squirrels are most commonly associated with rural or wooded \"\n            \"areas, their adaptability has allowed them to thrive in urban \"\n            \"environments as well. In cities, squirrels have become adept at \"\n            \"finding food sources in places like parks, streets, and even \"\n            \"garbage cans. However, their urban counterparts face unique \"\n            \"challenges, including traffic, predators, and the lack of natural \"\n            \"shelters. Despite these obstacles, squirrels in urban areas are \"\n            \"often observed using human infrastructure such as buildings, \"\n            \"bridges, and power lines as highways for their acrobatic \"\n            \"escapades.\\n\"\n            \"There is, however, a growing concern regarding the impact of urban \"\n            \"life on squirrel populations. Pollution, deforestation, and the \"\n            \"loss of natural habitats are making it more difficult for squirrels \"\n            \"to find adequate food and shelter. As a result, conservationists \"\n            \"are focusing on creating squirrel-friendly spaces within cities, \"\n            \"with the goal of ensuring these resourceful creatures continue to \"\n            \"thrive in both rural and urban landscapes.\\n\"\n            \"\\n\"\n            \"A Symbol of Resilience\\n\"\n            \"In many cultures, squirrels are symbols of resourcefulness, \"\n            \"adaptability, and preparation. Their ability to thrive in a variety \"\n            \"of environments while navigating challenges with agility and grace \"\n            \"serves as a reminder of the resilience inherent in nature. Whether \"\n            \"you encounter them in a quiet forest, a city park, or your own \"\n            \"backyard, squirrels are creatures that never fail to amaze with \"\n            \"their endless energy and ingenuity.\\n\"\n            \"In the end, squirrels may be small, but they are mighty in their \"\n            \"ability to survive and thrive in a world that is constantly \"\n            \"changing. So next time you spot one hopping across a branch or \"\n            \"darting across your lawn, take a moment to appreciate the \"\n            \"remarkable acrobat at work a true marvel of the natural world.\\n\"\n        )\n    };\n    documents.documents[1] = (Document){\n        .title = CLAY_STRING(\"Lorem Ipsum\"),\n        .image = pd->graphics->loadBitmap(\"star.png\", NULL),\n        .contents = CLAY_STRING(\n            \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do \"\n            \"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim \"\n            \"ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut \"\n            \"aliquip ex ea commodo consequat. Duis aute irure dolor in \"\n            \"reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \"\n            \"pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \"\n            \"culpa qui officia deserunt mollit anim id est laborum.\"\n        )\n    };\n    documents.documents[2] = (Document){\n        .title = CLAY_STRING(\"Vacuum Instructions\"),\n        .image = pd->graphics->loadBitmap(\"star.png\", NULL),\n        .contents = CLAY_STRING(\n            \"Chapter 3: Getting Started - Unpacking and Setup\\n\"\n            \"\\n\"\n            \"Congratulations on your new SuperClean Pro 5000 vacuum cleaner! In \"\n            \"this section, we will guide you through the simple steps to get \"\n            \"your vacuum up and running. Before you begin, please ensure that \"\n            \"you have all the components listed in the \\\"Package Contents\\\" \"\n            \"section on page 2.\\n\"\n            \"\\n\"\n            \"1. Unboxing Your Vacuum\\n\"\n            \"Carefully remove the vacuum cleaner from the box. Avoid using sharp \"\n            \"objects that could damage the product. Once removed, place the unit \"\n            \"on a flat, stable surface to proceed with the setup. Inside the \"\n            \"box, you should find:\\n\"\n            \"\\n\"\n            \"    The main vacuum unit\\n\"\n            \"    A telescoping extension wand\\n\"\n            \"    A set of specialized cleaning tools (crevice tool, upholstery \"\n            \"brush, etc.)\\n\"\n            \"    A reusable dust bag (if applicable)\\n\"\n            \"    A power cord with a 3-prong plug\\n\"\n            \"    A set of quick-start instructions\\n\"\n            \"\\n\"\n            \"2. Assembling Your Vacuum\\n\"\n            \"Begin by attaching the extension wand to the main body of the \"\n            \"vacuum cleaner. Line up the connectors and twist the wand into \"\n            \"place until you hear a click. Next, select the desired cleaning \"\n            \"tool and firmly attach it to the wand's end, ensuring it is \"\n            \"securely locked in.\\n\"\n            \"\\n\"\n            \"For models that require a dust bag, slide the bag into the \"\n            \"compartment at the back of the vacuum, making sure it is properly \"\n            \"aligned with the internal mechanism. If your vacuum uses a bagless \"\n            \"system, ensure the dust container is correctly seated and locked in \"\n            \"place before use.\\n\"\n            \"\\n\"\n            \"3. Powering On\\n\"\n            \"To start the vacuum, plug the power cord into a grounded electrical \"\n            \"outlet. Once plugged in, locate the power switch, usually \"\n            \"positioned on the side of the handle or body of the unit, depending \"\n            \"on your model. Press the switch to the \\\"On\\\" position, and you \"\n            \"should hear the motor begin to hum. If the vacuum does not power \"\n            \"on, check that the power cord is securely plugged in, and ensure \"\n            \"there are no blockages in the power switch.\\n\"\n            \"\\n\"\n            \"Note: Before first use, ensure that the vacuum filter (if your \"\n            \"model has one) is properly installed. If unsure, refer to \\\"Section \"\n            \"5: Maintenance\\\" for filter installation instructions.\"\n        )\n    };\n}\n\nClay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentIndex) {\n\n    Clay_BeginLayout();\n\n    Clay_Sizing layoutExpand = {\n        .width = CLAY_SIZING_GROW(0),\n        .height = CLAY_SIZING_GROW(0)\n    };\n\n    Clay_BorderElementConfig contentBorders = {\n        .color = COLOR_BLACK,\n        .width = { .top = 1, .left = 1, .right = 1, .bottom = 1 }\n    };\n\n    // Build UI here\n    CLAY(CLAY_ID(\"OuterContainer\"), {\n        .backgroundColor = COLOR_WHITE,\n        .layout = {\n            .layoutDirection = CLAY_TOP_TO_BOTTOM,\n            .sizing = layoutExpand,\n            .padding = CLAY_PADDING_ALL(8),\n            .childGap = 4\n         }\n    }) {\n        // Child elements go inside braces\n        CLAY(CLAY_ID(\"HeaderBar\"), {\n            .layout = {\n                .sizing = {\n                    .height = CLAY_SIZING_FIXED(30),\n                    .width = CLAY_SIZING_GROW(0)\n                },\n                .childGap = 8,\n                .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }\n            },\n        }) {\n            // Header buttons go here\n            CLAY(CLAY_ID(\"FileButton\"), {\n                .layout = {\n                    .padding = { 8, 8, 4, 4 }\n                },\n                .backgroundColor = COLOR_BLACK,\n                .cornerRadius = CLAY_CORNER_RADIUS(4)\n            }) {\n                CLAY_TEXT(\n                    CLAY_STRING(\"File\"),\n                    CLAY_TEXT_CONFIG({\n                        .fontId = FONT_ID_BUTTON,\n                        .textColor = COLOR_WHITE\n                    })\n                );\n            }\n            RenderHeaderButton(CLAY_STRING(\"Edit\"));\n            CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0) } } }) {}\n            RenderHeaderButton(CLAY_STRING(\"Upload\"));\n            RenderHeaderButton(CLAY_STRING(\"Media\"));\n            RenderHeaderButton(CLAY_STRING(\"Support\"));\n        }\n\n        CLAY(CLAY_ID(\"LowerContent\"), {\n            .layout = { .sizing = layoutExpand, .childGap = 8 },\n        }) {\n            CLAY(CLAY_ID(\"Sidebar\"), {\n                .border = contentBorders,\n                .cornerRadius = CLAY_CORNER_RADIUS(4),\n                .layout = {\n                    .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                    .padding = CLAY_PADDING_ALL(8),\n                    .childGap = 4,\n                    .sizing = {\n                        .width = CLAY_SIZING_FIXED(125),\n                        .height = CLAY_SIZING_GROW(0)\n                    }\n                }\n            }) {\n                for (int i = 0; i < documents.length; i++) {\n                    Document document = documents.documents[i];\n                    Clay_LayoutConfig sidebarButtonLayout = {\n                        .sizing = { .width = CLAY_SIZING_GROW(0) },\n                        .padding = CLAY_PADDING_ALL(8)\n                    };\n\n                    if (i == selectedDocumentIndex) {\n                        CLAY_AUTO_ID({\n                            .layout = sidebarButtonLayout,\n                            .backgroundColor = COLOR_BLACK,\n                            .cornerRadius = CLAY_CORNER_RADIUS(4)\n                        }) {\n                            CLAY_TEXT(\n                                document.title,\n                                CLAY_TEXT_CONFIG({\n                                    .fontId = FONT_ID_BUTTON,\n                                    .textColor = COLOR_WHITE\n                                })\n                            );\n                        }\n                    } else {\n                        CLAY_AUTO_ID({\n                            .layout = sidebarButtonLayout,\n                            .backgroundColor = (Clay_Color){ 0, 0, 0, Clay_Hovered() ? 120 : 0 },\n                            .cornerRadius = CLAY_CORNER_RADIUS(4),\n                            .border = contentBorders\n                        }) {\n                            CLAY_TEXT(\n                                document.title,\n                                CLAY_TEXT_CONFIG({\n                                    .fontId = FONT_ID_BUTTON,\n                                    .textColor = COLOR_BLACK,\n                                })\n                            );\n                        }\n                    }\n                }\n            }\n\n            CLAY(CLAY_ID(\"MainContent\"), {\n                .border = contentBorders,\n                .cornerRadius = CLAY_CORNER_RADIUS(4),\n                .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },\n                .layout = {\n                    .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                    .childGap = 8,\n                    .padding = CLAY_PADDING_ALL(8),\n                    .sizing = layoutExpand\n                }\n            }) {\n                Document selectedDocument = documents.documents[selectedDocumentIndex];\n                CLAY_AUTO_ID({\n                    .layout = {\n                        .layoutDirection = CLAY_LEFT_TO_RIGHT,\n                        .childGap = 4,\n                        .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_BOTTOM }\n                    }\n                }) {\n                    CLAY_TEXT(\n                        selectedDocument.title,\n                        CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY, .textColor = COLOR_BLACK })\n                    );\n                    CLAY_AUTO_ID({\n                        .layout = {\n                            .sizing = {\n                                .width = CLAY_SIZING_FIXED(32),\n                                .height = CLAY_SIZING_FIXED(30)\n                            }\n                        },\n                        .image = { .imageData = selectedDocument.image, .sourceDimensions = { 32, 30 } }\n                    }) {}\n                }\n                CLAY_TEXT(\n                    selectedDocument.contents,\n                    CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY, .textColor = COLOR_BLACK })\n                );\n            }\n        }\n    }\n\n    Clay_RenderCommandArray renderCommands = Clay_EndLayout();\n    for (int32_t i = 0; i < renderCommands.length; i++) {\n        Clay_RenderCommandArray_Get(&renderCommands, i);\n    }\n    return renderCommands;\n}\n"
  },
  {
    "path": "examples/playdate-project-example/main.c",
    "content": "#include \"pd_api.h\"\n#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n\n#include \"../../renderers/playdate/clay_renderer_playdate.c\"\n#include \"clay-video-demo-playdate.c\"\n\nstatic int update(void *userdata);\n\n#define NUM_FONTS 2\nconst char *fontsToLoad[NUM_FONTS] = {\n    \"/System/Fonts/Asheville-Sans-14-Bold.pft\",\n    \"/System/Fonts/Roobert-10-Bold.pft\"\n};\n\nvoid HandleClayErrors(Clay_ErrorData errorData) {}\n\nstruct TextUserData {\n    LCDFont *font[NUM_FONTS];\n    PlaydateAPI *pd;\n};\n\nstatic struct TextUserData textUserData = { .font = { NULL }, .pd = NULL };\n\nstatic Clay_Dimensions PlayDate_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {\n    struct TextUserData *textUserData = userData;\n    int width = textUserData->pd->graphics->getTextWidth(\n        textUserData->font[config->fontId],\n        text.chars,\n        Clay_Playdate_CountUtf8Codepoints(text.chars, text.length),\n        kUTF8Encoding,\n        0\n    );\n    int height = textUserData->pd->graphics->getFontHeight(textUserData->font[config->fontId]);\n    return (Clay_Dimensions){\n        .width = (float)width,\n        .height = (float)height,\n    };\n}\n\n#ifdef _WINDLL\n__declspec(dllexport)\n#endif\nint eventHandler(PlaydateAPI* pd, PDSystemEvent event, uint32_t eventArg) {\n    if (event == kEventInit) {\n        const char *err;\n        for (int i = 0; i < NUM_FONTS; ++i) {\n\n            textUserData.font[i] = pd->graphics->loadFont(fontsToLoad[i], &err);\n            if (textUserData.font[i] == NULL) {\n                pd->system->error(\"%s:%i Couldn't load font %s: %s\", __FILE__, __LINE__, fontsToLoad[i], err);\n            }\n        }\n\n        textUserData.pd = pd;\n        pd->system->setUpdateCallback(update, pd);\n\n        uint64_t totalMemorySize = Clay_MinMemorySize();\n        Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(\n            totalMemorySize,\n            pd->system->realloc(NULL, totalMemorySize)\n        );\n        Clay_Initialize(\n            clayMemory,\n            (Clay_Dimensions){\n                (float)pd->display->getWidth(),\n                (float)pd->display->getHeight()\n            },\n            (Clay_ErrorHandler){HandleClayErrors}\n        );\n        Clay_SetMeasureTextFunction(PlayDate_MeasureText, &textUserData);\n        ClayVideoDemoPlaydate_Initialize(pd);\n    }\n\n    return 0;\n}\n\nint selectedDocumentIndex = 0;\n#define WRAP_RANGE(x, N) ((((x) % (N)) + (N)) % (N))\n\nstatic int update(void *userdata) {\n    PlaydateAPI *pd = userdata;\n    PDButtons pushedButtons;\n    pd->system->getButtonState(NULL, &pushedButtons, NULL);\n\n    if (pushedButtons & kButtonDown) {\n        selectedDocumentIndex = WRAP_RANGE(selectedDocumentIndex + 1, MAX_DOCUMENTS);\n    } else if (pushedButtons & kButtonUp) {\n        selectedDocumentIndex = WRAP_RANGE(selectedDocumentIndex - 1, MAX_DOCUMENTS);\n    }\n\n    pd->graphics->clear(kColorWhite);\n\n    // A bit hacky, setting the cursor on to the document view so it can be\n    // scrolled..\n    Clay_SetPointerState(\n        (Clay_Vector2){\n            .x = pd->display->getWidth() / 2.0f,\n            .y = pd->display->getHeight() / 2.0f\n        },\n        false\n    );\n\n    float crankDelta = pd->system->getCrankChange();\n    Clay_UpdateScrollContainers(\n        false,\n        (Clay_Vector2){ 0, -crankDelta * 0.25f },\n        pd->system->getElapsedTime()\n    );\n\n    Clay_RenderCommandArray renderCommands = ClayVideoDemoPlaydate_CreateLayout(selectedDocumentIndex);\n    Clay_Playdate_Render(pd, renderCommands, textUserData.font);\n\n    return 1;\n}\n"
  },
  {
    "path": "examples/raylib-multi-context/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(clay_examples_raylib_multi_context C)\nset(CMAKE_C_STANDARD 99)\n\n# Adding Raylib\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\nset(BUILD_EXAMPLES OFF CACHE BOOL \"\" FORCE) # don't build the supplied examples\nset(BUILD_GAMES    OFF CACHE BOOL \"\" FORCE) # don't build the supplied example games\n\nFetchContent_Declare(\n    raylib\n    GIT_REPOSITORY \"https://github.com/raysan5/raylib.git\"\n    GIT_TAG \"5.5\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\n\nFetchContent_MakeAvailable(raylib)\n\nadd_executable(clay_examples_raylib_multi_context main.c)\n\ntarget_compile_options(clay_examples_raylib_multi_context PUBLIC)\ntarget_include_directories(clay_examples_raylib_multi_context PUBLIC .)\n\ntarget_link_libraries(clay_examples_raylib_multi_context PUBLIC raylib)\n\nset(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\nset(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\n\nadd_custom_command(\n        TARGET clay_examples_raylib_multi_context POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy_directory\n        ${CMAKE_CURRENT_SOURCE_DIR}/resources\n        ${CMAKE_CURRENT_BINARY_DIR}/resources)\n"
  },
  {
    "path": "examples/raylib-multi-context/main.c",
    "content": "#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n#include \"../../renderers/raylib/clay_renderer_raylib.c\"\n#include \"../shared-layouts/clay-video-demo.c\"\n\nvoid HandleClayErrors(Clay_ErrorData errorData) {\n    printf(\"%s\", errorData.errorText.chars);\n}\n\nClay_RenderCommandArray CreateLayout(Clay_Context* context, ClayVideoDemo_Data *data) {\n    Clay_SetCurrentContext(context);\n    Clay_SetDebugModeEnabled(true);\n    // Run once per frame\n    Clay_SetLayoutDimensions((Clay_Dimensions) {\n            .width = GetScreenWidth(),\n            .height = GetScreenHeight() / 2\n    });\n    Vector2 mousePosition = GetMousePosition();\n    mousePosition.y -= data->yOffset;\n    Vector2 scrollDelta = GetMouseWheelMoveV();\n    Clay_SetPointerState(\n            (Clay_Vector2) { mousePosition.x, mousePosition.y },\n            IsMouseButtonDown(0)\n    );\n    Clay_UpdateScrollContainers(\n            true,\n            (Clay_Vector2) { scrollDelta.x, scrollDelta.y },\n            GetFrameTime()\n    );\n    return ClayVideoDemo_CreateLayout(data);\n}\n\nint main(void) {\n    documents.documents = (Document[]) {\n            { .title = CLAY_STRING(\"Squirrels\"), .contents = CLAY_STRING(\"The Secret Life of Squirrels: Nature's Clever Acrobats\\n\"\"Squirrels are often overlooked creatures, dismissed as mere park inhabitants or backyard nuisances. Yet, beneath their fluffy tails and twitching noses lies an intricate world of cunning, agility, and survival tactics that are nothing short of fascinating. As one of the most common mammals in North America, squirrels have adapted to a wide range of environments from bustling urban centers to tranquil forests and have developed a variety of unique behaviors that continue to intrigue scientists and nature enthusiasts alike.\\n\"\"\\n\"\"Master Tree Climbers\\n\"\"At the heart of a squirrel's skill set is its impressive ability to navigate trees with ease. Whether they're darting from branch to branch or leaping across wide gaps, squirrels possess an innate talent for acrobatics. Their powerful hind legs, which are longer than their front legs, give them remarkable jumping power. With a tail that acts as a counterbalance, squirrels can leap distances of up to ten times the length of their body, making them some of the best aerial acrobats in the animal kingdom.\\n\"\"But it's not just their agility that makes them exceptional climbers. Squirrels' sharp, curved claws allow them to grip tree bark with precision, while the soft pads on their feet provide traction on slippery surfaces. Their ability to run at high speeds and scale vertical trunks with ease is a testament to the evolutionary adaptations that have made them so successful in their arboreal habitats.\\n\"\"\\n\"\"Food Hoarders Extraordinaire\\n\"\"Squirrels are often seen frantically gathering nuts, seeds, and even fungi in preparation for winter. While this behavior may seem like instinctual hoarding, it is actually a survival strategy that has been honed over millions of years. Known as \\\"scatter hoarding,\\\" squirrels store their food in a variety of hidden locations, often burying it deep in the soil or stashing it in hollowed-out tree trunks.\\n\"\"Interestingly, squirrels have an incredible memory for the locations of their caches. Research has shown that they can remember thousands of hiding spots, often returning to them months later when food is scarce. However, they don't always recover every stash some forgotten caches eventually sprout into new trees, contributing to forest regeneration. This unintentional role as forest gardeners highlights the ecological importance of squirrels in their ecosystems.\\n\"\"\\n\"\"The Great Squirrel Debate: Urban vs. Wild\\n\"\"While squirrels are most commonly associated with rural or wooded areas, their adaptability has allowed them to thrive in urban environments as well. In cities, squirrels have become adept at finding food sources in places like parks, streets, and even garbage cans. However, their urban counterparts face unique challenges, including traffic, predators, and the lack of natural shelters. Despite these obstacles, squirrels in urban areas are often observed using human infrastructure such as buildings, bridges, and power lines as highways for their acrobatic escapades.\\n\"\"There is, however, a growing concern regarding the impact of urban life on squirrel populations. Pollution, deforestation, and the loss of natural habitats are making it more difficult for squirrels to find adequate food and shelter. As a result, conservationists are focusing on creating squirrel-friendly spaces within cities, with the goal of ensuring these resourceful creatures continue to thrive in both rural and urban landscapes.\\n\"\"\\n\"\"A Symbol of Resilience\\n\"\"In many cultures, squirrels are symbols of resourcefulness, adaptability, and preparation. Their ability to thrive in a variety of environments while navigating challenges with agility and grace serves as a reminder of the resilience inherent in nature. Whether you encounter them in a quiet forest, a city park, or your own backyard, squirrels are creatures that never fail to amaze with their endless energy and ingenuity.\\n\"\"In the end, squirrels may be small, but they are mighty in their ability to survive and thrive in a world that is constantly changing. So next time you spot one hopping across a branch or darting across your lawn, take a moment to appreciate the remarkable acrobat at work a true marvel of the natural world.\\n\") },\n            { .title = CLAY_STRING(\"Lorem Ipsum\"), .contents = CLAY_STRING(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\") },\n            { .title = CLAY_STRING(\"Vacuum Instructions\"), .contents = CLAY_STRING(\"Chapter 3: Getting Started - Unpacking and Setup\\n\"\"\\n\"\"Congratulations on your new SuperClean Pro 5000 vacuum cleaner! In this section, we will guide you through the simple steps to get your vacuum up and running. Before you begin, please ensure that you have all the components listed in the \\\"Package Contents\\\" section on page 2.\\n\"\"\\n\"\"1. Unboxing Your Vacuum\\n\"\"Carefully remove the vacuum cleaner from the box. Avoid using sharp objects that could damage the product. Once removed, place the unit on a flat, stable surface to proceed with the setup. Inside the box, you should find:\\n\"\"\\n\"\"    The main vacuum unit\\n\"\"    A telescoping extension wand\\n\"\"    A set of specialized cleaning tools (crevice tool, upholstery brush, etc.)\\n\"\"    A reusable dust bag (if applicable)\\n\"\"    A power cord with a 3-prong plug\\n\"\"    A set of quick-start instructions\\n\"\"\\n\"\"2. Assembling Your Vacuum\\n\"\"Begin by attaching the extension wand to the main body of the vacuum cleaner. Line up the connectors and twist the wand into place until you hear a click. Next, select the desired cleaning tool and firmly attach it to the wand's end, ensuring it is securely locked in.\\n\"\"\\n\"\"For models that require a dust bag, slide the bag into the compartment at the back of the vacuum, making sure it is properly aligned with the internal mechanism. If your vacuum uses a bagless system, ensure the dust container is correctly seated and locked in place before use.\\n\"\"\\n\"\"3. Powering On\\n\"\"To start the vacuum, plug the power cord into a grounded electrical outlet. Once plugged in, locate the power switch, usually positioned on the side of the handle or body of the unit, depending on your model. Press the switch to the \\\"On\\\" position, and you should hear the motor begin to hum. If the vacuum does not power on, check that the power cord is securely plugged in, and ensure there are no blockages in the power switch.\\n\"\"\\n\"\"Note: Before first use, ensure that the vacuum filter (if your model has one) is properly installed. If unsure, refer to \\\"Section 5: Maintenance\\\" for filter installation instructions.\") },\n            { .title = CLAY_STRING(\"Article 4\"), .contents = CLAY_STRING(\"Article 4\") },\n            { .title = CLAY_STRING(\"Article 5\"), .contents = CLAY_STRING(\"Article 5\") },\n    };\n    Clay_Raylib_Initialize(1024, 768, \"Introducing Clay Demo\", FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); // Extra parameters to this function are new since the video was published\n\n    Font fonts[1];\n    fonts[FONT_ID_BODY_16] = LoadFontEx(\"resources/Roboto-Regular.ttf\", 48, 0, 400);\n    SetTextureFilter(fonts[FONT_ID_BODY_16].texture, TEXTURE_FILTER_BILINEAR);\n\n    uint64_t clayRequiredMemory = Clay_MinMemorySize();\n\n    Clay_Arena clayMemoryTop = Clay_CreateArenaWithCapacityAndMemory(clayRequiredMemory, malloc(clayRequiredMemory));\n    Clay_Context *clayContextTop = Clay_Initialize(clayMemoryTop, (Clay_Dimensions) {\n       .width = GetScreenWidth(),\n       .height = GetScreenHeight() / 2\n    }, (Clay_ErrorHandler) { HandleClayErrors }); // This final argument is new since the video was published\n    ClayVideoDemo_Data dataTop = ClayVideoDemo_Initialize();\n    Clay_SetMeasureTextFunction(Raylib_MeasureText, fonts);\n\n    Clay_Arena clayMemoryBottom = Clay_CreateArenaWithCapacityAndMemory(clayRequiredMemory, malloc(clayRequiredMemory));\n    Clay_Context *clayContextBottom = Clay_Initialize(clayMemoryBottom, (Clay_Dimensions) {\n            .width = GetScreenWidth(),\n            .height = GetScreenHeight() / 2\n    }, (Clay_ErrorHandler) { HandleClayErrors }); // This final argument is new since the video was published\n    ClayVideoDemo_Data dataBottom = ClayVideoDemo_Initialize();\n    Clay_SetMeasureTextFunction(Raylib_MeasureText, fonts);\n\n    while (!WindowShouldClose()) {\n        dataBottom.yOffset = GetScreenHeight() / 2;\n        Clay_RenderCommandArray renderCommandsTop = CreateLayout(clayContextTop, &dataTop);\n        Clay_RenderCommandArray renderCommandsBottom = CreateLayout(clayContextBottom, &dataBottom);\n        BeginDrawing();\n        ClearBackground(BLACK);\n        Clay_Raylib_Render(renderCommandsTop, fonts);\n        Clay_Raylib_Render(renderCommandsBottom, fonts);\n        EndDrawing();\n    }\n\n    Clay_Raylib_Close();\n}\n"
  },
  {
    "path": "examples/raylib-sidebar-scrolling-container/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(clay_examples_raylib_sidebar_scrolling_container C)\nset(CMAKE_C_STANDARD 99)\n\n# Adding Raylib\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\nset(BUILD_EXAMPLES OFF CACHE BOOL \"\" FORCE) # don't build the supplied examples\nset(BUILD_GAMES    OFF CACHE BOOL \"\" FORCE) # don't build the supplied example games\n\nFetchContent_Declare(\n    raylib\n    GIT_REPOSITORY \"https://github.com/raysan5/raylib.git\"\n    GIT_TAG \"5.5\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\n\nFetchContent_MakeAvailable(raylib)\n\nadd_executable(clay_examples_raylib_sidebar_scrolling_container main.c multi-compilation-unit.c)\n\ntarget_compile_options(clay_examples_raylib_sidebar_scrolling_container PUBLIC)\ntarget_include_directories(clay_examples_raylib_sidebar_scrolling_container PUBLIC .)\n\ntarget_link_libraries(clay_examples_raylib_sidebar_scrolling_container PUBLIC raylib)\nif(MSVC)\n  set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\nelse()\n  set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\n  set(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\nendif()\n\nadd_custom_command(\n        TARGET clay_examples_raylib_sidebar_scrolling_container POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy_directory\n        ${CMAKE_CURRENT_SOURCE_DIR}/resources\n        ${CMAKE_CURRENT_BINARY_DIR}/resources)\n"
  },
  {
    "path": "examples/raylib-sidebar-scrolling-container/main.c",
    "content": "#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n#include \"../../renderers/raylib/clay_renderer_raylib.c\"\n\nconst uint32_t FONT_ID_BODY_24 = 0;\nconst uint32_t FONT_ID_BODY_16 = 1;\n#define COLOR_ORANGE (Clay_Color) {225, 138, 50, 255}\n#define COLOR_BLUE (Clay_Color) {111, 173, 162, 255}\n\nTexture2D profilePicture;\n#define RAYLIB_VECTOR2_TO_CLAY_VECTOR2(vector) (Clay_Vector2) { .x = vector.x, .y = vector.y }\n\nClay_String profileText = CLAY_STRING_CONST(\"Profile Page one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen\");\nClay_TextElementConfig headerTextConfig = { .fontId = 1, .letterSpacing = 5, .fontSize = 16, .textColor = {0,0,0,255} };\n\nvoid HandleHeaderButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerData, intptr_t userData) {\n    if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n        // Do some click handling\n    }\n}\n\nClay_ElementDeclaration HeaderButtonStyle(bool hovered) {\n    return (Clay_ElementDeclaration) {\n        .layout = {.padding = {16, 16, 8, 8}},\n        .backgroundColor = hovered ? COLOR_ORANGE : COLOR_BLUE,\n    };\n}\n\n// Examples of re-usable \"Components\"\nvoid RenderHeaderButton(Clay_String text) {\n    CLAY_AUTO_ID(HeaderButtonStyle(Clay_Hovered())) {\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG(headerTextConfig));\n    }\n}\n\nClay_LayoutConfig dropdownTextItemLayout = { .padding = {8, 8, 4, 4} };\nClay_TextElementConfig dropdownTextElementConfig = { .fontSize = 24, .textColor = {255,255,255,255} };\n\nvoid RenderDropdownTextItem(int index) {\n    CLAY_AUTO_ID({ .layout = dropdownTextItemLayout, .backgroundColor = {180, 180, 180, 255} }) {\n        CLAY_TEXT(CLAY_STRING(\"I'm a text field in a scroll container.\"), &dropdownTextElementConfig);\n    }\n}\n\nClay_RenderCommandArray CreateLayout(void) {\n    Clay_BeginLayout();\n    CLAY(CLAY_ID(\"OuterContainer\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }, .padding = { 16, 16, 16, 16 }, .childGap = 16 }, .backgroundColor = {200, 200, 200, 255} }) {\n        CLAY(CLAY_ID(\"SideBar\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) }, .padding = {16, 16, 16, 16 }, .childGap = 16 }, .backgroundColor = {150, 150, 255, 255} }) {\n            CLAY(CLAY_ID(\"ProfilePictureOuter\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = { 8, 8, 8, 8 }, .childGap = 8, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .backgroundColor = {130, 130, 255, 255} }) {\n                CLAY(CLAY_ID(\"ProfilePicture\"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) } }, .image = { .imageData = &profilePicture }}) {}\n                CLAY_TEXT(profileText, CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {0, 0, 0, 255}, .textAlignment = CLAY_TEXT_ALIGN_RIGHT }));\n            }\n            CLAY(CLAY_ID(\"SidebarBlob1\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50) }}, .backgroundColor = {110, 110, 255, 255} }) {}\n            CLAY(CLAY_ID(\"SidebarBlob2\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50) }}, .backgroundColor = {110, 110, 255, 255} }) {}\n            CLAY(CLAY_ID(\"SidebarBlob3\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50) }}, .backgroundColor = {110, 110, 255, 255} }) {}\n            CLAY(CLAY_ID(\"SidebarBlob4\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50) }}, .backgroundColor = {110, 110, 255, 255} }) {}\n        }\n\n        CLAY(CLAY_ID(\"RightPanel\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }, .childGap = 16 }}) {\n            CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .childAlignment = { .x = CLAY_ALIGN_X_RIGHT }, .padding = {8, 8, 8, 8 }, .childGap = 8 }, .backgroundColor =  {180, 180, 180, 255} }) {\n                RenderHeaderButton(CLAY_STRING(\"Header Item 1\"));\n                RenderHeaderButton(CLAY_STRING(\"Header Item 2\"));\n                RenderHeaderButton(CLAY_STRING(\"Header Item 3\"));\n            }\n            CLAY(CLAY_ID(\"MainContent\"), {\n                .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {16, 16, 16, 16}, .childGap = 16, .sizing = { .width = CLAY_SIZING_GROW(0) } },\n                .backgroundColor = {200, 200, 255, 255},\n                .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },\n            })\n            {\n                 CLAY(CLAY_ID(\"FloatingContainer\"), {\n                     .layout = { .sizing = { .width = CLAY_SIZING_PERCENT(0.5), .height = CLAY_SIZING_FIXED(300) }, .padding = { 16, 16, 16, 16 }},\n                     .backgroundColor = { 140, 80, 200, 200 },\n                     .floating = { .attachTo = CLAY_ATTACH_TO_PARENT, .zIndex = 1, .attachPoints = { CLAY_ATTACH_POINT_CENTER_TOP, CLAY_ATTACH_POINT_CENTER_TOP }, .offset = {0, 0} },\n                     .border = { .width = CLAY_BORDER_OUTSIDE(2), .color = {80, 80, 80, 255} },\n                 }) {\n                     CLAY_TEXT(CLAY_STRING(\"I'm an inline floating container.\"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255,255,255,255} }));\n                 }\n\n                 CLAY_TEXT(CLAY_STRING(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.\"),\n                     CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {0,0,0,255} }));\n\n                 CLAY(CLAY_ID(\"Photos2\"), { .layout = { .childGap = 16, .padding = { 16, 16, 16, 16 }}, .backgroundColor = {180, 180, 220, Clay_Hovered() ? 120 : 255} }) {\n                     CLAY(CLAY_ID(\"Picture4\"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }}, .image = { .imageData = &profilePicture }}) {}\n                     CLAY(CLAY_ID(\"Picture5\"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }}, .image = { .imageData = &profilePicture }}) {}\n                     CLAY(CLAY_ID(\"Picture6\"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }}, .image = { .imageData = &profilePicture }}) {}\n                 }\n\n                 CLAY_TEXT(CLAY_STRING(\"Faucibus purus in massa tempor nec. Nec ullamcorper sit amet risus nullam eget felis eget nunc. Diam vulputate ut pharetra sit amet aliquam id diam. Lacus suspendisse faucibus interdum posuere lorem. A diam sollicitudin tempor id. Amet massa vitae tortor condimentum lacinia. Aliquet nibh praesent tristique magna.\"),\n                           CLAY_TEXT_CONFIG({ .fontSize = 24, .lineHeight = 60, .textColor = {0,0,0,255}, .textAlignment = CLAY_TEXT_ALIGN_CENTER }));\n\n                 CLAY_TEXT(CLAY_STRING(\"Suspendisse in est ante in nibh. Amet venenatis urna cursus eget nunc scelerisque viverra. Elementum sagittis vitae et leo duis ut diam quam nulla. Enim nulla aliquet porttitor lacus. Pellentesque habitant morbi tristique senectus et. Facilisi nullam vehicula ipsum a arcu cursus vitae.\\nSem fringilla ut morbi tincidunt. Euismod quis viverra nibh cras pulvinar mattis nunc sed. Velit sed ullamcorper morbi tincidunt ornare massa. Varius quam quisque id diam vel quam. Nulla pellentesque dignissim enim sit amet venenatis. Enim lobortis scelerisque fermentum dui faucibus in. Pretium viverra suspendisse potenti nullam ac tortor vitae. Lectus vestibulum mattis ullamcorper velit sed. Eget mauris pharetra et ultrices neque ornare aenean euismod elementum. Habitant morbi tristique senectus et. Integer vitae justo eget magna fermentum iaculis eu. Semper quis lectus nulla at volutpat diam. Enim praesent elementum facilisis leo. Massa vitae tortor condimentum lacinia quis vel.\"),\n                     CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {0,0,0,255} }));\n\n                 CLAY(CLAY_ID(\"Photos\"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, .childGap = 16, .padding = {16, 16, 16, 16} }, .backgroundColor = {180, 180, 220, 255} }) {\n                     CLAY(CLAY_ID(\"Picture2\"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(120) }}, .aspectRatio = 1, .image = { .imageData = &profilePicture }}) {}\n                     CLAY(CLAY_ID(\"Picture1\"), { .layout = { .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8, 8, 8} }, .backgroundColor = {170, 170, 220, 255} }) {\n                         CLAY(CLAY_ID(\"ProfilePicture2\"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}, .image = { .imageData = &profilePicture }}) {}\n                         CLAY_TEXT(CLAY_STRING(\"Image caption below\"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {0,0,0,255} }));\n                     }\n                     CLAY(CLAY_ID(\"Picture3\"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(120) }}, .aspectRatio = 1, .image = { .imageData = &profilePicture }}) {}\n                 }\n\n                 CLAY_TEXT(CLAY_STRING(\"Amet cursus sit amet dictum sit amet justo donec. Et malesuada fames ac turpis egestas maecenas. A lacus vestibulum sed arcu non odio euismod lacinia. Gravida neque convallis a cras. Dui nunc mattis enim ut tellus elementum sagittis vitae et. Orci sagittis eu volutpat odio facilisis mauris. Neque gravida in fermentum et sollicitudin ac orci. Ultrices dui sapien eget mi proin sed libero. Euismod quis viverra nibh cras pulvinar mattis. Diam volutpat commodo sed egestas egestas. In fermentum posuere urna nec tincidunt praesent semper. Integer eget aliquet nibh praesent tristique magna.\\nId cursus metus aliquam eleifend mi in. Sed pulvinar proin gravida hendrerit lectus a. Etiam tempor orci eu lobortis elementum nibh tellus. Nullam vehicula ipsum a arcu cursus vitae. Elit scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Condimentum lacinia quis vel eros donec ac odio. Mattis pellentesque id nibh tortor id aliquet lectus. Turpis egestas integer eget aliquet nibh praesent tristique. Porttitor massa id neque aliquam vestibulum morbi. Mauris commodo quis imperdiet massa tincidunt nunc pulvinar sapien et. Nunc scelerisque viverra mauris in aliquam sem fringilla. Suspendisse ultrices gravida dictum fusce ut placerat orci nulla.\\nLacus laoreet non curabitur gravida arcu ac tortor dignissim. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar. Tristique senectus et netus et malesuada fames ac. Nunc aliquet bibendum enim facilisis gravida. Egestas maecenas pharetra convallis posuere morbi leo urna molestie. Sapien nec sagittis aliquam malesuada bibendum arcu vitae elementum curabitur. Ac turpis egestas maecenas pharetra convallis posuere morbi leo urna. Viverra vitae congue eu consequat. Aliquet enim tortor at auctor urna. Ornare massa eget egestas purus viverra accumsan in nisl nisi. Elit pellentesque habitant morbi tristique senectus et netus et malesuada.\\nSuspendisse ultrices gravida dictum fusce ut placerat orci nulla pellentesque. Lobortis feugiat vivamus at augue eget arcu. Vitae justo eget magna fermentum iaculis eu. Gravida rutrum quisque non tellus orci. Ipsum faucibus vitae aliquet nec. Nullam non nisi est sit amet. Nunc consequat interdum varius sit amet mattis vulputate enim. Sem fringilla ut morbi tincidunt augue interdum. Vitae purus faucibus ornare suspendisse. Massa tincidunt nunc pulvinar sapien et. Fringilla ut morbi tincidunt augue interdum velit euismod in. Donec massa sapien faucibus et. Est placerat in egestas erat imperdiet. Gravida rutrum quisque non tellus. Morbi non arcu risus quis varius quam quisque id diam. Habitant morbi tristique senectus et netus et malesuada fames ac. Eget lorem dolor sed viverra.\\nOrnare massa eget egestas purus viverra. Varius vel pharetra vel turpis nunc eget lorem. Consectetur purus ut faucibus pulvinar elementum. Placerat in egestas erat imperdiet sed euismod nisi. Interdum velit euismod in pellentesque massa placerat duis ultricies lacus. Aliquam nulla facilisi cras fermentum odio eu. Est pellentesque elit ullamcorper dignissim cras tincidunt. Nunc sed id semper risus in hendrerit gravida rutrum. A pellentesque sit amet porttitor eget dolor morbi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Nisl nunc mi ipsum faucibus vitae aliquet nec ullamcorper. Sed id semper risus in hendrerit gravida. Tincidunt praesent semper feugiat nibh. Aliquet lectus proin nibh nisl condimentum id venenatis a. Enim sit amet venenatis urna cursus eget. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Lacinia quis vel eros donec ac odio tempor orci. Donec pretium vulputate sapien nec sagittis aliquam malesuada bibendum arcu. Erat pellentesque adipiscing commodo elit at.\\nEgestas sed sed risus pretium quam vulputate. Vitae congue mauris rhoncus aenean vel elit scelerisque mauris pellentesque. Aliquam malesuada bibendum arcu vitae elementum. Congue mauris rhoncus aenean vel elit scelerisque mauris. Pellentesque dignissim enim sit amet venenatis urna cursus. Et malesuada fames ac turpis egestas sed tempus urna. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Nibh cras pulvinar mattis nunc sed blandit libero. Fringilla est ullamcorper eget nulla facilisi etiam dignissim. Aenean euismod elementum nisi quis eleifend quam adipiscing vitae proin. Mauris pharetra et ultrices neque ornare aenean euismod elementum. Ornare quam viverra orci sagittis eu. Odio ut sem nulla pharetra diam sit amet nisl suscipit. Ornare lectus sit amet est. Ullamcorper sit amet risus nullam eget. Tincidunt lobortis feugiat vivamus at augue eget arcu dictum.\\nUrna nec tincidunt praesent semper feugiat nibh. Ut venenatis tellus in metus vulputate eu scelerisque felis. Cursus risus at ultrices mi tempus. In pellentesque massa placerat duis ultricies lacus sed turpis. Platea dictumst quisque sagittis purus. Cras adipiscing enim eu turpis egestas. Egestas sed tempus urna et pharetra pharetra. Netus et malesuada fames ac turpis egestas integer eget aliquet. Ac turpis egestas sed tempus. Sed lectus vestibulum mattis ullamcorper velit sed. Ante metus dictum at tempor commodo ullamcorper a. Augue neque gravida in fermentum et sollicitudin ac. Praesent semper feugiat nibh sed pulvinar proin gravida. Metus aliquam eleifend mi in nulla posuere sollicitudin aliquam ultrices. Neque gravida in fermentum et sollicitudin ac orci phasellus egestas.\\nRidiculus mus mauris vitae ultricies. Morbi quis commodo odio aenean. Duis ultricies lacus sed turpis. Non pulvinar neque laoreet suspendisse interdum consectetur. Scelerisque eleifend donec pretium vulputate sapien nec sagittis aliquam. Volutpat est velit egestas dui id ornare arcu odio ut. Viverra tellus in hac habitasse platea dictumst vestibulum rhoncus est. Vestibulum lectus mauris ultrices eros. Sed blandit libero volutpat sed cras ornare. Id leo in vitae turpis massa sed elementum tempus. Gravida dictum fusce ut placerat orci nulla pellentesque. Pretium quam vulputate dignissim suspendisse in. Nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Risus viverra adipiscing at in tellus. Turpis nunc eget lorem dolor sed viverra ipsum. Senectus et netus et malesuada fames ac. Habitasse platea dictumst vestibulum rhoncus est. Nunc sed id semper risus in hendrerit gravida. Felis eget velit aliquet sagittis id. Eget felis eget nunc lobortis.\\nMaecenas pharetra convallis posuere morbi leo. Maecenas volutpat blandit aliquam etiam. A condimentum vitae sapien pellentesque habitant morbi tristique senectus et. Pulvinar mattis nunc sed blandit libero volutpat sed. Feugiat in ante metus dictum at tempor commodo ullamcorper. Vel pharetra vel turpis nunc eget lorem dolor. Est placerat in egestas erat imperdiet sed euismod. Quisque non tellus orci ac auctor augue mauris augue. Placerat vestibulum lectus mauris ultrices eros in cursus turpis. Enim nunc faucibus a pellentesque sit. Adipiscing vitae proin sagittis nisl. Iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Aliquam sem fringilla ut morbi.\\nArcu odio ut sem nulla pharetra diam sit amet nisl. Non diam phasellus vestibulum lorem sed. At erat pellentesque adipiscing commodo elit at. Lacus luctus accumsan tortor posuere ac ut consequat. Et malesuada fames ac turpis egestas integer. Tristique magna sit amet purus. A condimentum vitae sapien pellentesque habitant. Quis varius quam quisque id diam vel quam. Est ullamcorper eget nulla facilisi etiam dignissim diam quis. Augue interdum velit euismod in pellentesque massa. Elit scelerisque mauris pellentesque pulvinar pellentesque habitant. Vulputate eu scelerisque felis imperdiet. Nibh tellus molestie nunc non blandit massa. Velit euismod in pellentesque massa placerat. Sed cras ornare arcu dui. Ut sem viverra aliquet eget sit. Eu lobortis elementum nibh tellus molestie nunc non. Blandit libero volutpat sed cras ornare arcu dui vivamus.\\nSit amet aliquam id diam maecenas. Amet risus nullam eget felis eget nunc lobortis mattis aliquam. Magna sit amet purus gravida. Egestas purus viverra accumsan in nisl nisi. Leo duis ut diam quam. Ante metus dictum at tempor commodo ullamcorper. Ac turpis egestas integer eget. Fames ac turpis egestas integer eget aliquet nibh. Sem integer vitae justo eget magna fermentum. Semper auctor neque vitae tempus quam pellentesque nec nam aliquam. Vestibulum mattis ullamcorper velit sed. Consectetur adipiscing elit duis tristique sollicitudin nibh. Massa id neque aliquam vestibulum morbi blandit cursus risus.\\nCursus sit amet dictum sit amet justo donec enim diam. Egestas erat imperdiet sed euismod. Nullam vehicula ipsum a arcu cursus vitae congue mauris. Habitasse platea dictumst vestibulum rhoncus est pellentesque elit. Duis ultricies lacus sed turpis tincidunt id aliquet risus feugiat. Faucibus ornare suspendisse sed nisi lacus sed viverra. Pretium fusce id velit ut tortor pretium viverra. Fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel. Senectus et netus et malesuada. Tellus pellentesque eu tincidunt tortor aliquam. Aenean sed adipiscing diam donec adipiscing tristique risus nec feugiat. Quis vel eros donec ac odio. Id interdum velit laoreet id donec ultrices tincidunt.\\nMassa id neque aliquam vestibulum morbi blandit cursus risus at. Enim tortor at auctor urna nunc id cursus metus. Lorem ipsum dolor sit amet consectetur. At quis risus sed vulputate odio. Facilisis mauris sit amet massa vitae tortor condimentum lacinia quis. Et malesuada fames ac turpis egestas maecenas. Bibendum arcu vitae elementum curabitur vitae nunc sed velit dignissim. Viverra orci sagittis eu volutpat odio facilisis mauris. Adipiscing bibendum est ultricies integer quis auctor elit sed. Neque viverra justo nec ultrices dui sapien. Elementum nibh tellus molestie nunc non blandit massa enim. Euismod elementum nisi quis eleifend quam adipiscing vitae proin sagittis. Faucibus ornare suspendisse sed nisi. Quis viverra nibh cras pulvinar mattis nunc sed blandit. Tristique senectus et netus et. Magnis dis parturient montes nascetur ridiculus mus.\\nDolor magna eget est lorem ipsum dolor. Nibh sit amet commodo nulla. Donec pretium vulputate sapien nec sagittis aliquam malesuada. Cras adipiscing enim eu turpis egestas pretium. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Mus mauris vitae ultricies leo integer. In nulla posuere sollicitudin aliquam ultrices sagittis orci. Quis hendrerit dolor magna eget. Nisl tincidunt eget nullam non. Vitae congue eu consequat ac felis donec et odio. Vivamus at augue eget arcu dictum varius duis at. Ornare quam viverra orci sagittis.\\nErat nam at lectus urna duis convallis. Massa placerat duis ultricies lacus sed turpis tincidunt id aliquet. Est ullamcorper eget nulla facilisi etiam dignissim diam. Arcu vitae elementum curabitur vitae nunc sed velit dignissim sodales. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus. Neque viverra justo nec ultrices dui sapien eget mi proin. Viverra accumsan in nisl nisi scelerisque eu ultrices. Consequat interdum varius sit amet mattis. In aliquam sem fringilla ut morbi. Eget arcu dictum varius duis at. Nulla aliquet porttitor lacus luctus accumsan tortor posuere. Arcu bibendum at varius vel pharetra vel turpis. Hac habitasse platea dictumst quisque sagittis purus sit amet. Sapien eget mi proin sed libero enim sed. Quam elementum pulvinar etiam non quam lacus suspendisse faucibus interdum. Semper viverra nam libero justo. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Et malesuada fames ac turpis egestas maecenas pharetra convallis posuere.\\nTurpis egestas sed tempus urna et pharetra pharetra massa. Gravida in fermentum et sollicitudin ac orci phasellus. Ornare suspendisse sed nisi lacus sed viverra tellus in. Fames ac turpis egestas maecenas pharetra convallis posuere. Mi proin sed libero enim sed faucibus turpis. Sit amet mauris commodo quis imperdiet massa tincidunt nunc. Ut etiam sit amet nisl purus in mollis nunc. Habitasse platea dictumst quisque sagittis purus sit amet volutpat consequat. Eget aliquet nibh praesent tristique magna. Sit amet est placerat in egestas erat. Commodo sed egestas egestas fringilla. Enim nulla aliquet porttitor lacus luctus accumsan tortor posuere ac. Et molestie ac feugiat sed lectus vestibulum mattis ullamcorper. Dignissim convallis aenean et tortor at risus viverra. Morbi blandit cursus risus at ultrices mi. Ac turpis egestas integer eget aliquet nibh praesent tristique magna.\\nVolutpat sed cras ornare arcu dui. Egestas erat imperdiet sed euismod nisi porta lorem mollis aliquam. Viverra justo nec ultrices dui sapien. Amet risus nullam eget felis eget nunc lobortis. Metus aliquam eleifend mi in. Ut eu sem integer vitae. Auctor elit sed vulputate mi sit amet. Nisl nisi scelerisque eu ultrices. Dictum fusce ut placerat orci nulla. Pellentesque habitant morbi tristique senectus et. Auctor elit sed vulputate mi sit. Tincidunt arcu non sodales neque. Mi in nulla posuere sollicitudin aliquam. Morbi non arcu risus quis varius quam quisque id diam. Cras adipiscing enim eu turpis egestas pretium aenean pharetra magna. At auctor urna nunc id cursus metus aliquam. Mauris a diam maecenas sed enim ut sem viverra. Nunc scelerisque viverra mauris in. In iaculis nunc sed augue lacus viverra vitae congue eu. Volutpat blandit aliquam etiam erat velit scelerisque in dictum non.\"),\n                     CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {0,0,0,255} }));\n            }\n        }\n\n        CLAY(CLAY_ID(\"Blob4Floating2\"), { .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING(\"SidebarBlob4\")).id } }) {\n            CLAY(CLAY_ID(\"ScrollContainer\"), { .layout = { .sizing = { .height = CLAY_SIZING_FIXED(200) }, .childGap = 2 }, .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {\n                CLAY(CLAY_ID(\"FloatingContainer2\"), { .layout.sizing.height = CLAY_SIZING_GROW(), .floating = { .attachTo = CLAY_ATTACH_TO_PARENT, .zIndex = 1 } }) {\n                    CLAY(CLAY_ID(\"FloatingContainerInner\"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW() }, .padding = {16, 16, 16, 16} }, .backgroundColor = {140,80, 200, 200} }) {\n                        CLAY_TEXT(CLAY_STRING(\"I'm an inline floating container.\"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255,255,255,255} }));\n                    }\n                }\n                CLAY(CLAY_ID(\"ScrollContainerInner\"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = {160, 160, 160, 255} }) {\n                    for (int i = 0; i < 100; i++) {\n                        RenderDropdownTextItem(i);\n                    }\n                }\n            }\n        }\n        Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING(\"MainContent\")));\n        if (scrollData.found) {\n            CLAY(CLAY_ID(\"ScrollBar\"), {\n                .floating = {\n                    .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID,\n                    .offset = { .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height },\n                    .zIndex = 1,\n                    .parentId = Clay_GetElementId(CLAY_STRING(\"MainContent\")).id,\n                    .attachPoints = { .element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP }\n                }\n            }) {\n                CLAY(CLAY_ID(\"ScrollBarButton\"), {\n                    .layout = { .sizing = {CLAY_SIZING_FIXED(12), CLAY_SIZING_FIXED((scrollData.scrollContainerDimensions.height / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height) }},\n                    .backgroundColor = Clay_PointerOver(Clay_GetElementId(CLAY_STRING(\"ScrollBar\"))) ? (Clay_Color){100, 100, 140, 150} : (Clay_Color){120, 120, 160, 150} ,\n                    .cornerRadius = CLAY_CORNER_RADIUS(6)\n                }) {}\n            }\n        }\n    }\n    return Clay_EndLayout();\n}\n\ntypedef struct\n{\n    Clay_Vector2 clickOrigin;\n    Clay_Vector2 positionOrigin;\n    bool mouseDown;\n} ScrollbarData;\n\nScrollbarData scrollbarData = {0};\n\nbool debugEnabled = false;\n\nvoid UpdateDrawFrame(Font* fonts)\n{\n    Vector2 mouseWheelDelta = GetMouseWheelMoveV();\n    float mouseWheelX = mouseWheelDelta.x;\n    float mouseWheelY = mouseWheelDelta.y;\n\n    if (IsKeyPressed(KEY_D)) {\n        debugEnabled = !debugEnabled;\n        Clay_SetDebugModeEnabled(debugEnabled);\n    }\n    //----------------------------------------------------------------------------------\n    // Handle scroll containers\n    Clay_Vector2 mousePosition = RAYLIB_VECTOR2_TO_CLAY_VECTOR2(GetMousePosition());\n    Clay_SetPointerState(mousePosition, IsMouseButtonDown(0) && !scrollbarData.mouseDown);\n    Clay_SetLayoutDimensions((Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() });\n    if (!IsMouseButtonDown(0)) {\n        scrollbarData.mouseDown = false;\n    }\n\n    if (IsMouseButtonDown(0) && !scrollbarData.mouseDown && Clay_PointerOver(Clay_GetElementId(CLAY_STRING(\"ScrollBar\")))) {\n        Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING(\"MainContent\")));\n        scrollbarData.clickOrigin = mousePosition;\n        scrollbarData.positionOrigin = *scrollContainerData.scrollPosition;\n        scrollbarData.mouseDown = true;\n    } else if (scrollbarData.mouseDown) {\n        Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING(\"MainContent\")));\n        if (scrollContainerData.contentDimensions.height > 0) {\n            Clay_Vector2 ratio = (Clay_Vector2) {\n                scrollContainerData.contentDimensions.width / scrollContainerData.scrollContainerDimensions.width,\n                scrollContainerData.contentDimensions.height / scrollContainerData.scrollContainerDimensions.height,\n            };\n            if (scrollContainerData.config.vertical) {\n                scrollContainerData.scrollPosition->y = scrollbarData.positionOrigin.y + (scrollbarData.clickOrigin.y - mousePosition.y) * ratio.y;\n            }\n            if (scrollContainerData.config.horizontal) {\n                scrollContainerData.scrollPosition->x = scrollbarData.positionOrigin.x + (scrollbarData.clickOrigin.x - mousePosition.x) * ratio.x;\n            }\n        }\n    }\n\n    Clay_UpdateScrollContainers(true, (Clay_Vector2) {mouseWheelX, mouseWheelY}, GetFrameTime());\n    // Generate the auto layout for rendering\n    double currentTime = GetTime();\n    Clay_RenderCommandArray renderCommands = CreateLayout();\n    printf(\"layout time: %f microseconds\\n\", (GetTime() - currentTime) * 1000 * 1000);\n    // RENDERING ---------------------------------\n//    currentTime = GetTime();\n    BeginDrawing();\n    ClearBackground(BLACK);\n    Clay_Raylib_Render(renderCommands, fonts);\n    EndDrawing();\n//    printf(\"render time: %f ms\\n\", (GetTime() - currentTime) * 1000);\n\n    //----------------------------------------------------------------------------------\n}\n\nbool reinitializeClay = false;\n\nvoid HandleClayErrors(Clay_ErrorData errorData) {\n    printf(\"%s\", errorData.errorText.chars);\n    if (errorData.errorType == CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED) {\n        reinitializeClay = true;\n        Clay_SetMaxElementCount(Clay_GetMaxElementCount() * 2);\n    } else if (errorData.errorType == CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED) {\n        reinitializeClay = true;\n        Clay_SetMaxMeasureTextCacheWordCount(Clay_GetMaxMeasureTextCacheWordCount() * 2);\n    }\n}\n\nint main(void) {\n    uint64_t totalMemorySize = Clay_MinMemorySize();\n    Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));\n    Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() }, (Clay_ErrorHandler) { HandleClayErrors, 0 });\n    Clay_Raylib_Initialize(1024, 768, \"Clay - Raylib Renderer Example\", FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE | FLAG_MSAA_4X_HINT);\n    profilePicture = LoadTexture(\"resources/profile-picture.png\");\n\n    Font fonts[2];\n    fonts[FONT_ID_BODY_24] = LoadFontEx(\"resources/Roboto-Regular.ttf\", 48, 0, 400);\n\tSetTextureFilter(fonts[FONT_ID_BODY_24].texture, TEXTURE_FILTER_BILINEAR);\n    fonts[FONT_ID_BODY_16] = LoadFontEx(\"resources/Roboto-Regular.ttf\", 32, 0, 400);\n    SetTextureFilter(fonts[FONT_ID_BODY_16].texture, TEXTURE_FILTER_BILINEAR);\n    Clay_SetMeasureTextFunction(Raylib_MeasureText, fonts);\n\n    //--------------------------------------------------------------------------------------\n\n    // Main game loop\n    while (!WindowShouldClose())    // Detect window close button or ESC key\n    {\n        if (reinitializeClay) {\n            Clay_SetMaxElementCount(8192);\n            totalMemorySize = Clay_MinMemorySize();\n            clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));\n            Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() }, (Clay_ErrorHandler) { HandleClayErrors, 0 });\n            reinitializeClay = false;\n        }\n        UpdateDrawFrame(fonts);\n    }\n    Clay_Raylib_Close();\n    return 0;\n}\n"
  },
  {
    "path": "examples/raylib-sidebar-scrolling-container/multi-compilation-unit.c",
    "content": "#include \"../../clay.h\"\n\n// NOTE: This file only exists to make sure that clay works when included in multiple translation units.\n\nvoid SatisfyCompiler(void) {\n    CLAY(CLAY_ID(\"SatisfyCompiler\"), { }) {\n      CLAY_TEXT(CLAY_STRING(\"Test\"), CLAY_TEXT_CONFIG({ .fontId = 0, .fontSize = 24 }));\n    }\n}\n"
  },
  {
    "path": "examples/shared-layouts/clay-video-demo.c",
    "content": "#include \"../../clay.h\"\n#include <stdlib.h>\n\nconst int FONT_ID_BODY_16 = 0;\nClay_Color COLOR_WHITE = { 255, 255, 255, 255};\n\nvoid RenderHeaderButton(Clay_String text) {\n    CLAY_AUTO_ID({\n        .layout = { .padding = { 16, 16, 8, 8 }},\n        .backgroundColor = { 140, 140, 140, 255 },\n        .cornerRadius = CLAY_CORNER_RADIUS(5)\n    }) {\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG({\n            .fontId = FONT_ID_BODY_16,\n            .fontSize = 16,\n            .textColor = { 255, 255, 255, 255 }\n        }));\n    }\n}\n\nvoid RenderDropdownMenuItem(Clay_String text) {\n    CLAY_AUTO_ID({.layout = { .padding = CLAY_PADDING_ALL(16)}}) {\n        CLAY_TEXT(text, CLAY_TEXT_CONFIG({\n            .fontId = FONT_ID_BODY_16,\n            .fontSize = 16,\n            .textColor = { 255, 255, 255, 255 }\n        }));\n    }\n}\n\ntypedef struct {\n    Clay_String title;\n    Clay_String contents;\n} Document;\n\ntypedef struct {\n    Document *documents;\n    uint32_t length;\n} DocumentArray;\n\nDocument documentsRaw[5];\n\nDocumentArray documents = {\n    .length = 5,\n    .documents = documentsRaw\n};\n\ntypedef struct {\n    intptr_t offset;\n    intptr_t memory;\n} ClayVideoDemo_Arena;\n\ntypedef struct {\n    int32_t selectedDocumentIndex;\n    float yOffset;\n    ClayVideoDemo_Arena frameArena;\n} ClayVideoDemo_Data;\n\ntypedef struct {\n    int32_t requestedDocumentIndex;\n    int32_t* selectedDocumentIndex;\n} SidebarClickData;\n\nvoid HandleSidebarInteraction(\n    Clay_ElementId elementId,\n    Clay_PointerData pointerData,\n    void *userData\n) {\n    SidebarClickData *clickData = (SidebarClickData*)userData;\n    // If this button was clicked\n    if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {\n        if (clickData->requestedDocumentIndex >= 0 && clickData->requestedDocumentIndex < documents.length) {\n            // Select the corresponding document\n            *clickData->selectedDocumentIndex = clickData->requestedDocumentIndex;\n        }\n    }\n}\n\nClayVideoDemo_Data ClayVideoDemo_Initialize() {\n    documents.documents[0] = (Document){ .title = CLAY_STRING(\"Squirrels\"), .contents = CLAY_STRING(\"The Secret Life of Squirrels: Nature's Clever Acrobats\\n\"\"Squirrels are often overlooked creatures, dismissed as mere park inhabitants or backyard nuisances. Yet, beneath their fluffy tails and twitching noses lies an intricate world of cunning, agility, and survival tactics that are nothing short of fascinating. As one of the most common mammals in North America, squirrels have adapted to a wide range of environments from bustling urban centers to tranquil forests and have developed a variety of unique behaviors that continue to intrigue scientists and nature enthusiasts alike.\\n\"\"\\n\"\"Master Tree Climbers\\n\"\"At the heart of a squirrel's skill set is its impressive ability to navigate trees with ease. Whether they're darting from branch to branch or leaping across wide gaps, squirrels possess an innate talent for acrobatics. Their powerful hind legs, which are longer than their front legs, give them remarkable jumping power. With a tail that acts as a counterbalance, squirrels can leap distances of up to ten times the length of their body, making them some of the best aerial acrobats in the animal kingdom.\\n\"\"But it's not just their agility that makes them exceptional climbers. Squirrels' sharp, curved claws allow them to grip tree bark with precision, while the soft pads on their feet provide traction on slippery surfaces. Their ability to run at high speeds and scale vertical trunks with ease is a testament to the evolutionary adaptations that have made them so successful in their arboreal habitats.\\n\"\"\\n\"\"Food Hoarders Extraordinaire\\n\"\"Squirrels are often seen frantically gathering nuts, seeds, and even fungi in preparation for winter. While this behavior may seem like instinctual hoarding, it is actually a survival strategy that has been honed over millions of years. Known as \\\"scatter hoarding,\\\" squirrels store their food in a variety of hidden locations, often burying it deep in the soil or stashing it in hollowed-out tree trunks.\\n\"\"Interestingly, squirrels have an incredible memory for the locations of their caches. Research has shown that they can remember thousands of hiding spots, often returning to them months later when food is scarce. However, they don't always recover every stash some forgotten caches eventually sprout into new trees, contributing to forest regeneration. This unintentional role as forest gardeners highlights the ecological importance of squirrels in their ecosystems.\\n\"\"\\n\"\"The Great Squirrel Debate: Urban vs. Wild\\n\"\"While squirrels are most commonly associated with rural or wooded areas, their adaptability has allowed them to thrive in urban environments as well. In cities, squirrels have become adept at finding food sources in places like parks, streets, and even garbage cans. However, their urban counterparts face unique challenges, including traffic, predators, and the lack of natural shelters. Despite these obstacles, squirrels in urban areas are often observed using human infrastructure such as buildings, bridges, and power lines as highways for their acrobatic escapades.\\n\"\"There is, however, a growing concern regarding the impact of urban life on squirrel populations. Pollution, deforestation, and the loss of natural habitats are making it more difficult for squirrels to find adequate food and shelter. As a result, conservationists are focusing on creating squirrel-friendly spaces within cities, with the goal of ensuring these resourceful creatures continue to thrive in both rural and urban landscapes.\\n\"\"\\n\"\"A Symbol of Resilience\\n\"\"In many cultures, squirrels are symbols of resourcefulness, adaptability, and preparation. Their ability to thrive in a variety of environments while navigating challenges with agility and grace serves as a reminder of the resilience inherent in nature. Whether you encounter them in a quiet forest, a city park, or your own backyard, squirrels are creatures that never fail to amaze with their endless energy and ingenuity.\\n\"\"In the end, squirrels may be small, but they are mighty in their ability to survive and thrive in a world that is constantly changing. So next time you spot one hopping across a branch or darting across your lawn, take a moment to appreciate the remarkable acrobat at work a true marvel of the natural world.\\n\") };\n    documents.documents[1] = (Document){ .title = CLAY_STRING(\"Lorem Ipsum\"), .contents = CLAY_STRING(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\") };\n    documents.documents[2] = (Document){ .title = CLAY_STRING(\"Vacuum Instructions\"), .contents = CLAY_STRING(\"Chapter 3: Getting Started - Unpacking and Setup\\n\"\"\\n\"\"Congratulations on your new SuperClean Pro 5000 vacuum cleaner! In this section, we will guide you through the simple steps to get your vacuum up and running. Before you begin, please ensure that you have all the components listed in the \\\"Package Contents\\\" section on page 2.\\n\"\"\\n\"\"1. Unboxing Your Vacuum\\n\"\"Carefully remove the vacuum cleaner from the box. Avoid using sharp objects that could damage the product. Once removed, place the unit on a flat, stable surface to proceed with the setup. Inside the box, you should find:\\n\"\"\\n\"\"    The main vacuum unit\\n\"\"    A telescoping extension wand\\n\"\"    A set of specialized cleaning tools (crevice tool, upholstery brush, etc.)\\n\"\"    A reusable dust bag (if applicable)\\n\"\"    A power cord with a 3-prong plug\\n\"\"    A set of quick-start instructions\\n\"\"\\n\"\"2. Assembling Your Vacuum\\n\"\"Begin by attaching the extension wand to the main body of the vacuum cleaner. Line up the connectors and twist the wand into place until you hear a click. Next, select the desired cleaning tool and firmly attach it to the wand's end, ensuring it is securely locked in.\\n\"\"\\n\"\"For models that require a dust bag, slide the bag into the compartment at the back of the vacuum, making sure it is properly aligned with the internal mechanism. If your vacuum uses a bagless system, ensure the dust container is correctly seated and locked in place before use.\\n\"\"\\n\"\"3. Powering On\\n\"\"To start the vacuum, plug the power cord into a grounded electrical outlet. Once plugged in, locate the power switch, usually positioned on the side of the handle or body of the unit, depending on your model. Press the switch to the \\\"On\\\" position, and you should hear the motor begin to hum. If the vacuum does not power on, check that the power cord is securely plugged in, and ensure there are no blockages in the power switch.\\n\"\"\\n\"\"Note: Before first use, ensure that the vacuum filter (if your model has one) is properly installed. If unsure, refer to \\\"Section 5: Maintenance\\\" for filter installation instructions.\") };\n    documents.documents[3] = (Document){ .title = CLAY_STRING(\"Article 4\"), .contents = CLAY_STRING(\"Article 4\") };\n    documents.documents[4] = (Document){ .title = CLAY_STRING(\"Article 5\"), .contents = CLAY_STRING(\"Article 5\") };\n\n    ClayVideoDemo_Data data = {\n        .frameArena = { .memory = (intptr_t)malloc(1024) }\n    };\n    return data;\n}\n\nClay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {\n    data->frameArena.offset = 0;\n\n    Clay_BeginLayout();\n\n    Clay_Sizing layoutExpand = {\n        .width = CLAY_SIZING_GROW(0),\n        .height = CLAY_SIZING_GROW(0)\n    };\n\n    Clay_Color contentBackgroundColor = { 90, 90, 90, 255 };\n\n    // Build UI here\n    CLAY(CLAY_ID(\"OuterContainer\"), {\n        .backgroundColor = {43, 41, 51, 255 },\n        .layout = {\n            .layoutDirection = CLAY_TOP_TO_BOTTOM,\n            .sizing = layoutExpand,\n            .padding = CLAY_PADDING_ALL(16),\n            .childGap = 16\n        }\n    }) {\n        // Child elements go inside braces\n        CLAY(CLAY_ID(\"HeaderBar\"), {\n            .layout = {\n                .sizing = {\n                    .height = CLAY_SIZING_FIXED(60),\n                    .width = CLAY_SIZING_GROW(0)\n                },\n                .padding = { 16, 16, 0, 0 },\n                .childGap = 16,\n                .childAlignment = {\n                    .y = CLAY_ALIGN_Y_CENTER\n                }\n            },\n            .backgroundColor = contentBackgroundColor,\n            .cornerRadius = CLAY_CORNER_RADIUS(8)\n        }) {\n            // Header buttons go here\n            CLAY(CLAY_ID(\"FileButton\"), {\n                .layout = { .padding = { 16, 16, 8, 8 }},\n                .backgroundColor = {140, 140, 140, 255 },\n                .cornerRadius = CLAY_CORNER_RADIUS(5)\n            }) {\n                CLAY_TEXT(CLAY_STRING(\"File\"), CLAY_TEXT_CONFIG({\n                    .fontId = FONT_ID_BODY_16,\n                    .fontSize = 16,\n                    .textColor = { 255, 255, 255, 255 }\n                }));\n\n                bool fileMenuVisible =\n                    Clay_PointerOver(Clay_GetElementId(CLAY_STRING(\"FileButton\")))\n                    ||\n                    Clay_PointerOver(Clay_GetElementId(CLAY_STRING(\"FileMenu\")));\n\n                if (fileMenuVisible) { // Below has been changed slightly to fix the small bug where the menu would dismiss when mousing over the top gap\n                    CLAY(CLAY_ID(\"FileMenu\"), {\n                        .floating = {\n                            .attachTo = CLAY_ATTACH_TO_PARENT,\n                            .attachPoints = {\n                                .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM\n                            },\n                        },\n                        .layout = {\n                            .padding = {0, 0, 8, 8 }\n                        }\n                    }) {\n                        CLAY_AUTO_ID({\n                            .layout = {\n                                .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                                .sizing = {\n                                        .width = CLAY_SIZING_FIXED(200)\n                                },\n                            },\n                            .backgroundColor = {40, 40, 40, 255 },\n                            .cornerRadius = CLAY_CORNER_RADIUS(8)\n                        }) {\n                            // Render dropdown items here\n                            RenderDropdownMenuItem(CLAY_STRING(\"New\"));\n                            RenderDropdownMenuItem(CLAY_STRING(\"Open\"));\n                            RenderDropdownMenuItem(CLAY_STRING(\"Close\"));\n                        }\n                    }\n                }\n            }\n            RenderHeaderButton(CLAY_STRING(\"Edit\"));\n            CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0) }}}) {}\n            RenderHeaderButton(CLAY_STRING(\"Upload\"));\n            RenderHeaderButton(CLAY_STRING(\"Media\"));\n            RenderHeaderButton(CLAY_STRING(\"Support\"));\n        }\n\n        CLAY(CLAY_ID(\"LowerContent\"), {\n            .layout = { .sizing = layoutExpand, .childGap = 16 }\n        }) {\n            CLAY(CLAY_ID(\"Sidebar\"), {\n                .backgroundColor = contentBackgroundColor,\n                .layout = {\n                    .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                    .padding = CLAY_PADDING_ALL(16),\n                    .childGap = 8,\n                    .sizing = {\n                        .width = CLAY_SIZING_FIXED(250),\n                        .height = CLAY_SIZING_GROW(0)\n                    }\n                }\n            }) {\n                for (int i = 0; i < documents.length; i++) {\n                    Document document = documents.documents[i];\n                    Clay_LayoutConfig sidebarButtonLayout = {\n                        .sizing = { .width = CLAY_SIZING_GROW(0) },\n                        .padding = CLAY_PADDING_ALL(16)\n                    };\n\n                    if (i == data->selectedDocumentIndex) {\n                        CLAY_AUTO_ID({\n                            .layout = sidebarButtonLayout,\n                            .backgroundColor = {120, 120, 120, 255 },\n                            .cornerRadius = CLAY_CORNER_RADIUS(8)\n                        }) {\n                            CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({\n                                .fontId = FONT_ID_BODY_16,\n                                .fontSize = 20,\n                                .textColor = { 255, 255, 255, 255 }\n                            }));\n                        }\n                    } else {\n                        SidebarClickData *clickData = (SidebarClickData *)(data->frameArena.memory + data->frameArena.offset);\n                        *clickData = (SidebarClickData) { .requestedDocumentIndex = i, .selectedDocumentIndex = &data->selectedDocumentIndex };\n                        data->frameArena.offset += sizeof(SidebarClickData);\n                        CLAY_AUTO_ID({ .layout = sidebarButtonLayout, .backgroundColor = (Clay_Color) { 120, 120, 120, (float)(Clay_Hovered() ? 120 : 0) }, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {\n                            Clay_OnHover(HandleSidebarInteraction, clickData);\n                            CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({\n                                .fontId = FONT_ID_BODY_16,\n                                .fontSize = 20,\n                                .textColor = { 255, 255, 255, 255 }\n                            }));\n                        }\n                    }\n                }\n            }\n\n            CLAY(CLAY_ID(\"MainContent\"), {\n                .backgroundColor = contentBackgroundColor,\n                .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },\n                .layout = {\n                    .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                    .childGap = 16,\n                    .padding = CLAY_PADDING_ALL(16),\n                    .sizing = layoutExpand\n                }\n            }) {\n                Document selectedDocument = documents.documents[data->selectedDocumentIndex];\n                CLAY_TEXT(selectedDocument.title, CLAY_TEXT_CONFIG({\n                    .fontId = FONT_ID_BODY_16,\n                    .fontSize = 24,\n                    .textColor = COLOR_WHITE\n                }));\n                CLAY_TEXT(selectedDocument.contents, CLAY_TEXT_CONFIG({\n                    .fontId = FONT_ID_BODY_16,\n                    .fontSize = 24,\n                    .textColor = COLOR_WHITE\n                }));\n            }\n        }\n    }\n\n    Clay_RenderCommandArray renderCommands = Clay_EndLayout();\n    for (int32_t i = 0; i < renderCommands.length; i++) {\n        Clay_RenderCommandArray_Get(&renderCommands, i)->boundingBox.y += data->yOffset;\n    }\n    return renderCommands;\n}"
  },
  {
    "path": "examples/sokol-corner-radius/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(sokol_corner_radius C)\n\nif(CMAKE_SYSTEM_NAME STREQUAL Windows)\n    add_executable(sokol_corner_radius WIN32 main.c)\n    set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT sokol_corner_radius)\nelse()\n    add_executable(sokol_corner_radius main.c)\nendif()\ntarget_link_libraries(sokol_corner_radius PUBLIC sokol)"
  },
  {
    "path": "examples/sokol-corner-radius/main.c",
    "content": "#include \"sokol_app.h\"\n#include \"sokol_gfx.h\"\n#include \"sokol_glue.h\"\n#include \"sokol_log.h\"\n\n#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n\n#include \"util/sokol_gl.h\"\n#include \"fontstash.h\"\n#include \"util/sokol_fontstash.h\"\n#define SOKOL_CLAY_IMPL\n#include \"../../renderers/sokol/sokol_clay.h\"\n\nstatic void init() {\n    sg_setup(&(sg_desc){\n        .environment = sglue_environment(),\n        .logger.func = slog_func,\n    });\n    sgl_setup(&(sgl_desc_t){\n        .logger.func = slog_func,\n    });\n    sclay_setup();\n    uint64_t totalMemorySize = Clay_MinMemorySize();\n    Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));\n    Clay_Initialize(clayMemory, (Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() }, (Clay_ErrorHandler){0});\n    Clay_SetMeasureTextFunction(sclay_measure_text, NULL);\n}\n\nClay_RenderCommandArray CornerRadiusTest(){\n    Clay_BeginLayout();\n    Clay_Sizing layoutExpand = {\n        .width = CLAY_SIZING_GROW(0),\n        .height = CLAY_SIZING_GROW(0)\n    };\n    CLAY(CLAY_ID(\"OuterContainer\"), {\n        .backgroundColor = {43, 41, 51, 255},\n        .layout = {\n            .layoutDirection = CLAY_TOP_TO_BOTTOM,\n            .sizing = layoutExpand,\n            .padding = {0, 0, 20, 20},\n            .childGap = 20\n        }\n    }) {\n        for(int i = 0; i < 6; ++i){\n            CLAY(CLAY_IDI(\"Row\", i), {\n                .layout = {\n                    .layoutDirection = CLAY_LEFT_TO_RIGHT,\n                    .sizing = layoutExpand,\n                    .padding = {20, 20, 0, 0},\n                    .childGap = 20\n                }\n            }) {\n                for(int j = 0; j < 6; ++j){\n                    CLAY(CLAY_IDI(\"Tile\", i*6+j), {\n                        .backgroundColor = {120, 140, 255, 128},\n                        .cornerRadius = {(i%3)*15, (j%3)*15, (i/2)*15, (j/2)*15},\n                        .border = {\n                            .color = {120, 140, 255, 255},\n                            .width = {3, 9, 6, 12, 0},\n                        },\n                        .layout = { .sizing = layoutExpand }\n                    });\n                }\n            }\n        }\n    }\n    return Clay_EndLayout();\n}\n\nstatic void frame() {\n    sclay_new_frame();\n    Clay_RenderCommandArray renderCommands = CornerRadiusTest();\n\n    sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() });\n    sgl_matrix_mode_modelview();\n    sgl_load_identity();\n    sclay_render(renderCommands, NULL);\n    sgl_draw();\n    sg_end_pass();\n    sg_commit();\n}\n\nstatic void event(const sapp_event *ev) {\n    if(ev->type == SAPP_EVENTTYPE_KEY_DOWN && ev->key_code == SAPP_KEYCODE_D){\n        Clay_SetDebugModeEnabled(true);\n    } else {\n        sclay_handle_event(ev);\n    }\n}\n\nstatic void cleanup() {\n    sclay_shutdown();\n    sgl_shutdown();\n    sg_shutdown();\n}\n\nsapp_desc sokol_main(int argc, char **argv) {\n    return (sapp_desc){\n        .init_cb = init,\n        .frame_cb = frame,\n        .event_cb = event,\n        .cleanup_cb = cleanup,\n        .window_title = \"Clay - Corner Radius Test\",\n        .width = 800,\n        .height = 600,\n        .icon.sokol_default = true,\n        .logger.func = slog_func,\n    };\n}\n"
  },
  {
    "path": "examples/sokol-video-demo/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(sokol_video_demo C)\n\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\n\n# Linux -pthread shenanigans\nif (CMAKE_SYSTEM_NAME STREQUAL Linux)\n    set(THREADS_PREFER_PTHREAD_FLAG ON)\n    find_package(Threads REQUIRED)\nendif()\n\nFetchContent_Declare(\n    fontstash\n    GIT_REPOSITORY \"https://github.com/memononen/fontstash.git\"\n    GIT_TAG \"b5ddc9741061343740d85d636d782ed3e07cf7be\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(fontstash)\n\nFetchContent_Declare(\n    sokol\n    GIT_REPOSITORY \"https://github.com/floooh/sokol.git\"\n    GIT_TAG \"master\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(sokol)\nset(sokol_HEADERS\n    ${sokol_SOURCE_DIR}/sokol_app.h\n    ${sokol_SOURCE_DIR}/sokol_gfx.h\n    ${sokol_SOURCE_DIR}/sokol_glue.h\n    ${sokol_SOURCE_DIR}/sokol_log.h\n    ${sokol_SOURCE_DIR}/util/sokol_gl.h\n    ${fontstash_SOURCE_DIR}/src/fontstash.h\n    ${sokol_SOURCE_DIR}/util/sokol_fontstash.h)\nif(CMAKE_SYSTEM_NAME STREQUAL Darwin)\n    add_library(sokol STATIC sokol.c ${sokol_HEADERS})\n    target_compile_options(sokol PRIVATE -x objective-c)\n    target_link_libraries(sokol PUBLIC\n        \"-framework QuartzCore\"\n        \"-framework Cocoa\"\n        \"-framework MetalKit\"\n        \"-framework Metal\")\nelse()\n    add_library(sokol STATIC sokol.c ${sokol_HEADERS})\n    if (CMAKE_SYSTEM_NAME STREQUAL Linux)\n        target_compile_definitions(sokol PRIVATE SOKOL_GLCORE=1)\n        target_link_libraries(sokol INTERFACE X11 Xi Xcursor GL dl m)\n        target_link_libraries(sokol PUBLIC Threads::Threads)\n    endif()\nendif()\ntarget_include_directories(sokol INTERFACE ${sokol_SOURCE_DIR} ${fontstash_SOURCE_DIR}/src\n                                 PRIVATE ${sokol_SOURCE_DIR} ${fontstash_SOURCE_DIR}/src)\n\n\n\nif(CMAKE_SYSTEM_NAME STREQUAL Windows)\n    add_executable(sokol_video_demo WIN32 main.c)\n    set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT sokol_video_demo)\nelse()\n    add_executable(sokol_video_demo main.c)\nendif()\ntarget_link_libraries(sokol_video_demo PUBLIC sokol)\n\nadd_custom_command(\n        TARGET sokol_video_demo POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy_directory\n        ${CMAKE_CURRENT_SOURCE_DIR}/resources\n        ${CMAKE_CURRENT_BINARY_DIR}/resources)\n"
  },
  {
    "path": "examples/sokol-video-demo/main.c",
    "content": "#include \"sokol_app.h\"\n#include \"sokol_gfx.h\"\n#include \"sokol_glue.h\"\n#include \"sokol_log.h\"\n\n#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n\n#include \"util/sokol_gl.h\"\n#include \"fontstash.h\"\n#include \"util/sokol_fontstash.h\"\n#define SOKOL_CLAY_IMPL\n#include \"../../renderers/sokol/sokol_clay.h\"\n\n#include \"../shared-layouts/clay-video-demo.c\"\n\nstatic ClayVideoDemo_Data demoData;\nstatic sclay_font_t fonts[1];\n\nstatic void init() {\n    sg_setup(&(sg_desc){\n        .environment = sglue_environment(),\n        .logger.func = slog_func,\n    });\n    sgl_setup(&(sgl_desc_t){\n        .logger.func = slog_func,\n    });\n    sclay_setup();\n    uint64_t totalMemorySize = Clay_MinMemorySize();\n    Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));\n    Clay_Initialize(clayMemory, (Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() }, (Clay_ErrorHandler){0});\n    fonts[FONT_ID_BODY_16] = sclay_add_font(\"resources/Roboto-Regular.ttf\");\n    Clay_SetMeasureTextFunction(sclay_measure_text, &fonts);\n    demoData = ClayVideoDemo_Initialize();\n}\n\nstatic void frame() {\n    sclay_new_frame();\n    Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData);\n\n    sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() });\n    sgl_matrix_mode_modelview();\n    sgl_load_identity();\n    sclay_render(renderCommands, fonts);\n    sgl_draw();\n    sg_end_pass();\n    sg_commit();\n}\n\nstatic void event(const sapp_event *ev) {\n    if(ev->type == SAPP_EVENTTYPE_KEY_DOWN && ev->key_code == SAPP_KEYCODE_D){\n        Clay_SetDebugModeEnabled(true);\n    } else {\n        sclay_handle_event(ev);\n    }\n}\n\nstatic void cleanup() {\n    sclay_shutdown();\n    sgl_shutdown();\n    sg_shutdown();\n}\n\nsapp_desc sokol_main(int argc, char **argv) {\n    return (sapp_desc){\n        .init_cb = init,\n        .frame_cb = frame,\n        .event_cb = event,\n        .cleanup_cb = cleanup,\n        .window_title = \"Clay - Sokol Renderer Example\",\n        .width = 800,\n        .height = 600,\n        .high_dpi = true,\n        .icon.sokol_default = true,\n        .logger.func = slog_func,\n    };\n}\n"
  },
  {
    "path": "examples/sokol-video-demo/sokol.c",
    "content": "#define SOKOL_IMPL\n#if defined(_WIN32)\n#define SOKOL_D3D11\n#elif defined(__EMSCRIPTEN__)\n#define SOKOL_GLES2\n#elif defined(__APPLE__)\n#define SOKOL_METAL\n#else\n#define SOKOL_GLCORE33\n#endif\n#include \"sokol_app.h\"\n#include \"sokol_gfx.h\"\n#include \"sokol_time.h\"\n#include \"sokol_fetch.h\"\n#include \"sokol_glue.h\"\n#include \"sokol_log.h\"\n\n#include \"util/sokol_gl.h\"\n#include <stdio.h> // fontstash requires this\n#include <stdlib.h> // fontstash requires this\n#define FONTSTASH_IMPLEMENTATION\n#include \"fontstash.h\"\n#define SOKOL_FONTSTASH_IMPL\n#include \"util/sokol_fontstash.h\"\n"
  },
  {
    "path": "examples/termbox2-demo/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.25)\nproject(clay_examples_termbox2_demo C)\nset(CMAKE_C_STANDARD 11)\n\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\n\nFetchContent_Declare(\n    termbox2\n    GIT_REPOSITORY \"https://github.com/termbox/termbox2.git\"\n    GIT_TAG \"ffd159c2a6106dd5eef338a6702ad15d4d4aa809\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(termbox2)\n\nFetchContent_Declare(\n    stb\n    GIT_REPOSITORY \"https://github.com/nothings/stb.git\"\n    GIT_TAG \"fede005abaf93d9d7f3a679d1999b2db341b360f\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(stb)\n\nadd_executable(clay_examples_termbox2_demo main.c)\n\ntarget_compile_options(clay_examples_termbox2_demo PUBLIC)\ntarget_include_directories(clay_examples_termbox2_demo PUBLIC . PRIVATE ${termbox2_SOURCE_DIR} PRIVATE ${stb_SOURCE_DIR})\ntarget_link_libraries(clay_examples_termbox2_demo PRIVATE m) # Used by stb_image.h\n\nadd_custom_command(\n        TARGET clay_examples_termbox2_demo POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy_directory\n        ${CMAKE_CURRENT_SOURCE_DIR}/resources\n        ${CMAKE_CURRENT_BINARY_DIR}/resources)\n"
  },
  {
    "path": "examples/termbox2-demo/main.c",
    "content": "/*\n    Unlicense\n\n    Copyright (c) 2025 Mivirl\n\n    This is free and unencumbered software released into the public domain.\n\n    Anyone is free to copy, modify, publish, use, compile, sell, or\n    distribute this software, either in source code form or as a compiled\n    binary, for any purpose, commercial or non-commercial, and by any\n    means.\n\n    In jurisdictions that recognize copyright laws, the author or authors\n    of this software dedicate any and all copyright interest in the\n    software to the public domain. We make this dedication for the benefit\n    of the public at large and to the detriment of our heirs and\n    successors. We intend this dedication to be an overt act of\n    relinquishment in perpetuity of all present and future rights to this\n    software under copyright law.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n    IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n    OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n    OTHER DEALINGS IN THE SOFTWARE.\n\n    For more information, please refer to <https://unlicense.org/>\n*/\n\n#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n#include \"../../renderers/termbox2/clay_renderer_termbox2.c\"\n\n#define TB_IMPL\n#include \"termbox2.h\"\n\n#define STB_IMAGE_IMPLEMENTATION\n#include \"stb_image.h\"\n\n#define STB_IMAGE_RESIZE_IMPLEMENTATION\n#include \"stb_image_resize2.h\"\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Internal state\n\n// If the program should exit the main render/interaction loop\nbool end_loop = false;\n\n// If the debug tools should be displayed\nbool debugMode = false;\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Clay components\n\nvoid component_text_pair(const char *key, const char *value)\n{\n    size_t keylen = strlen(key);\n    size_t vallen = strlen(value);\n    Clay_String keytext = (Clay_String) {\n        .length = keylen,\n        .chars = key,\n    };\n    Clay_String valtext = (Clay_String) {\n        .length = vallen,\n        .chars = value,\n    };\n    Clay_TextElementConfig *textconfig =\n        CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } });\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = {\n                .width = {\n                    .size.minMax = {\n                        .min = strlen(\"Border chars CLAY_TB_IMAGE_MODE_UNICODE_FAST\") * Clay_Termbox_Cell_Width(),\n                    }\n                },\n            }\n        },\n    }) {\n        CLAY_TEXT(keytext, textconfig);\n        CLAY_AUTO_ID({ .layout = { .sizing = CLAY_SIZING_GROW(1) } }) { }\n        CLAY_TEXT(valtext, textconfig);\n    }\n\n}\n\nvoid component_termbox_settings(void)\n{\n    CLAY(CLAY_ID(\"Termbox Settings\"), {\n        .floating = {\n            .attachTo = CLAY_ATTACH_TO_PARENT,\n            .zIndex = 1,\n            .attachPoints = { CLAY_ATTACH_POINT_CENTER_CENTER, CLAY_ATTACH_POINT_CENTER_TOP },\n            .offset = { 0, 0 }\n        },\n    }) {\n        CLAY_AUTO_ID({\n                .layout = {\n                    .sizing = CLAY_SIZING_FIT(),\n                    .padding = {\n                        6 * Clay_Termbox_Cell_Width(),\n                        6 * Clay_Termbox_Cell_Width(),\n                        2 * Clay_Termbox_Cell_Height(),\n                        2 * Clay_Termbox_Cell_Height(),\n                    }\n                },\n                .border = {\n                    .width = CLAY_BORDER_ALL(1),\n                    .color = { 0x00, 0x00, 0x00, 0xff }\n                },\n                .backgroundColor = { 0x7f, 0x00, 0x00, 0x7f }\n        }) {\n            const char *color_mode = NULL;\n            switch (clay_tb_color_mode) {\n                case TB_OUTPUT_NORMAL: {\n                    color_mode = \"TB_OUTPUT_NORMAL\";\n                    break;\n                }\n                case TB_OUTPUT_256: {\n                    color_mode = \"TB_OUTPUT_256\";\n                    break;\n                }\n                case TB_OUTPUT_216: {\n                    color_mode = \"TB_OUTPUT_216\";\n                    break;\n                }\n                case TB_OUTPUT_GRAYSCALE: {\n                    color_mode = \"TB_OUTPUT_GRAYSCALE\";\n                    break;\n                }\n                case TB_OUTPUT_TRUECOLOR: {\n                    color_mode = \"TB_OUTPUT_TRUECOLOR\";\n                    break;\n                }\n                case CLAY_TB_OUTPUT_NOCOLOR: {\n                    color_mode = \"CLAY_TB_OUTPUT_NOCOLOR\";\n                    break;\n                }\n                default: {\n                    color_mode = \"INVALID\";\n                    break;\n                }\n            }\n            const char *border_mode = NULL;\n            switch (clay_tb_border_mode) {\n                case CLAY_TB_BORDER_MODE_ROUND: {\n                    border_mode = \"CLAY_TB_BORDER_MODE_ROUND\";\n                    break;\n                }\n                case CLAY_TB_BORDER_MODE_MINIMUM: {\n                    border_mode = \"CLAY_TB_BORDER_MODE_MINIMUM\";\n                    break;\n                }\n                default: {\n                    border_mode = \"INVALID\";\n                    break;\n                }\n            }\n            const char *border_chars = NULL;\n            switch (clay_tb_border_chars) {\n                case CLAY_TB_BORDER_CHARS_ASCII: {\n                    border_chars = \"CLAY_TB_BORDER_CHARS_ASCII\";\n                    break;\n                }\n                case CLAY_TB_BORDER_CHARS_UNICODE: {\n                    border_chars = \"CLAY_TB_BORDER_CHARS_UNICODE\";\n                    break;\n                }\n                case CLAY_TB_BORDER_CHARS_BLANK: {\n                    border_chars = \"CLAY_TB_BORDER_CHARS_BLANK\";\n                    break;\n                }\n                case CLAY_TB_BORDER_CHARS_NONE: {\n                    border_chars = \"CLAY_TB_BORDER_CHARS_NONE\";\n                    break;\n                }\n                default: {\n                    border_chars = \"INVALID\";\n                    break;\n                }\n            }\n            const char *image_mode = NULL;\n            switch (clay_tb_image_mode) {\n                case CLAY_TB_IMAGE_MODE_PLACEHOLDER: {\n                    image_mode = \"CLAY_TB_IMAGE_MODE_PLACEHOLDER\";\n                    break;\n                }\n                case CLAY_TB_IMAGE_MODE_BG: {\n                    image_mode = \"CLAY_TB_IMAGE_MODE_BG\";\n                    break;\n                }\n                case CLAY_TB_IMAGE_MODE_ASCII_FG: {\n                    image_mode = \"CLAY_TB_IMAGE_MODE_ASCII_FG\";\n                    break;\n                }\n                case CLAY_TB_IMAGE_MODE_ASCII_FG_FAST: {\n                    image_mode = \"CLAY_TB_IMAGE_MODE_ASCII_FG_FAST\";\n                    break;\n                }\n                case CLAY_TB_IMAGE_MODE_ASCII: {\n                    image_mode = \"CLAY_TB_IMAGE_MODE_ASCII\";\n                    break;\n                }\n                case CLAY_TB_IMAGE_MODE_ASCII_FAST: {\n                    image_mode = \"CLAY_TB_IMAGE_MODE_ASCII_FAST\";\n                    break;\n                }\n                case CLAY_TB_IMAGE_MODE_UNICODE: {\n                    image_mode = \"CLAY_TB_IMAGE_MODE_UNICODE\";\n                    break;\n                }\n                case CLAY_TB_IMAGE_MODE_UNICODE_FAST: {\n                    image_mode = \"CLAY_TB_IMAGE_MODE_UNICODE_FAST\";\n                    break;\n                }\n                default: {\n                    image_mode = \"INVALID\";\n                    break;\n                }\n            }\n            const char *transparency = NULL;\n            if (clay_tb_transparency) {\n                transparency = \"true\";\n            } else {\n                transparency = \"false\";\n            }\n\n            CLAY_AUTO_ID({\n                .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },\n            }) {\n                component_text_pair(\"Color mode\", color_mode);\n                component_text_pair(\"Border mode\", border_mode);\n                component_text_pair(\"Border chars\", border_chars);\n                component_text_pair(\"Image mode\", image_mode);\n                component_text_pair(\"Transparency\", transparency);\n            }\n        }\n    }\n}\n\nvoid component_color_palette(void)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .childGap = 16,\n            .padding = {\n                2 * Clay_Termbox_Cell_Width(),\n                2 * Clay_Termbox_Cell_Width(),\n                2 * Clay_Termbox_Cell_Height(),\n                2 * Clay_Termbox_Cell_Height(),\n            }\n        },\n        .border = {\n            .width = CLAY_BORDER_OUTSIDE(2),\n            .color = { 0x00, 0x00, 0x00, 0xff }\n        },\n        .backgroundColor = { 0x7f, 0x7f, 0x7f, 0xff }\n    }) {\n        for (int type = 0; type < 2; ++type) {\n            CLAY_AUTO_ID({\n                .layout ={\n                    .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                    .sizing = CLAY_SIZING_FIT(),\n                    .childGap = Clay_Termbox_Cell_Height()\n                },\n            }) {\n                for (float ri = 0; ri < 4; ri += 1) {\n                    CLAY_AUTO_ID({\n                        .layout ={\n                            .sizing = CLAY_SIZING_FIT(),\n                            .childGap = Clay_Termbox_Cell_Width()\n                        },\n                    }) {\n                        for (float r = ri * 0x44; r < (ri + 1) * 0x44; r += 0x22) {\n                            CLAY_AUTO_ID({\n                                .layout ={\n                                    .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                                    .sizing = CLAY_SIZING_FIT(),\n                                },\n                            }) {\n                                for (float g = 0; g < 0xff; g += 0x22) {\n                                    CLAY_AUTO_ID({\n                                        .layout ={\n                                            .sizing = CLAY_SIZING_FIT(),\n                                        },\n                                    }) {\n                                        for (float b = 0; b < 0xff; b += 0x22) {\n                                            Clay_Color color = { r, g, b, 0x7f };\n\n                                            Clay_LayoutConfig layout = (Clay_LayoutConfig) {\n                                                .sizing = {\n                                                    .width = CLAY_SIZING_FIXED(2 * Clay_Termbox_Cell_Width()),\n                                                    .height = CLAY_SIZING_FIXED(1 * Clay_Termbox_Cell_Height())\n                                                }\n                                            };\n                                            if (0 == type) {\n                                                CLAY_AUTO_ID({\n                                                    .layout = layout,\n                                                    .backgroundColor = color\n                                                }) {}\n                                            } else if (1 == type) {\n                                                CLAY_AUTO_ID({\n                                                    .layout = layout,\n                                                }) {\n                                                    CLAY_TEXT(CLAY_STRING(\"#\"), CLAY_TEXT_CONFIG({ .textColor = color }));\n                                                }\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\nvoid component_unicode_text(void)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = CLAY_SIZING_FIT(),\n            .padding = {\n                2 * Clay_Termbox_Cell_Width(),\n                2 * Clay_Termbox_Cell_Width(),\n                2 * Clay_Termbox_Cell_Height(),\n                2 * Clay_Termbox_Cell_Height(),\n            }\n        },\n        .backgroundColor = { 0xcc, 0xbb, 0xaa, 0xff },\n        .border = {\n            // This border should still be displayed in CLAY_TB_BORDER_MODE_ROUND mode\n            .width = {\n                0.75 * Clay_Termbox_Cell_Width(),\n                0.75 * Clay_Termbox_Cell_Width(),\n                0.75 * Clay_Termbox_Cell_Height(),\n                0.75 * Clay_Termbox_Cell_Height(),\n            },\n            .color = { 0x33, 0x33, 0x33, 0xff },\n        },\n    }) {\n        CLAY_TEXT(\n            CLAY_STRING(\"Non-ascii character tests:\\n\"\n                \"\\n\"\n                \"(from https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html)\\n\"\n                \" Mathematics and Sciences:\\n\"\n                \"  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i), ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),\\n\"\n                \"  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (A ⇔ B),\\n\"\n                \"  2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm\\n\"\n                \"\\n\"\n                \" Compact font selection example text:\\n\"\n                \"  ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789\\n\"\n                \"  abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ\\n\"\n                \"  –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд\\n\"\n                \"  ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ ﬁ�⑀₂ἠḂӥẄɐː⍎אԱა\\n\"\n                \"\\n\"\n                \"(from https://blog.denisbider.com/2015/09/when-monospace-fonts-arent-unicode.html):\\n\"\n                \" aeioucsz\\n\"\n                \" áéíóúčšž\\n\"\n                \" 台北1234\\n\"\n                \" ＱＲＳ12\\n\"\n                \" ｱｲｳ1234\\n\"\n                \"\\n\"\n                \"(from https://stackoverflow.com/a/1644280)\\n\"\n                \" ٩(-̮̮̃-̃)۶ ٩(●̮̮̃•̃)۶ ٩(͡๏̯͡๏)۶ ٩(-̮̮̃•̃).\"\n            ),\n            CLAY_TEXT_CONFIG({ .textColor = { 0x11, 0x11, 0x11, 0xff } })\n        );\n    }\n}\n\nvoid component_keybinds(void)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = CLAY_SIZING_FIT(),\n            .padding = {\n                4 * Clay_Termbox_Cell_Width(),\n                4 * Clay_Termbox_Cell_Width(),\n                2 * Clay_Termbox_Cell_Height(),\n                2 * Clay_Termbox_Cell_Height(),\n            }\n        },\n        .backgroundColor = { 0x00, 0x7f, 0x7f, 0xff }\n    }) {\n        CLAY_TEXT(\n            CLAY_STRING(\n                \"Termbox2 renderer test\\n\"\n                \"\\n\"\n                \"Keybinds:\\n\"\n                \"  c/C - Cycle through color modes\\n\"\n                \"  b/B - Cycle through border modes\\n\"\n                \"  h/H - Cycle through border characters\\n\"\n                \"  i/I - Cycle through image modes\\n\"\n                \"  t/T - Toggle transparency\\n\"\n                \"  d/D - Toggle debug mode\\n\"\n                \"  q/Q - Quit\\n\"\n            ),\n            CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff }})\n        );\n    }\n}\n\nvoid component_image(clay_tb_image *image, int width)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = {\n                .width = (0 == width) ? CLAY_SIZING_GROW() : CLAY_SIZING_FIXED(width),\n            },\n        },\n        .image = {\n            .imageData = image,\n        },\n        .aspectRatio = { 512.0 / 406.0 }\n    }) { }\n}\n\nvoid component_mouse_data(void)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = {\n                .width = CLAY_SIZING_GROW(),\n            },\n        },\n    }) {\n        Clay_Context* context = Clay_GetCurrentContext();\n        Clay_Vector2 mouse_position = context->pointerInfo.position;\n\n        Clay_LayoutConfig layout = (Clay_LayoutConfig) {\n            .sizing = {\n                .width = CLAY_SIZING_FIXED(2 * Clay_Termbox_Cell_Width()),\n                .height = CLAY_SIZING_FIXED(1 * Clay_Termbox_Cell_Height())\n            }\n        };\n\n        float v = 255 * mouse_position.x / Clay_Termbox_Width();\n        v = (0 > v) ? 0 : v;\n        v = (255 < v) ? 255 : v;\n        Clay_Color color = (Clay_Color) { v, v, v, 0xff };\n\n        CLAY_AUTO_ID({\n            .layout = layout,\n            .backgroundColor = color\n        }) {}\n\n        v = 255 * mouse_position.y / Clay_Termbox_Height();\n        v = (0 > v) ? 0 : v;\n        v = (255 < v) ? 255 : v;\n        color = (Clay_Color) { v, v, v, 0xff };\n\n\n        CLAY_AUTO_ID({\n            .layout = layout,\n            .backgroundColor = color\n        }) {}\n\n    }\n}\n\nvoid component_bordered_text(void)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .layoutDirection = CLAY_TOP_TO_BOTTOM,\n            .sizing = {\n                .width = CLAY_SIZING_FIT(450),\n                .height = CLAY_SIZING_FIT(),\n            },\n            .padding = CLAY_PADDING_ALL(32)\n        },\n        .backgroundColor = { 0x24, 0x55, 0x34, 0xff },\n    }) {\n        CLAY_AUTO_ID({\n            .border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0xaa, 0x00, 0x00, 0xff } },\n        }) {\n            CLAY_TEXT(\n                CLAY_STRING(\"Test\"), CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));\n        }\n        CLAY_AUTO_ID({\n            .border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0x00, 0xaa, 0x00, 0xff } },\n        }) {\n            CLAY_TEXT(CLAY_STRING(\"of the border width\"),\n                CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));\n        }\n        CLAY_AUTO_ID({\n            .border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0x00, 0x00, 0xaa, 0xff } },\n        }) {\n            CLAY_TEXT(CLAY_STRING(\"and overlap for multiple lines\\nof text\"),\n                CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));\n        }\n        CLAY_AUTO_ID({\n            .border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0x00, 0x00, 0xaa, 0xff } },\n        }) {\n            CLAY_TEXT(CLAY_STRING(\"this text\\nis long enough\\nto display all\\n borders\\naround it\"),\n                CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));\n        }\n    }\n}\n\nClay_RenderCommandArray CreateLayout(clay_tb_image *image1, clay_tb_image *image2)\n{\n    Clay_BeginLayout();\n    CLAY_AUTO_ID({\n        .clip = {\n            .vertical = false,\n            .horizontal = true,\n            .childOffset = Clay_GetScrollOffset(),\n        },\n        .layout = {\n            .sizing = {\n                .width = CLAY_SIZING_GROW(),\n                .height = CLAY_SIZING_GROW()\n            },\n            .childAlignment = {\n                .x = CLAY_ALIGN_X_LEFT,\n                .y = CLAY_ALIGN_Y_CENTER\n            },\n            .childGap = 64\n        },\n        .backgroundColor = { 0x24, 0x24, 0x24, 0xff }\n    }) {\n        CLAY_AUTO_ID({\n            .layout = {\n                .childAlignment = {\n                    .x = CLAY_ALIGN_X_RIGHT,\n                },\n                .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                .sizing = CLAY_SIZING_FIT(),\n            },\n        }) {\n            component_keybinds();\n            component_unicode_text();\n        }\n        CLAY_AUTO_ID({\n            .layout = {\n                .layoutDirection = CLAY_TOP_TO_BOTTOM,\n                .childGap = 32,\n                .sizing = CLAY_SIZING_FIT(),\n            },\n        }) {\n            component_termbox_settings();\n            component_image(image1, 150);\n            component_image(image2, 0);\n            component_mouse_data();\n            component_bordered_text();\n        }\n\n        component_color_palette();\n    }\n    return Clay_EndLayout();\n}\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Interactive functions\n\nvoid handle_clay_errors(Clay_ErrorData errorData)\n{\n    Clay_Termbox_Close();\n    fprintf(stderr, \"%s\", errorData.errorText.chars);\n    exit(1);\n}\n\n/**\n  Process events received from termbox2 and handle interaction\n */\nvoid handle_termbox_events(void)\n{\n    // Wait up to 100ms for an event (key/mouse press, terminal resize) before continuing\n    // If an event is already available, this doesn't wait. Will not wait due to the previous call\n    // to termbox_waitfor_event. Increasing the wait time reduces load without reducing\n    // responsiveness (but will of course prevent other code from running on this thread while it's\n    // waiting)\n    struct tb_event evt;\n    int ms_to_wait = 0;\n    int err = tb_peek_event(&evt, ms_to_wait);\n\n    switch (err) {\n        default:\n        case TB_ERR_NO_EVENT: {\n            break;\n        }\n        case TB_ERR_POLL: {\n            if (EINTR != tb_last_errno()) {\n                Clay_Termbox_Close();\n                fprintf(stderr, \"Failed to read event from TTY\\n\");\n                exit(1);\n            }\n            break;\n        }\n        case TB_OK: {\n            switch (evt.type) {\n                case TB_EVENT_RESIZE: {\n                    Clay_SetLayoutDimensions((Clay_Dimensions) {\n                        Clay_Termbox_Width(),\n                        Clay_Termbox_Height()\n                    });\n                    break;\n                }\n                case TB_EVENT_KEY: {\n                    if (TB_KEY_CTRL_C == evt.key) {\n                        end_loop = true;\n                        break;\n                    }\n                    switch (evt.ch) {\n                        case 'q':\n                        case 'Q': {\n                            end_loop = true;\n                            break;\n                        }\n                        case 'd':\n                        case 'D': {\n                            debugMode = !debugMode;\n                            Clay_SetDebugModeEnabled(debugMode);\n                            break;\n                        }\n                        case 'c': {\n                            int new_mode = clay_tb_color_mode - 1;\n                            new_mode = (0 <= new_mode) ? new_mode : TB_OUTPUT_TRUECOLOR;\n                            Clay_Termbox_Set_Color_Mode(new_mode);\n                            break;\n                        }\n                        case 'C': {\n                            int new_mode = (clay_tb_color_mode + 1) % (TB_OUTPUT_TRUECOLOR + 1);\n                            Clay_Termbox_Set_Color_Mode(new_mode);\n                            break;\n                        }\n                        case 'b': {\n                            enum border_mode new_mode = clay_tb_border_mode - 1;\n                            new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)\n                                ? new_mode\n                                : CLAY_TB_BORDER_MODE_MINIMUM;\n                            Clay_Termbox_Set_Border_Mode(new_mode);\n                            break;\n                        }\n                        case 'B': {\n                            enum border_mode new_mode = (clay_tb_border_mode + 1)\n                                % (CLAY_TB_BORDER_MODE_MINIMUM + 1);\n                            new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)\n                                ? new_mode\n                                : CLAY_TB_BORDER_MODE_ROUND;\n                            Clay_Termbox_Set_Border_Mode(new_mode);\n                            break;\n                        }\n                        case 'h': {\n                            enum border_chars new_chars = clay_tb_border_chars - 1;\n                            new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)\n                                ? new_chars\n                                : CLAY_TB_BORDER_CHARS_NONE;\n                            Clay_Termbox_Set_Border_Chars(new_chars);\n                            break;\n                        }\n                        case 'H': {\n                            enum border_chars new_chars\n                                = (clay_tb_border_chars + 1) % (CLAY_TB_BORDER_CHARS_NONE + 1);\n                            new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)\n                                ? new_chars\n                                : CLAY_TB_BORDER_CHARS_ASCII;\n                            Clay_Termbox_Set_Border_Chars(new_chars);\n                            break;\n                        }\n                        case 'i': {\n                            enum image_mode new_mode = clay_tb_image_mode - 1;\n                            new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)\n                                ? new_mode\n                                : CLAY_TB_IMAGE_MODE_UNICODE_FAST;\n                            Clay_Termbox_Set_Image_Mode(new_mode);\n                            break;\n                        }\n                        case 'I': {\n                            enum image_mode new_mode = (clay_tb_image_mode + 1)\n                                % (CLAY_TB_IMAGE_MODE_UNICODE_FAST + 1);\n                            new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)\n                                ? new_mode\n                                : CLAY_TB_IMAGE_MODE_PLACEHOLDER;\n                            Clay_Termbox_Set_Image_Mode(new_mode);\n                            break;\n                        }\n                        case 't':\n                        case 'T': {\n                            Clay_Termbox_Set_Transparency(!clay_tb_transparency);\n                        }\n                    }\n                    break;\n                }\n                case TB_EVENT_MOUSE: {\n                    Clay_Vector2 mousePosition = {\n                        (float)evt.x * Clay_Termbox_Cell_Width(),\n                        (float)evt.y * Clay_Termbox_Cell_Height()\n                    };\n\n                    // Mouse release events may not be produced by all terminals, and will\n                    // be sent during hover, so can't be used to detect when the mouse has\n                    // been released\n\n                    switch (evt.key) {\n                        case TB_KEY_MOUSE_LEFT: {\n                            Clay_SetPointerState(mousePosition, true);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_RIGHT: {\n                            Clay_SetPointerState(mousePosition, false);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_MIDDLE: {\n                            Clay_SetPointerState(mousePosition, false);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_RELEASE: {\n                            Clay_SetPointerState(mousePosition, false);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_WHEEL_UP: {\n                            Clay_Vector2 scrollDelta = { 0.5 * Clay_Termbox_Cell_Width(), 0 };\n                            Clay_UpdateScrollContainers(false, scrollDelta, 1);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_WHEEL_DOWN: {\n                            Clay_Vector2 scrollDelta = { -0.5 * Clay_Termbox_Cell_Width(), 0 };\n                            Clay_UpdateScrollContainers(false, scrollDelta, 1);\n                            break;\n                        }\n                        default: {\n                            break;\n                        }\n                    }\n                    break;\n                }\n                default: {\n                    break;\n                }\n            }\n            break;\n        }\n    }\n}\n\nint main(void)\n{\n    clay_tb_image shark_image1 = Clay_Termbox_Image_Load_File(\"resources/512px-Shark_antwerp_zoo.jpeg\");\n    clay_tb_image shark_image2 = Clay_Termbox_Image_Load_File(\"resources/512px-Shark_antwerp_zoo.jpeg\");\n    if (NULL == shark_image1.pixel_data) { exit(1); }\n    if (NULL == shark_image2.pixel_data) { exit(1); }\n\n    int num_elements = 3 * 8192;\n    Clay_SetMaxElementCount(num_elements);\n\n    uint64_t size = Clay_MinMemorySize();\n    void *memory = malloc(size);\n    if (NULL == memory) { exit(1); }\n\n    Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(size, memory);\n\n    Clay_Termbox_Initialize(\n        TB_OUTPUT_256, CLAY_TB_BORDER_MODE_DEFAULT, CLAY_TB_BORDER_CHARS_DEFAULT, CLAY_TB_IMAGE_MODE_DEFAULT, false);\n\n    Clay_Initialize(clay_arena, (Clay_Dimensions) { Clay_Termbox_Width(), Clay_Termbox_Height() },\n        (Clay_ErrorHandler) { handle_clay_errors, NULL });\n\n    Clay_SetMeasureTextFunction(Clay_Termbox_MeasureText, NULL);\n\n    // Initial render before waiting for events\n    Clay_RenderCommandArray commands = CreateLayout(&shark_image1, &shark_image2);\n    Clay_Termbox_Render(commands);\n    tb_present();\n\n    while (!end_loop) {\n        // Block until event is available. Optional, but reduces load since this demo is purely\n        // synchronous to user input.\n        Clay_Termbox_Waitfor_Event();\n\n        handle_termbox_events();\n\n        commands = CreateLayout(&shark_image1, &shark_image2);\n\n        tb_clear();\n        Clay_Termbox_Render(commands);\n        tb_present();\n    }\n\n    Clay_Termbox_Close();\n    Clay_Termbox_Image_Free(&shark_image1);\n    Clay_Termbox_Image_Free(&shark_image2);\n    free(memory);\n    return 0;\n}\n"
  },
  {
    "path": "examples/termbox2-demo/readme.md",
    "content": "# Termbox2 renderer demo\n\nTerminal-based renderer using [termbox2](https://github.com/termbox/termbox2)\n\nThis demo shows a color palette and a few different components. It allows\nchanging configuration settings for colors, border size rounding mode,\ncharacters used for borders, and transparency.\n\n```\nKeybinds:\nc/C - Cycle through color modes\nb/B - Cycle through border modes\nh/H - Cycle through border characters\ni/I - Cycle through image modes\nt/T - Toggle transparency\nd/D - Toggle debug mode\nq/Q - Quit\n```\n\nConfiguration can be also be overriden by environment variables:\n- `CLAY_TB_COLOR_MODE`\n  - `NORMAL`\n  - `256`\n  - `216`\n  - `GRAYSCALE`\n  - `TRUECOLOR`\n  - `NOCOLOR`\n- `CLAY_TB_BORDER_CHARS`\n  - `DEFAULT`\n  - `ASCII`\n  - `UNICODE`\n  - `NONE`\n- `CLAY_TB_IMAGE_MODE`\n  - `DEFAULT`\n  - `PLACEHOLDER`\n  - `BG`\n  - `ASCII_FG`\n  - `ASCII`\n  - `UNICODE`\n  - `ASCII_FG_FAST`\n  - `ASCII_FAST`\n  - `UNICODE_FAST`\n- `CLAY_TB_TRANSPARENCY`\n  - `1`\n  - `0`\n- `CLAY_TB_CELL_PIXELS`\n  - `widthxheight`\n\n## Building\n\nBuild the binary with cmake\n\n```sh\nmkdir build\ncd build\ncmake ..\nmake\n```\n\nThen run the executable:\n\n```sh\n./clay_examples_termbox2_demo\n```\n\n## Attributions\n\nResources used:\n- `512px-Shark_antwerp_zoo.jpeg`\n  - Retrieved from <https://commons.wikimedia.org/wiki/File:Shark_antwerp_zoo.jpg>\n  - License: [Creative Commons Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/deed.en)\n  - No changes made\n"
  },
  {
    "path": "examples/termbox2-image-demo/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.25)\nproject(clay_examples_termbox2_image_demo C)\nset(CMAKE_C_STANDARD 11)\nset(CMAKE_BUILD_TYPE Release)\nset(CMAKE_C_FLAGS_RELEASE \"-O3\")\n\ninclude(FetchContent)\nset(FETCHCONTENT_QUIET FALSE)\n\nFetchContent_Declare(\n    termbox2\n    GIT_REPOSITORY \"https://github.com/termbox/termbox2.git\"\n    GIT_TAG \"ffd159c2a6106dd5eef338a6702ad15d4d4aa809\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(termbox2)\n\nFetchContent_Declare(\n    stb\n    GIT_REPOSITORY \"https://github.com/nothings/stb.git\"\n    GIT_TAG \"fede005abaf93d9d7f3a679d1999b2db341b360f\"\n    GIT_PROGRESS TRUE\n    GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(stb)\n\nadd_executable(clay_examples_termbox2_image_demo main.c)\n\ntarget_compile_options(clay_examples_termbox2_image_demo PUBLIC)\ntarget_include_directories(clay_examples_termbox2_image_demo PUBLIC . PRIVATE ${termbox2_SOURCE_DIR} PRIVATE ${stb_SOURCE_DIR})\ntarget_link_libraries(clay_examples_termbox2_image_demo PRIVATE m) # Used by stb_image.h\n\nadd_custom_command(\n        TARGET clay_examples_termbox2_image_demo POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy_directory\n        ${CMAKE_CURRENT_SOURCE_DIR}/resources\n        ${CMAKE_CURRENT_BINARY_DIR}/resources)\n"
  },
  {
    "path": "examples/termbox2-image-demo/main.c",
    "content": "/*\n    Unlicense\n\n    Copyright (c) 2025 Mivirl\n\n    This is free and unencumbered software released into the public domain.\n\n    Anyone is free to copy, modify, publish, use, compile, sell, or\n    distribute this software, either in source code form or as a compiled\n    binary, for any purpose, commercial or non-commercial, and by any\n    means.\n\n    In jurisdictions that recognize copyright laws, the author or authors\n    of this software dedicate any and all copyright interest in the\n    software to the public domain. We make this dedication for the benefit\n    of the public at large and to the detriment of our heirs and\n    successors. We intend this dedication to be an overt act of\n    relinquishment in perpetuity of all present and future rights to this\n    software under copyright law.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n    IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n    OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n    OTHER DEALINGS IN THE SOFTWARE.\n\n    For more information, please refer to <https://unlicense.org/>\n*/\n\n#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n#include \"../../renderers/termbox2/clay_renderer_termbox2.c\"\n\n#define TB_IMPL\n#include \"termbox2.h\"\n\n#define STB_IMAGE_IMPLEMENTATION\n#include \"stb_image.h\"\n\n#define STB_IMAGE_RESIZE_IMPLEMENTATION\n#include \"stb_image_resize2.h\"\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Data structures\n\nstruct img_group {\n    clay_tb_image thumbnail;\n    clay_tb_image image;\n    clay_tb_image image_1;\n    clay_tb_image image_2;\n    int width;\n    int height;\n};\ntypedef struct img_group img_group;\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Internal state\n\n// If the program should exit the main render/interaction loop\nbool end_loop = false;\n\n// If the debug tools should be displayed\nbool debugMode = false;\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Internal utility functions\n\nimg_group img_group_load(const char *filename)\n{\n    img_group rv;\n    rv.thumbnail = Clay_Termbox_Image_Load_File(filename);\n    rv.image = Clay_Termbox_Image_Load_File(filename);\n    rv.image_1 = Clay_Termbox_Image_Load_File(filename);\n    rv.image_2 = Clay_Termbox_Image_Load_File(filename);\n    if (NULL == rv.thumbnail.pixel_data\n        || NULL == rv.image.pixel_data\n        || NULL == rv.image_1.pixel_data\n        || NULL == rv.image_2.pixel_data) {\n        exit(1);\n    }\n    rv.width = rv.image.pixel_width;\n    rv.height = rv.image.pixel_height;\n    return rv;\n}\nvoid img_group_free(img_group *img)\n{\n    Clay_Termbox_Image_Free(&img->thumbnail);\n    Clay_Termbox_Image_Free(&img->image);\n    Clay_Termbox_Image_Free(&img->image_1);\n    Clay_Termbox_Image_Free(&img->image_2);\n}\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Clay components\n\nvoid component_text_pair(const char *key, const char *value)\n{\n    size_t keylen = strlen(key);\n    size_t vallen = strlen(value);\n    Clay_String keytext = (Clay_String) {\n        .length = keylen,\n        .chars = key,\n    };\n    Clay_String valtext = (Clay_String) {\n        .length = vallen,\n        .chars = value,\n    };\n    Clay_TextElementConfig *textconfig =\n        CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } });\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = {\n                .width = {\n                    .size.minMax = {\n                        .min = strlen(\"Border chars CLAY_TB_IMAGE_MODE_UNICODE_FAST\") * Clay_Termbox_Cell_Width(),\n                    }\n                },\n            }\n        },\n    }) {\n        CLAY_TEXT(keytext, textconfig);\n        CLAY_AUTO_ID({ .layout = { .sizing = CLAY_SIZING_GROW(1) } }) { }\n        CLAY_TEXT(valtext, textconfig);\n    }\n\n}\n\nvoid component_termbox_settings(void)\n{\n    CLAY_AUTO_ID({\n            .layout = {\n                .sizing = CLAY_SIZING_FIT(),\n                .padding = {\n                    6 * Clay_Termbox_Cell_Width(),\n                    6 * Clay_Termbox_Cell_Width(),\n                    2 * Clay_Termbox_Cell_Height(),\n                    2 * Clay_Termbox_Cell_Height(),\n                }\n            },\n            .border = {\n                .width = CLAY_BORDER_ALL(1),\n                .color = { 0x00, 0x00, 0x00, 0xff }\n            },\n            .backgroundColor = { 0x7f, 0x00, 0x00, 0x7f }\n    }) {\n        const char *color_mode = NULL;\n        switch (clay_tb_color_mode) {\n            case TB_OUTPUT_NORMAL: {\n                color_mode = \"TB_OUTPUT_NORMAL\";\n                break;\n            }\n            case TB_OUTPUT_256: {\n                color_mode = \"TB_OUTPUT_256\";\n                break;\n            }\n            case TB_OUTPUT_216: {\n                color_mode = \"TB_OUTPUT_216\";\n                break;\n            }\n            case TB_OUTPUT_GRAYSCALE: {\n                color_mode = \"TB_OUTPUT_GRAYSCALE\";\n                break;\n            }\n            case TB_OUTPUT_TRUECOLOR: {\n                color_mode = \"TB_OUTPUT_TRUECOLOR\";\n                break;\n            }\n            case CLAY_TB_OUTPUT_NOCOLOR: {\n                color_mode = \"CLAY_TB_OUTPUT_NOCOLOR\";\n                break;\n            }\n            default: {\n                color_mode = \"INVALID\";\n                break;\n            }\n        }\n        const char *border_mode = NULL;\n        switch (clay_tb_border_mode) {\n            case CLAY_TB_BORDER_MODE_ROUND: {\n                border_mode = \"CLAY_TB_BORDER_MODE_ROUND\";\n                break;\n            }\n            case CLAY_TB_BORDER_MODE_MINIMUM: {\n                border_mode = \"CLAY_TB_BORDER_MODE_MINIMUM\";\n                break;\n            }\n            default: {\n                border_mode = \"INVALID\";\n                break;\n            }\n        }\n        const char *border_chars = NULL;\n        switch (clay_tb_border_chars) {\n            case CLAY_TB_BORDER_CHARS_ASCII: {\n                border_chars = \"CLAY_TB_BORDER_CHARS_ASCII\";\n                break;\n            }\n            case CLAY_TB_BORDER_CHARS_UNICODE: {\n                border_chars = \"CLAY_TB_BORDER_CHARS_UNICODE\";\n                break;\n            }\n            case CLAY_TB_BORDER_CHARS_BLANK: {\n                border_chars = \"CLAY_TB_BORDER_CHARS_BLANK\";\n                break;\n            }\n            case CLAY_TB_BORDER_CHARS_NONE: {\n                border_chars = \"CLAY_TB_BORDER_CHARS_NONE\";\n                break;\n            }\n            default: {\n                border_chars = \"INVALID\";\n                break;\n            }\n        }\n        const char *image_mode = NULL;\n        switch (clay_tb_image_mode) {\n            case CLAY_TB_IMAGE_MODE_PLACEHOLDER: {\n                image_mode = \"CLAY_TB_IMAGE_MODE_PLACEHOLDER\";\n                break;\n            }\n            case CLAY_TB_IMAGE_MODE_BG: {\n                image_mode = \"CLAY_TB_IMAGE_MODE_BG\";\n                break;\n            }\n            case CLAY_TB_IMAGE_MODE_ASCII_FG: {\n                image_mode = \"CLAY_TB_IMAGE_MODE_ASCII_FG\";\n                break;\n            }\n            case CLAY_TB_IMAGE_MODE_ASCII_FG_FAST: {\n                image_mode = \"CLAY_TB_IMAGE_MODE_ASCII_FG_FAST\";\n                break;\n            }\n            case CLAY_TB_IMAGE_MODE_ASCII: {\n                image_mode = \"CLAY_TB_IMAGE_MODE_ASCII\";\n                break;\n            }\n            case CLAY_TB_IMAGE_MODE_ASCII_FAST: {\n                image_mode = \"CLAY_TB_IMAGE_MODE_ASCII_FAST\";\n                break;\n            }\n            case CLAY_TB_IMAGE_MODE_UNICODE: {\n                image_mode = \"CLAY_TB_IMAGE_MODE_UNICODE\";\n                break;\n            }\n            case CLAY_TB_IMAGE_MODE_UNICODE_FAST: {\n                image_mode = \"CLAY_TB_IMAGE_MODE_UNICODE_FAST\";\n                break;\n            }\n            default: {\n                image_mode = \"INVALID\";\n                break;\n            }\n        }\n        const char *transparency = NULL;\n        if (clay_tb_transparency) {\n            transparency = \"true\";\n        } else {\n            transparency = \"false\";\n        }\n\n        CLAY_AUTO_ID({\n            .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },\n        }) {\n            component_text_pair(\"Color mode\", color_mode);\n            component_text_pair(\"Border mode\", border_mode);\n            component_text_pair(\"Border chars\", border_chars);\n            component_text_pair(\"Image mode\", image_mode);\n            component_text_pair(\"Transparency\", transparency);\n        }\n    }\n}\n\nvoid component_keybinds(void)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = CLAY_SIZING_FIT(),\n            .padding = {\n                4 * Clay_Termbox_Cell_Width(),\n                4 * Clay_Termbox_Cell_Width(),\n                2 * Clay_Termbox_Cell_Height(),\n                2 * Clay_Termbox_Cell_Height(),\n            }\n        },\n        .backgroundColor = { 0x00, 0x7f, 0x7f, 0xff }\n    }) {\n        CLAY_TEXT(\n            CLAY_STRING(\n                \"Termbox2 renderer test\\n\"\n                \"\\n\"\n                \"Keybinds:\\n\"\n                \"  up/down arrows - Change selected image\\n\"\n                \"  c/C - Cycle through color modes\\n\"\n                \"  b/B - Cycle through border modes\\n\"\n                \"  h/H - Cycle through border characters\\n\"\n                \"  i/I - Cycle through image modes\\n\"\n                \"  t/T - Toggle transparency\\n\"\n                \"  d/D - Toggle debug mode\\n\"\n                \"  q/Q - Quit\\n\"\n            ),\n            CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff }})\n        );\n    }\n}\n\nvoid component_image(img_group *img_pair)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = {\n                .width = CLAY_SIZING_GROW(),\n                .height = CLAY_SIZING_GROW()\n            },\n            .layoutDirection = CLAY_TOP_TO_BOTTOM,\n            .childAlignment = {\n                .x = CLAY_ALIGN_X_CENTER,\n                .y = CLAY_ALIGN_Y_CENTER\n            },\n            .childGap = 1 * Clay_Termbox_Cell_Height()\n        },\n        .backgroundColor = { 0x24, 0x24, 0x24, 0xff }\n    }) {\n        CLAY_AUTO_ID({\n            .layout = {\n                .sizing = {\n                    .width = CLAY_SIZING_GROW(),\n                },\n            },\n            .image = {\n                .imageData = &img_pair->image,\n            },\n            .aspectRatio = { (float)img_pair->width / img_pair->height }\n        }) { }\n        component_keybinds();\n    }\n}\n\nvoid component_image_small(img_group **img_pairs, int count, int selected_index)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = {\n                .width = CLAY_SIZING_PERCENT(0.25),\n            },\n            .layoutDirection = CLAY_TOP_TO_BOTTOM,\n            .childGap = 20,\n            .childAlignment = {\n                .x = CLAY_ALIGN_X_CENTER,\n                .y = CLAY_ALIGN_Y_CENTER\n            },\n        },\n    }) {\n        CLAY_AUTO_ID({\n            .layout = {\n                .sizing = {\n                    .width = CLAY_SIZING_PERCENT(0.7),\n                },\n            },\n            .image = {\n                .imageData = &img_pairs[selected_index]->image_1,\n            },\n            .aspectRatio = { (float)img_pairs[selected_index]->width / img_pairs[selected_index]->height }\n        }) { }\n        CLAY_AUTO_ID({\n            .layout = {\n                .sizing = {\n                    .width = CLAY_SIZING_GROW(),\n                },\n            },\n            .image = {\n                .imageData = &img_pairs[selected_index]->image_2,\n            },\n            .aspectRatio = { (float)img_pairs[selected_index]->width / img_pairs[selected_index]->height }\n        }) { }\n        component_termbox_settings();\n    }\n}\n\nvoid component_thumbnails(img_group **img_pairs, int count, int selected_index)\n{\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = {\n                .width = CLAY_SIZING_PERCENT(0.1),\n                .height = CLAY_SIZING_GROW()\n            },\n            .layoutDirection = CLAY_TOP_TO_BOTTOM,\n            .childGap = 20\n        },\n        .backgroundColor = { 0x42, 0x42, 0x42, 0xff }\n    }) {\n        for (int i = 0; i < count; ++i) {\n            Clay_BorderElementConfig border;\n            if (i == selected_index) {\n                border = (Clay_BorderElementConfig) {\n                    .width =CLAY_BORDER_OUTSIDE(10),\n                    .color = { 0x00, 0x30, 0xc0, 0x8f }\n                };\n            } else {\n                border = (Clay_BorderElementConfig) {\n                    .width = CLAY_BORDER_OUTSIDE(0),\n                };\n            }\n            CLAY_AUTO_ID({\n                .layout = {\n                    .sizing = {\n                        .width = CLAY_SIZING_GROW(),\n                    },\n                },\n                .border = border,\n                .image = {\n                    .imageData = &img_pairs[i]->thumbnail,\n                },\n                .aspectRatio = { (float)img_pairs[i]->width / img_pairs[i]->height }\n            }) { }\n        }\n    }\n}\n\nint selected_thumbnail = 0;\nconst int thumbnail_count = 5;\nClay_RenderCommandArray CreateLayout(struct img_group **imgs)\n{\n    Clay_BeginLayout();\n    CLAY_AUTO_ID({\n        .layout = {\n            .sizing = {\n                .width = CLAY_SIZING_GROW(),\n                .height = CLAY_SIZING_GROW()\n            },\n            .childAlignment = {\n                .x = CLAY_ALIGN_X_LEFT,\n                .y = CLAY_ALIGN_Y_CENTER\n            },\n            .childGap = 64\n        },\n        .backgroundColor = { 0x24, 0x24, 0x24, 0xff }\n    }) {\n        component_thumbnails(imgs, thumbnail_count, selected_thumbnail);\n        component_image_small(imgs, thumbnail_count, selected_thumbnail);\n        component_image(imgs[selected_thumbnail]);\n    }\n    return Clay_EndLayout();\n}\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Interactive functions\n\nvoid handle_clay_errors(Clay_ErrorData errorData)\n{\n    Clay_Termbox_Close();\n    fprintf(stderr, \"%s\", errorData.errorText.chars);\n    exit(1);\n}\n\n/**\n  Process events received from termbox2 and handle interaction\n */\nvoid handle_termbox_events(void)\n{\n    // Wait up to 100ms for an event (key/mouse press, terminal resize) before continuing\n    // If an event is already available, this doesn't wait. Will not wait due to the previous call\n    // to termbox_waitfor_event. Increasing the wait time reduces load without reducing\n    // responsiveness (but will of course prevent other code from running on this thread while it's\n    // waiting)\n    struct tb_event evt;\n    int ms_to_wait = 0;\n    int err = tb_peek_event(&evt, ms_to_wait);\n\n    switch (err) {\n        default:\n        case TB_ERR_NO_EVENT: {\n            break;\n        }\n        case TB_ERR_POLL: {\n            if (EINTR != tb_last_errno()) {\n                Clay_Termbox_Close();\n                fprintf(stderr, \"Failed to read event from TTY\\n\");\n                exit(1);\n            }\n            break;\n        }\n        case TB_OK: {\n            switch (evt.type) {\n                case TB_EVENT_RESIZE: {\n                    Clay_SetLayoutDimensions((Clay_Dimensions) {\n                        Clay_Termbox_Width(),\n                        Clay_Termbox_Height()\n                    });\n                    break;\n                }\n                case TB_EVENT_KEY: {\n                    if (TB_KEY_CTRL_C == evt.key) {\n                        end_loop = true;\n                        break;\n                    }\n                    if (0 != evt.ch) {\n                        switch (evt.ch) {\n                            case 'q':\n                            case 'Q': {\n                                end_loop = true;\n                                break;\n                            }\n                            case 'd':\n                            case 'D': {\n                                debugMode = !debugMode;\n                                Clay_SetDebugModeEnabled(debugMode);\n                                break;\n                            }\n                            case 'c': {\n                                int new_mode = clay_tb_color_mode - 1;\n                                new_mode = (0 <= new_mode) ? new_mode : TB_OUTPUT_TRUECOLOR;\n                                Clay_Termbox_Set_Color_Mode(new_mode);\n                                break;\n                            }\n                            case 'C': {\n                                int new_mode = (clay_tb_color_mode + 1) % (TB_OUTPUT_TRUECOLOR + 1);\n                                Clay_Termbox_Set_Color_Mode(new_mode);\n                                break;\n                            }\n                            case 'b': {\n                                enum border_mode new_mode = clay_tb_border_mode - 1;\n                                new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)\n                                    ? new_mode\n                                    : CLAY_TB_BORDER_MODE_MINIMUM;\n                                Clay_Termbox_Set_Border_Mode(new_mode);\n                                break;\n                            }\n                            case 'B': {\n                                enum border_mode new_mode = (clay_tb_border_mode + 1)\n                                    % (CLAY_TB_BORDER_MODE_MINIMUM + 1);\n                                new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)\n                                    ? new_mode\n                                    : CLAY_TB_BORDER_MODE_ROUND;\n                                Clay_Termbox_Set_Border_Mode(new_mode);\n                                break;\n                            }\n                            case 'h': {\n                                enum border_chars new_chars = clay_tb_border_chars - 1;\n                                new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)\n                                    ? new_chars\n                                    : CLAY_TB_BORDER_CHARS_NONE;\n                                Clay_Termbox_Set_Border_Chars(new_chars);\n                                break;\n                            }\n                            case 'H': {\n                                enum border_chars new_chars\n                                    = (clay_tb_border_chars + 1) % (CLAY_TB_BORDER_CHARS_NONE + 1);\n                                new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)\n                                    ? new_chars\n                                    : CLAY_TB_BORDER_CHARS_ASCII;\n                                Clay_Termbox_Set_Border_Chars(new_chars);\n                                break;\n                            }\n                            case 'i': {\n                                enum image_mode new_mode = clay_tb_image_mode - 1;\n                                new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)\n                                    ? new_mode\n                                    : CLAY_TB_IMAGE_MODE_UNICODE_FAST;\n                                Clay_Termbox_Set_Image_Mode(new_mode);\n                                break;\n                            }\n                            case 'I': {\n                                enum image_mode new_mode = (clay_tb_image_mode + 1)\n                                    % (CLAY_TB_IMAGE_MODE_UNICODE_FAST + 1);\n                                new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)\n                                    ? new_mode\n                                    : CLAY_TB_IMAGE_MODE_PLACEHOLDER;\n                                Clay_Termbox_Set_Image_Mode(new_mode);\n                                break;\n                            }\n                            case 't':\n                            case 'T': {\n                                Clay_Termbox_Set_Transparency(!clay_tb_transparency);\n                            }\n                        }\n                    } else if (0 != evt.key) {\n                        switch (evt.key) {\n                            case TB_KEY_ARROW_UP: {\n                                selected_thumbnail = (selected_thumbnail > 0) ? selected_thumbnail - 1 : 0;\n                                break;\n                            }\n                            case TB_KEY_ARROW_DOWN: {\n                                selected_thumbnail = (selected_thumbnail < thumbnail_count - 1) ? selected_thumbnail + 1 : thumbnail_count - 1;\n                                break;\n                            }\n                        }\n                    }\n                    break;\n                }\n                case TB_EVENT_MOUSE: {\n                    Clay_Vector2 mousePosition = {\n                        (float)evt.x * Clay_Termbox_Cell_Width(),\n                        (float)evt.y * Clay_Termbox_Cell_Height()\n                    };\n\n                    // Mouse release events may not be produced by all terminals, and will\n                    // be sent during hover, so can't be used to detect when the mouse has\n                    // been released\n\n                    switch (evt.key) {\n                        case TB_KEY_MOUSE_LEFT: {\n                            Clay_SetPointerState(mousePosition, true);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_RIGHT: {\n                            Clay_SetPointerState(mousePosition, false);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_MIDDLE: {\n                            Clay_SetPointerState(mousePosition, false);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_RELEASE: {\n                            Clay_SetPointerState(mousePosition, false);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_WHEEL_UP: {\n                            Clay_Vector2 scrollDelta = { 0, 1 * Clay_Termbox_Cell_Height() };\n                            Clay_UpdateScrollContainers(false, scrollDelta, 1);\n                            break;\n                        }\n                        case TB_KEY_MOUSE_WHEEL_DOWN: {\n                            Clay_Vector2 scrollDelta = { 0, -1 * Clay_Termbox_Cell_Height() };\n                            Clay_UpdateScrollContainers(false, scrollDelta, 1);\n                            break;\n                        }\n                        default: {\n                            break;\n                        }\n                    }\n                    break;\n                }\n                default: {\n                    break;\n                }\n            }\n            break;\n        }\n    }\n}\n\nint main(void)\n{\n    img_group *imgs[thumbnail_count];\n    img_group img_shark = img_group_load(\"resources/512px-Shark_antwerp_zoo.jpeg\");\n    img_group img_castle = img_group_load(\"resources/512px-Balmoral_Castle_30_July_2011.jpeg\");\n    img_group img_dog = img_group_load(\"resources/512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg\");\n    img_group img_rosa = img_group_load(\"resources/512px-Rosa_Cubana_2018-09-21_1610.jpeg\");\n    img_group img_vorderer = img_group_load(\"resources/512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg\");\n    imgs[0] = &img_shark;\n    imgs[1] = &img_castle;\n    imgs[2] = &img_dog;\n    imgs[3] = &img_rosa;\n    imgs[4] = &img_vorderer;\n\n    int num_elements = 3 * 8192;\n    Clay_SetMaxElementCount(num_elements);\n\n    uint64_t size = Clay_MinMemorySize();\n    void *memory = malloc(size);\n    if (NULL == memory) { exit(1); }\n\n    Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(size, memory);\n\n    Clay_Termbox_Initialize(\n        TB_OUTPUT_256, CLAY_TB_BORDER_MODE_DEFAULT, CLAY_TB_BORDER_CHARS_DEFAULT, CLAY_TB_IMAGE_MODE_DEFAULT, false);\n\n    Clay_Initialize(clay_arena, (Clay_Dimensions) { Clay_Termbox_Width(), Clay_Termbox_Height() },\n        (Clay_ErrorHandler) { handle_clay_errors, NULL });\n\n    Clay_SetMeasureTextFunction(Clay_Termbox_MeasureText, NULL);\n\n    // Initial render before waiting for events\n    Clay_RenderCommandArray commands = CreateLayout(imgs);\n    Clay_Termbox_Render(commands);\n    tb_present();\n\n    while (!end_loop) {\n        // Block until event is available. Optional, but reduces load since this demo is purely\n        // synchronous to user input.\n        Clay_Termbox_Waitfor_Event();\n\n        handle_termbox_events();\n\n        commands = CreateLayout(imgs);\n\n        Clay_Termbox_Render(commands);\n        tb_present();\n    }\n\n    Clay_Termbox_Close();\n    img_group_free(&img_shark);\n    img_group_free(&img_castle);\n    img_group_free(&img_dog);\n    img_group_free(&img_rosa);\n    img_group_free(&img_vorderer);\n    free(memory);\n    return 0;\n}\n"
  },
  {
    "path": "examples/termbox2-image-demo/readme.md",
    "content": "# Termbox2 renderer demo\n\nTerminal-based renderer using [termbox2](https://github.com/termbox/termbox2)\n\nThis demo shows a color palette and a few different components. It allows\nchanging configuration settings for colors, border size rounding mode,\ncharacters used for borders, and transparency.\n\n```\nKeybinds:\nc/C - Cycle through color modes\nb/B - Cycle through border modes\nh/H - Cycle through border characters\ni/I - Cycle through image modes\nt/T - Toggle transparency\nd/D - Toggle debug mode\nq/Q - Quit\n```\n\nConfiguration can be also be overriden by environment variables:\n- `CLAY_TB_COLOR_MODE`\n  - `NORMAL`\n  - `256`\n  - `216`\n  - `GRAYSCALE`\n  - `TRUECOLOR`\n  - `NOCOLOR`\n- `CLAY_TB_BORDER_CHARS`\n  - `DEFAULT`\n  - `ASCII`\n  - `UNICODE`\n  - `NONE`\n- `CLAY_TB_IMAGE_MODE`\n  - `DEFAULT`\n  - `PLACEHOLDER`\n  - `BG`\n  - `ASCII_FG`\n  - `ASCII`\n  - `UNICODE`\n  - `ASCII_FG_FAST`\n  - `ASCII_FAST`\n  - `UNICODE_FAST`\n- `CLAY_TB_TRANSPARENCY`\n  - `1`\n  - `0`\n- `CLAY_TB_CELL_PIXELS`\n  - `widthxheight`\n\n## Building\n\nBuild the binary with cmake\n\n```sh\nmkdir build\ncd build\ncmake ..\nmake\n```\n\nThen run the executable:\n\n```sh\n./clay_examples_termbox2_demo\n```\n\n## Attributions\n\nResources used:\n- `512px-Shark_antwerp_zoo.jpeg`\n  - Retrieved from <https://commons.wikimedia.org/wiki/File:Shark_antwerp_zoo.jpg>\n  - License: [Creative Commons Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/)\n  - No changes made\n- `512px-Balmoral_Castle_30_July_2011.jpg`\n  - Retrieved from <https://commons.wikimedia.org/wiki/File:Balmoral_Castle_30_July_2011.jpg>\n  - License: [Creative Commons Attribution-ShareAlike 3.0 Unported](https://creativecommons.org/licenses/by-sa/3.0/)\n  - No changes made\n- `512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg`\n  - Retrieved from <https://commons.wikimedia.org/wiki/File:German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Sch%C3%A4ferhund_(Folder_(IV)_22.JPG>\n  - License: [Creative Commons Attribution-ShareAlike 3.0 Unported](https://creativecommons.org/licenses/by-sa/3.0/)\n  - No changes made\n- `512px-Rosa_Cubana_2018-09-21_1610.jpeg`\n  - Retrieved from <https://commons.wikimedia.org/wiki/File:Rosa_Cubana_2018-09-21_1610.jpg>\n  - License: [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/)\n  - No changes made\n- `512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg`\n  - Retrieved from <https://commons.wikimedia.org/wiki/File:Vorderer_Graben_10_Bamberg_20190830_001.jpg>\n  - License: [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/)\n  - No changes made\n"
  },
  {
    "path": "examples/terminal-example/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(clay_examples_terminal C)\nset(CMAKE_C_STANDARD 99)\n\nadd_executable(clay_examples_terminal main.c)\n\ntarget_compile_options(clay_examples_terminal PUBLIC)\ntarget_include_directories(clay_examples_terminal PUBLIC .)\nif (CMAKE_SYSTEM_NAME STREQUAL Linux)\n    target_link_libraries(clay_examples_terminal INTERFACE m)\nendif()\n\ntarget_link_libraries(clay_examples_terminal PUBLIC ${CURSES_LIBRARY})\nset(CMAKE_CXX_FLAGS_DEBUG \"-Wall -Werror -DCLAY_DEBUG\")\nset(CMAKE_CXX_FLAGS_RELEASE \"-O3\")"
  },
  {
    "path": "examples/terminal-example/main.c",
    "content": "// Must be defined in one file, _before_ #include \"clay.h\"\n#define CLAY_IMPLEMENTATION\n\n#include <unistd.h>\n#include \"../../clay.h\"\n#include \"../../renderers/terminal/clay_renderer_terminal_ansi.c\"\n#include \"../shared-layouts/clay-video-demo.c\"\n\nconst Clay_Color COLOR_LIGHT = (Clay_Color) {224, 215, 210, 255};\nconst Clay_Color COLOR_RED = (Clay_Color) {168, 66, 28, 255};\nconst Clay_Color COLOR_ORANGE = (Clay_Color) {225, 138, 50, 255};\n\nvoid HandleClayErrors(Clay_ErrorData errorData) {\n    printf(\"%s\", errorData.errorText.chars);\n}\n\nint main() {\n    const int width = 145;\n    const int height = 41;\n    int columnWidth = 16;\n\n    uint64_t totalMemorySize = Clay_MinMemorySize();\n    Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));\n    Clay_Initialize(arena,\n                    (Clay_Dimensions) {.width = (float) width * columnWidth, .height = (float) height * columnWidth},\n                    (Clay_ErrorHandler) {HandleClayErrors});\n    // Tell clay how to measure text\n    Clay_SetMeasureTextFunction(Console_MeasureText, &columnWidth);\n    ClayVideoDemo_Data demoData = ClayVideoDemo_Initialize();\n\n    while (true) {\n        Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData);\n\n        Clay_Terminal_Render(renderCommands, width, height, columnWidth);\n\n        fflush(stdout);\n        sleep(1);\n    }\n}"
  },
  {
    "path": "examples/win32_gdi/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.27)\nproject(win32_gdi C)\n\nset(CMAKE_C_STANDARD 99)\n\nadd_executable(win32_gdi WIN32 main.c)\n\ntarget_compile_options(win32_gdi PUBLIC)\ntarget_include_directories(win32_gdi PUBLIC .)\n\nadd_custom_command(\n        TARGET win32_gdi POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy_directory\n        ${CMAKE_CURRENT_SOURCE_DIR}/resources\n        ${CMAKE_CURRENT_BINARY_DIR}/resources)\n"
  },
  {
    "path": "examples/win32_gdi/build.ps1",
    "content": "\r\n# to build this, install mingw\r\n\r\ngcc main.c -ggdb -omain -lgdi32 -lmingw32 # -mwindows # comment -mwindows out for console output"
  },
  {
    "path": "examples/win32_gdi/main.c",
    "content": "\r\n#define WIN32_LEAN_AND_MEAN\r\n#include <windows.h>\r\n#include <windowsx.h>\r\n\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n\r\n#include <math.h>\r\n\r\n#include \"../../renderers/win32_gdi/clay_renderer_gdi.c\"\r\n\r\n#define CLAY_IMPLEMENTATION\r\n#include \"../../clay.h\"\r\n\r\n#include \"../shared-layouts/clay-video-demo.c\"\r\n\r\nClayVideoDemo_Data demo_data;\r\n\r\n#define APPNAME \"Clay GDI Example\"\r\nchar szAppName[] = APPNAME; // The name of this application\r\nchar szTitle[] = APPNAME;   // The title bar text\r\n\r\nvoid CenterWindow(HWND hWnd);\r\n\r\nlong lastMsgTime = 0;\r\nbool ui_debug_mode;\r\nHFONT fonts[1];\r\n\r\n#ifndef RECTWIDTH\r\n#define RECTWIDTH(rc)   ((rc).right - (rc).left)\r\n#endif\r\n#ifndef RECTHEIGHT\r\n#define RECTHEIGHT(rc)  ((rc).bottom - (rc).top)\r\n#endif\r\n\r\nLRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r\n{\r\n\r\n    switch (message)\r\n    {\r\n\r\n    // ----------------------- first and last\r\n    case WM_CREATE:\r\n        CenterWindow(hwnd);\r\n        break;\r\n\r\n    case WM_DESTROY:\r\n        PostQuitMessage(0);\r\n        break;\r\n\r\n    case WM_MOUSEWHEEL: // scrolling data\r\n    {\r\n        short zDelta = GET_WHEEL_DELTA_WPARAM(wParam);\r\n        // todo: i think GetMessageTime can roll over, so something like if(lastmsgtime > now) ... may be needed\r\n        long now = GetMessageTime();\r\n        float dt = (now - lastMsgTime) / 1000.00;\r\n\r\n        lastMsgTime = now;\r\n\r\n        // little hacky hack to make scrolling *feel* right\r\n        if (abs(zDelta) > 100)\r\n        {\r\n            zDelta = zDelta * .012;\r\n        }\r\n\r\n        Clay_UpdateScrollContainers(true, (Clay_Vector2){.x = 0, .y = zDelta}, dt);\r\n\r\n        InvalidateRect(hwnd, NULL, false); // force a wm_paint event\r\n        break;\r\n    }\r\n    case WM_RBUTTONUP:\r\n    case WM_LBUTTONUP:\r\n    case WM_LBUTTONDOWN:\r\n    case WM_RBUTTONDOWN:\r\n    case WM_MOUSEMOVE: // mouse events\r\n    {\r\n        short mouseX = GET_X_LPARAM(lParam);\r\n        short mouseY = GET_Y_LPARAM(lParam);\r\n        short mouseButtons = LOWORD(wParam);\r\n\r\n        Clay_SetPointerState((Clay_Vector2){mouseX, mouseY}, mouseButtons & 0b01);\r\n\r\n        InvalidateRect(hwnd, NULL, false); // force a wm_paint event\r\n        break;\r\n    }\r\n\r\n    case WM_SIZE: // resize events\r\n    {\r\n\r\n        RECT r = {0};\r\n        if (GetClientRect(hwnd, &r))\r\n        {\r\n            Clay_Dimensions dim = (Clay_Dimensions){.height = r.bottom - r.top, .width = r.right - r.left};\r\n            Clay_SetLayoutDimensions(dim);\r\n        }\r\n\r\n        InvalidateRect(hwnd, NULL, false); // force a wm_paint event\r\n\r\n        break;\r\n    }\r\n\r\n    case WM_KEYDOWN:\r\n        if (VK_ESCAPE == wParam)\r\n        {\r\n            DestroyWindow(hwnd);\r\n            break;\r\n        }\r\n\r\n        if (wParam == VK_F12)\r\n        {\r\n            Clay_SetDebugModeEnabled(ui_debug_mode = !ui_debug_mode);\r\n            break;\r\n        }\r\n\r\n        printf(\"Key Pressed: %d\\r\\n\", wParam);\r\n        InvalidateRect(hwnd, NULL, false); // force a wm_paint event\r\n        break;\r\n\r\n    // ----------------------- render\r\n    case WM_PAINT:\r\n    {\r\n        Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demo_data);\r\n        Clay_Win32_Render(hwnd, renderCommands, fonts);\r\n        break;\r\n    }\r\n\r\n    // ----------------------- let windows do all other stuff\r\n    default:\r\n        return DefWindowProc(hwnd, message, wParam, lParam);\r\n    }\r\n    return 0;\r\n}\r\n\r\nbool didAllocConsole = false;\r\n\r\nvoid HandleClayErrors(Clay_ErrorData errorData)\r\n{\r\n    if (!didAllocConsole)\r\n    {\r\n        didAllocConsole = AllocConsole();\r\n    }\r\n\r\n    printf(\"Handle Clay Errors: %s\\r\\n\", errorData.errorText.chars);\r\n}\r\n\r\nint APIENTRY WinMain(\r\n    HINSTANCE hInstance,\r\n    HINSTANCE hPrevInstance,\r\n    LPSTR lpCmdLine,\r\n    int nCmdShow)\r\n{\r\n    MSG msg;\r\n    WNDCLASS wc;\r\n    HWND hwnd;\r\n\r\n    demo_data = ClayVideoDemo_Initialize();\r\n\r\n    uint64_t clayRequiredMemory = Clay_MinMemorySize();\r\n    Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(clayRequiredMemory, malloc(clayRequiredMemory));\r\n    Clay_Initialize(clayMemory, (Clay_Dimensions){.width = 800, .height = 600}, (Clay_ErrorHandler){HandleClayErrors}); // This final argument is new since the video was published\r\n\r\n    Clay_Win32_SetRendererFlags(CLAYGDI_RF_ALPHABLEND | CLAYGDI_RF_SMOOTHCORNERS);\r\n\r\n    // Initialize clay fonts and text drawing\r\n    fonts[FONT_ID_BODY_16] = Clay_Win32_SimpleCreateFont(\"resources/Roboto-Regular.ttf\", \"Roboto\", -11, FW_NORMAL);\r\n    Clay_SetMeasureTextFunction(Clay_Win32_MeasureText, fonts);\r\n\r\n    ZeroMemory(&wc, sizeof wc);\r\n    wc.hInstance = hInstance;\r\n    wc.lpszClassName = szAppName;\r\n    wc.lpfnWndProc = (WNDPROC)WndProc;\r\n    wc.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;\r\n    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);\r\n    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);\r\n    wc.hCursor = LoadCursor(NULL, IDC_ARROW);\r\n\r\n    if (FALSE == RegisterClass(&wc))\r\n        return 0;\r\n\r\n    // Calculate window rectangle by given client size\r\n    // TODO: AdjustWindowRectExForDpi for DPI support\r\n    RECT rcWindow = { .right = 800, .bottom = 600 };\r\n    AdjustWindowRect(&rcWindow, WS_OVERLAPPEDWINDOW, FALSE);\r\n\r\n    hwnd = CreateWindow(\r\n        szAppName,\r\n        szTitle,\r\n        WS_OVERLAPPEDWINDOW | WS_VISIBLE,\r\n        CW_USEDEFAULT,\r\n        CW_USEDEFAULT,\r\n        RECTWIDTH(rcWindow), // CW_USEDEFAULT,\r\n        RECTHEIGHT(rcWindow), // CW_USEDEFAULT,\r\n        0,\r\n        0,\r\n        hInstance,\r\n        0);\r\n\r\n    if (hwnd == NULL)\r\n        return 0;\r\n\r\n    // Main message loop:\r\n    while (GetMessage(&msg, NULL, 0, 0) > 0)\r\n    {\r\n        TranslateMessage(&msg);\r\n        DispatchMessage(&msg);\r\n    }\r\n\r\n    return msg.wParam;\r\n}\r\n\r\nvoid CenterWindow(HWND hwnd_self)\r\n{\r\n    HWND hwnd_parent;\r\n    RECT rw_self, rc_parent, rw_parent;\r\n    int xpos, ypos;\r\n\r\n    hwnd_parent = GetParent(hwnd_self);\r\n    if (NULL == hwnd_parent)\r\n        hwnd_parent = GetDesktopWindow();\r\n\r\n    GetWindowRect(hwnd_parent, &rw_parent);\r\n    GetClientRect(hwnd_parent, &rc_parent);\r\n    GetWindowRect(hwnd_self, &rw_self);\r\n\r\n    xpos = rw_parent.left + (rc_parent.right + rw_self.left - rw_self.right) / 2;\r\n    ypos = rw_parent.top + (rc_parent.bottom + rw_self.top - rw_self.bottom) / 2;\r\n\r\n    SetWindowPos(\r\n        hwnd_self, NULL,\r\n        xpos, ypos, 0, 0,\r\n        SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);\r\n}\r\n\r\n//+---------------------------------------------------------------------------\r\n"
  },
  {
    "path": "renderers/GLES3/clay_renderer_gles3.h",
    "content": "#ifndef CLAY_RENDERER_GLES3_H\n#define CLAY_RENDERER_GLES3_H\n\n// There may be custom header customizations, very client specific\n// let client indicate that they manage headers by setting GLSL_VERSION\n#ifndef GLSL_VERSION\n#if defined(__EMSCRIPTEN__)\n#include <emscripten.h>\n#include <emscripten/html5.h>\n#include <GLES3/gl3.h>\n#define GLSL_VERSION \"#version 300 es\"\n#else\n// Only apple computers now sorry\n// That means it is not really GLES3 but desktop OpenGL 3\n// Luckily, it is compatible with GLES3\n#include <OpenGL/gl3.h>\n#define GLSL_VERSION \"#version 330 core\"\n#endif\n#endif\n\n#define MAX_IMAGES 4\n#define MAX_FONTS 4\n\n/*\n * Instanced rendering for Rects/Images/Borders\n * will use this data\n * Note, it needs to be padded to 4 floats\n * Draws:\n * - One rectangular with possibly rounded corner\n * - And possibly with a hole inside (with rounded edges too, if corners are rounded)\n * - It could also draw a picture with alsoe rounded corner\n */\ntypedef struct RectInstance\n{\n    float x, y, w, h;         // 4 Draw where on screen\n    float u0, v0, u1, v1;     // 4 Atlas region\n    float r, g, b, a;         // 4 Color\n    float radiusTL, radiusTR; // 2 Corner rounding\n    float radiusBL, radiusBR; // 2\n    float borderL, borderR;   // 2 Border widths\n    float borderT, borderB;   // 2\n    float texToUse;           // 1 Texture atlas to take an image from (1-4)\n    float pad[3];             // 3\n} RectInstance;\n\n/*\n * Struct for glyph instanced rendering\n * Each glyph consists of 6 vertexes (to make 2 triangle of a quad)\n */\ntypedef struct GlyphVtx\n{\n    float x, y;         // To draw Where\n    float u, v;         // To draw What\n    float r, g, b, a;   // Text color\n    float atlasTexUnit; // Shader will have all samples loaded but this will point which to use\n    float pad[3];       // 3\n} GlyphVtx;\n\ntypedef struct Gles3_GlyphVtxArray\n{\n    GlyphVtx *instData;\n    int capacity;\n    int count;\n} Gles3_GlyphVtxArray;\n\ntypedef struct Gles3_QuadInstanceArray\n{\n    RectInstance *instData; // packed per-instance floats\n    int capacity;           // how many instances it can hold\n    int count;              // how many instances does it actually hold\n} Gles3_QuadInstanceArray;\n\ntypedef struct Gles3_ImageConfig\n{\n    int textureToUse;\n    float u0, v0;\n    float u1, v1;\n} Gles3_ImageConfig;\n\n#ifndef CLAY_RENDERER_GLES3_IMPLEMENTATION\ntypedef struct Gles3_Renderer Gles3_Renderer;\n#endif\n\n#ifdef CLAY_RENDERER_GLES3_IMPLEMENTATION\n\n#include <math.h>\n#include <clay.h>\n#include <stdlib.h>\n\n#include \"clay_renderer_gles3.h\"\n\nenum\n{\n    ATTR_QUAD_POS = 0,\n    ATTR_QUAD_RECT = 1,\n    ATTR_QUAD_COLOR = 2,\n    ATTR_QUAD_UV = 3,\n    ATTR_QUAD_RAD = 4,\n    ATTR_QUAD_BORDER = 5,\n    ATTR_QUAD_TEX = 6,\n};\n\nenum\n{\n    ATTR_GLYPH_POS = 0,\n    ATTR_GLYPH_UV = 1,\n    ATTR_GLYPH_COLOR = 2,\n    ATTR_GLYPH_TEX = 3,\n};\n\n/*\n * rendering\n */\n\nconst char *GLES3_QUAD_VERTEX_SHADER =\n    GLSL_VERSION\n    \"\\n\"\n    \"precision mediump float;\\n\"\n    \"layout(location = 0) in vec2 aPos;        // unit quad (0..1)\\n\"\n    \"layout(location = 1) in vec4 aRect;       // x,y,w,h (pixels)\\n\"\n    \"layout(location = 3) in vec4 aUV;         // u0,v0,u1,v1\\n\"\n    \"layout(location = 2) in vec4 aColor;      // rgba\\n\"\n    \"layout(location = 4) in vec4 aCornerRadii;\\n\"\n    \"layout(location = 5) in vec4 aBorderWidths;\\n\"\n    \"layout(location = 6) in float aTexSlot;\\n\"\n    \"uniform vec2 uScreen; // screen size in pixels\\n\"\n    \"out vec2 vPos;\\n\"\n    \"out vec4 vRect;\\n\"\n    \"out vec4 vColor;\\n\"\n    \"out vec2 vUV;\\n\"\n    \"out vec4 vCornerRadii;\\n\"\n    \"out vec4 vBorderWidths;\\n\"\n    \"out float vTexSlot;\\n\"\n    \"void main() {\\n\"\n    \"    vec2 pos = vec2(aPos.x * aRect.z + aRect.x, aPos.y * aRect.w + aRect.y);\\n\"\n    \"    vec2 ndc = pos / uScreen * 2.0 - 1.0; // ndc.y increases up; pos y increases down (we will inve\\n\"\n    \"    ndc.y = -ndc.y;\\n\"\n    \"    gl_Position = vec4(ndc, 0.0, 1.0);\\n\"\n    \"    vPos = aPos;\\n\"\n    \"    vRect = aRect;\\n\"\n    \"    vColor = aColor;\\n\"\n    \"    vUV = mix(aUV.xy, aUV.zw, aPos);\\n\"\n    \"    vCornerRadii = aCornerRadii;\\n\"\n    \"    vBorderWidths = aBorderWidths;\\n\"\n    \"    vTexSlot = aTexSlot;\\n\"\n    \"}\\n\";\n\nconst char *GLES3_QUAD_FRAGMENT_SHADER =\n    GLSL_VERSION\n    \"\\n\"\n    \"precision mediump float;\\n\"\n    \"in vec2 vPos;\\n\"\n    \"in vec4 vRect;\\n\"\n    \"in vec4 vColor;\\n\"\n    \"in vec2 vUV;\\n\"\n    \"in vec4 vCornerRadii;\\n\"\n    \"in vec4 vBorderWidths;\\n\"\n    \"in float vTexSlot;\\n\"\n    \"uniform sampler2D uTex0;\\n\"\n    \"uniform sampler2D uTex1;\\n\"\n    \"uniform sampler2D uTex2;\\n\"\n    \"uniform sampler2D uTex3;\\n\"\n    \"out vec4 frag;\\n\"\n    \"void main() {\\n\"\n    \"    // Pixel coordinates in pixel space\\n\"\n    \"    vec2 pix = vRect.xy + vPos * vRect.zw;\\n\"\n    \"    float x0 = vRect.x;\\n\"\n    \"    float y0 = vRect.y;\\n\"\n    \"    float w  = vRect.z;\\n\"\n    \"    float h  = vRect.w;\\n\"\n    \"    // Local position inside the rectangle (0..w, 0..h)\\n\"\n    \"    vec2 local = pix - vec2(x0, y0);\\n\"\n    \"    // Original corner radii\\n\"\n    \"    float tl = vCornerRadii.x;\\n\"\n    \"    float tr = vCornerRadii.y;\\n\"\n    \"    float bl = vCornerRadii.z;\\n\"\n    \"    float br = vCornerRadii.w;\\n\"\n    \"    // Border thicknesses\\n\"\n    \"    float L = vBorderWidths.x;\\n\"\n    \"    float R = vBorderWidths.y;\\n\"\n    \"    float T = vBorderWidths.z;\\n\"\n    \"    float B = vBorderWidths.w;\\n\"\n    \"    bool CLAY_BORDERS_ARE_INSET = true; // it is true\\n\"\n    \"    bool isBorder = (L > 0.0 || R > 0.0 || T > 0.0 || B > 0.0);\\n\"\n    \"    float outerAlpha = 1.0;\\n\"\n    \"    // If it is not a border but rect or image, then it only has outer border what is provided\\n\"\n    \"    // Otherwise it increases the outter border, but the provided borde is the ineer border\\n\"\n    \"    // I think is better not increase rounding radius when that radius is smaller than border thickness\\n\"\n    \"    float outter_tl;\\n\"\n    \"    float outter_tr;\\n\"\n    \"    float outter_bl;\\n\"\n    \"    float outter_br;\\n\"\n    \"    if (CLAY_BORDERS_ARE_INSET) {\\n\"\n    \"        // Actural behaviour\\n\"\n    \"        outter_tl = tl;\\n\"\n    \"        outter_tr = tr;\\n\"\n    \"        outter_bl = bl;\\n\"\n    \"        outter_br = br;\\n\"\n    \"        tl = (tl > min(T, L)) ? tl - min(T, L) : tl;\\n\"\n    \"        tr = (tr > min(T, R)) ? tr - min(T, R) : tr;\\n\"\n    \"        bl = (bl > min(B, L)) ? bl - min(B, L) : bl;\\n\"\n    \"        br = (br > min(B, R)) ? br - min(B, R) : br;\\n\"\n    \"    } else {\\n\"\n    \"        // Hypothetical behaviour\\n\"\n    \"        outter_tl = (tl > min(T, L)) ? tl + min(T, L) : tl;\\n\"\n    \"        outter_tr = (tr > min(T, R)) ? tr + min(T, R) : tr;\\n\"\n    \"        outter_bl = (bl > min(B, L)) ? bl + min(B, L) : bl;\\n\"\n    \"        outter_br = (br > min(B, R)) ? br + min(B, R) : br;\\n\"\n    \"    }\\n\"\n    \"    if (outter_tl > 0.0 && local.x < outter_tl && local.y < outter_tl)\\n\"\n    \"        outerAlpha = step(length(local - vec2(outter_tl, outter_tl)), outter_tl);\\n\"\n    \"    if (outter_tr > 0.0 && local.x > w - outter_tr && local.y < outter_tr)\\n\"\n    \"        outerAlpha *= step(length(local - vec2(w - outter_tr, outter_tr)), outter_tr);\\n\"\n    \"    if (outter_bl > 0.0 && local.x < outter_bl && local.y > h - outter_bl)\\n\"\n    \"        outerAlpha *= step(length(local - vec2(outter_bl, h - outter_bl)), outter_bl);\\n\"\n    \"    if (outter_br > 0.0 && local.x > w - outter_br && local.y > h - outter_br)\\n\"\n    \"        outerAlpha *= step(length(local - vec2(w - outter_br, h - outter_br)), outter_br);\\n\"\n    \"    if (outerAlpha < 0.5)\\n\"\n    \"        discard;\\n\"\n    \"    // -------- Border logic --------\\n\"\n    \"    if (isBorder) {\\n\"\n    \"        float iw = w - L - R;\\n\"\n    \"        float ih = h - T - B;\\n\"\n    \"        vec2 innerLocal = local - vec2(L, T);\\n\"\n    \"        // Check if pixel is inside inner rounded rect\\n\"\n    \"        bool insideInner = true;\\n\"\n    \"        if (tl > 0.0 && innerLocal.x < tl && innerLocal.y < tl)\\n\"\n    \"            insideInner = (length(innerLocal - vec2(tl, tl)) <= tl);\\n\"\n    \"        if (tr > 0.0 && innerLocal.x > iw - tr && innerLocal.y < tr)\\n\"\n    \"            insideInner = insideInner && (length(innerLocal - vec2(iw - tr, tr)) <= tr);\\n\"\n    \"        // Bottom-left\\n\"\n    \"        if (bl> 0.0 && innerLocal.x < bl && innerLocal.y > ih - bl) \\n\"\n    \"            insideInner = insideInner && (length(innerLocal - vec2(bl, ih - bl)) <= bl);\\n\"\n    \"        // Bottom-right\\n\"\n    \"        if (br > 0.0 && innerLocal.x > iw - br && innerLocal.y > ih - br)\\n\"\n    \"            insideInner = insideInner && (length(innerLocal - vec2(iw - br, ih - br)) <= br);\\n\"\n    \"        // Discard pixels inside inner rounded rect\\n\"\n    \"        if (insideInner && innerLocal.x >= 0.0 && innerLocal.x <= iw && innerLocal.y >= 0.0 && innerLocal.y <= ih)\\n\"\n    \"            discard;\\n\"\n    \"        frag = vColor;\\n\"\n    \"        return;\\n\"\n    \"    }\\n\"\n    \"    // -------- Non-border rectangle or image --------\\n\"\n    \"    if (vTexSlot < 0.0) {\\n\"\n    \"        frag = vColor;\\n\"\n    \"    } else {\\n\"\n    \"        int slot = int(vTexSlot + 0.5);\\n\"\n    \"        if (slot == 0) frag = texture(uTex0, vUV);\\n\"\n    \"        if (slot == 1) frag = texture(uTex1, vUV);\\n\"\n    \"        if (slot == 2) frag = texture(uTex2, vUV);\\n\"\n    \"        if (slot == 3) frag = texture(uTex3, vUV);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nconst char *GLES3_TEXT_VERTEX_SHADER =\n    GLSL_VERSION\n    \"\\n\"\n    \"precision mediump float;\\n\"\n    \"layout(location = 0) in vec2 aPos;\\n\"\n    \"layout(location = 1) in vec2 aUV;\\n\"\n    \"layout(location = 2) in vec4 aColor;\\n\"\n    \"layout(location = 3) in float aTexSlot;\\n\"\n    \"uniform vec2 uScreen;\\n\"\n    \"out vec2 vUV;\\n\"\n    \"out vec4 vColor;\\n\"\n    \"out float vTexSlot;\\n\"\n    \"void main() {\\n\"\n    \"    vec2 ndc = (aPos / uScreen) * 2.0 - 1.0;\\n\"\n    \"    gl_Position = vec4(ndc * vec2(1.0, -1.0), 0.0, 1.0);\\n\"\n    \"    vUV = aUV;\\n\"\n    \"    vColor = aColor;\\n\"\n    \"    vTexSlot = aTexSlot;\\n\"\n    \"}\\n\";\n\nconst char *GLES3_TEXT_FRAGMENT_SHADER =\n    GLSL_VERSION\n    \"\\n\"\n    \"precision mediump float;\\n\"\n    \"in vec2 vUV;\\n\"\n    \"in vec4 vColor;\\n\"\n    \"in float vTexSlot;\\n\"\n    \"uniform sampler2D uTex0;\\n\"\n    \"uniform sampler2D uTex1;\\n\"\n    \"uniform sampler2D uTex2;\\n\"\n    \"uniform sampler2D uTex3;\\n\"\n    \"out vec4 fragColor;\\n\"\n    \"void main() {\\n\"\n    \"    int slot = int(vTexSlot + 0.5);\\n\"\n    \"    float coverage;\\n\"\n    \"    if (slot == 0) coverage = texture(uTex0, vUV).r;\\n\"\n    \"    if (slot == 1) coverage = texture(uTex1, vUV).r;\\n\"\n    \"    if (slot == 2) coverage = texture(uTex2, vUV).r;\\n\"\n    \"    if (slot == 3) coverage = texture(uTex3, vUV).r;\\n\"\n    \"    fragColor = vec4(vColor.rgb, vColor.a * coverage);\\n\"\n    \"} \\n\";\n\n/**\n * This renderer accumulates all quads and glyphs of every draw coommand\n * in their array, and flushes them in just 2 instanced draw calls to OpenGL\n */\ntypedef struct Gles3_Renderer\n{\n    Clay_Arena clayMemory;\n\n    // It is super important keep track on the performance of this renderer:\n    uint64_t totalDrawCallsToOpenGl;\n\n    float screenWidth;\n    float screenHeight;\n\n    /* Quads rendering */\n    GLuint quadVAO;\n    GLuint quadVBO;\n    GLuint quadInstanceVBO;\n    GLuint quadShaderId;\n    GLuint imageTextures[MAX_IMAGES];\n    Gles3_QuadInstanceArray quadInstanceArray; // Each instance is one quad\n\n    /* Fonts rendering */\n    GLuint textVAO;\n    GLuint textVBO;\n    GLuint textShader;\n    GLuint fontTextures[MAX_FONTS];\n    Gles3_GlyphVtxArray glyphVtxArray; // Instance data: every vertex is an element,\n                                       // 6 elements per each instance\n\n    // Text renderer is delegated to external function, which is supposed\n    // to add glyph data based on passed render text command\n    void (*renderTextFunction)(\n        Clay_RenderCommand *cmd,    // Will be always of CLAY_RENDER_COMMAND_TYPE_TEXT\n        Gles3_GlyphVtxArray *accum, // 6 vertices need to be added to this array\n        void *userData              // Fonts pallete\n    );\n} Gles3_Renderer;\n\nstatic GLuint Gles3__CompileShader(GLenum type, const char *source)\n{\n    GLuint shader = glCreateShader(type);\n    glShaderSource(shader, 1, &source, NULL);\n    glCompileShader(shader);\n\n    GLint success;\n    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);\n    if (!success)\n    {\n        char infoLog[512];\n        glGetShaderInfoLog(shader, 512, NULL, infoLog);\n\n        printf(\"ERROR::SHADER::COMPILATION_FAILED\\n\");\n        printf(\"SHADER SOURCE:\\n%s\\n\", source);\n        printf(\"SHADER TYPE: \");\n        if (type == GL_VERTEX_SHADER)\n            printf(\"Vertex Shader\");\n        else if (type == GL_FRAGMENT_SHADER)\n            printf(\"Fragment Shader\");\n        else\n            printf(\"Unknown\");\n        printf(\"\\nSHADER COMPILATION ERROR:\\n%s\\n\", infoLog);\n        abort();\n    }\n    return shader;\n}\n\nGLuint Gles3__CreateShaderProgram(\n    const char *vertexShaderSource,\n    const char *fragmentShaderSource)\n{\n    GLuint vertexShader =\n        Gles3__CompileShader(GL_VERTEX_SHADER, vertexShaderSource);\n    GLuint fragmentShader =\n        Gles3__CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);\n\n    GLuint shaderProgram = glCreateProgram();\n    glAttachShader(shaderProgram, vertexShader);\n    glAttachShader(shaderProgram, fragmentShader);\n    glLinkProgram(shaderProgram);\n\n    glDeleteShader(vertexShader);\n    glDeleteShader(fragmentShader);\n\n    return shaderProgram;\n}\n\nvoid Gles3_Initialize(Gles3_Renderer *renderer, int maxInstances)\n{\n    renderer->totalDrawCallsToOpenGl = 0;\n    // compile shader\n    renderer->quadShaderId = Gles3__CreateShaderProgram(\n        GLES3_QUAD_VERTEX_SHADER, GLES3_QUAD_FRAGMENT_SHADER);\n\n    glUseProgram(renderer->quadShaderId);\n    glUniform1i(glGetUniformLocation(renderer->quadShaderId, \"uTex0\"), 0);\n    glUniform1i(glGetUniformLocation(renderer->quadShaderId, \"uTex1\"), 1);\n    glUniform1i(glGetUniformLocation(renderer->quadShaderId, \"uTex2\"), 2);\n    glUniform1i(glGetUniformLocation(renderer->quadShaderId, \"uTex3\"), 3);\n\n    // create unit quad VBO (0..1)\n    const float quadVerts[8] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};\n    glGenVertexArrays(1, &renderer->quadVAO);\n    glBindVertexArray(renderer->quadVAO);\n\n    glGenBuffers(1, &renderer->quadVBO);\n    glBindBuffer(GL_ARRAY_BUFFER, renderer->quadVBO);\n    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVerts), quadVerts, GL_STATIC_DRAW);\n\n    // attribute 0: aPos (vec2), per-vertex\n    glEnableVertexAttribArray(ATTR_QUAD_POS);\n    glVertexAttribPointer(ATTR_QUAD_POS, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);\n    glVertexAttribDivisor(ATTR_QUAD_POS, 0);\n\n    // create instance buffer big enough\n    Gles3_QuadInstanceArray *quads = &renderer->quadInstanceArray;\n    quads->capacity = maxInstances;\n    quads->instData =\n        (RectInstance *)malloc(sizeof(RectInstance) * quads->capacity);\n    quads->count = 0;\n\n    glGenBuffers(1, &renderer->quadInstanceVBO);\n    glBindBuffer(GL_ARRAY_BUFFER, renderer->quadInstanceVBO);\n    glBufferData(GL_ARRAY_BUFFER,\n                 sizeof(RectInstance) * quads->capacity,\n                 NULL,\n                 GL_DYNAMIC_DRAW);\n\n    // set up instance attributes\n    GLsizei stride = sizeof(RectInstance);\n\n    glEnableVertexAttribArray(ATTR_QUAD_RECT);\n    glVertexAttribPointer(ATTR_QUAD_RECT, 4, GL_FLOAT, GL_FALSE,\n                          stride, (void *)offsetof(RectInstance, x));\n    glVertexAttribDivisor(ATTR_QUAD_RECT, 1);\n\n    glEnableVertexAttribArray(ATTR_QUAD_COLOR);\n    glVertexAttribPointer(ATTR_QUAD_COLOR, 4, GL_FLOAT, GL_FALSE,\n                          stride, (void *)offsetof(RectInstance, r));\n    glVertexAttribDivisor(ATTR_QUAD_COLOR, 1);\n\n    glEnableVertexAttribArray(ATTR_QUAD_UV);\n    glVertexAttribPointer(ATTR_QUAD_UV, 4, GL_FLOAT, GL_FALSE,\n                          stride, (void *)offsetof(RectInstance, u0));\n    glVertexAttribDivisor(ATTR_QUAD_UV, 1);\n\n    glEnableVertexAttribArray(ATTR_QUAD_RAD);\n    glVertexAttribPointer(ATTR_QUAD_RAD, 4, GL_FLOAT, GL_FALSE,\n                          stride, (void *)offsetof(RectInstance, radiusTL));\n    glVertexAttribDivisor(ATTR_QUAD_RAD, 1);\n\n    glEnableVertexAttribArray(ATTR_QUAD_BORDER);\n    glVertexAttribPointer(ATTR_QUAD_BORDER, 4, GL_FLOAT, GL_FALSE,\n                          stride, (void *)offsetof(RectInstance, borderL));\n    glVertexAttribDivisor(ATTR_QUAD_BORDER, 1);\n\n    glEnableVertexAttribArray(ATTR_QUAD_TEX);\n    glVertexAttribPointer(ATTR_QUAD_TEX, 1, GL_FLOAT, GL_FALSE,\n                          stride, (void *)offsetof(RectInstance, texToUse));\n    glVertexAttribDivisor(ATTR_QUAD_TEX, 1);\n\n    glBindVertexArray(1);\n    glBindBuffer(GL_ARRAY_BUFFER, 0);\n\n    // Ok now we will initialize text!\n    Gles3_GlyphVtxArray *gVerts = &renderer->glyphVtxArray;\n\n    // configure capacity\n    gVerts->capacity = maxInstances;\n    gVerts->count = 0;\n\n    // allocate CPU-side vertex buffer: 6 vertices per glyph\n    gVerts->instData = (GlyphVtx *)malloc(sizeof(GlyphVtx) * 6 * gVerts->capacity);\n    if (!gVerts->instData)\n    {\n        fprintf(stderr, \"Failed to allocate glyph_vertices\\n\");\n        gVerts->capacity = 0;\n    }\n\n    // create VAO/VBO for text rendering\n    glGenVertexArrays(1, &renderer->textVAO);\n    glBindVertexArray(renderer->textVAO);\n\n    glGenBuffers(1, &renderer->textVBO);\n    glBindBuffer(GL_ARRAY_BUFFER, renderer->textVBO);\n    glBufferData(GL_ARRAY_BUFFER,\n                 sizeof(GlyphVtx) * 6 * gVerts->capacity,\n                 NULL,\n                 GL_DYNAMIC_DRAW);\n\n    GLsizei gv_stride = sizeof(GlyphVtx);\n\n    glEnableVertexAttribArray(ATTR_GLYPH_POS);\n    glVertexAttribPointer(ATTR_GLYPH_POS, 2, GL_FLOAT, GL_FALSE, gv_stride, (void *)(offsetof(GlyphVtx, x)));\n\n    glEnableVertexAttribArray(ATTR_GLYPH_UV);\n    glVertexAttribPointer(ATTR_GLYPH_UV, 2, GL_FLOAT, GL_FALSE, gv_stride, (void *)(offsetof(GlyphVtx, u)));\n\n    glEnableVertexAttribArray(ATTR_GLYPH_COLOR);\n    glVertexAttribPointer(ATTR_GLYPH_COLOR, 4, GL_FLOAT, GL_FALSE, gv_stride, (void *)(offsetof(GlyphVtx, r)));\n\n    glEnableVertexAttribArray(ATTR_GLYPH_TEX);\n    glVertexAttribPointer(ATTR_GLYPH_TEX, 1, GL_FLOAT, GL_FALSE, gv_stride, (void *)(offsetof(GlyphVtx, atlasTexUnit)));\n\n    glBindVertexArray(0);\n    glBindBuffer(GL_ARRAY_BUFFER, 0);\n\n    renderer->textShader = Gles3__CreateShaderProgram(\n        GLES3_TEXT_VERTEX_SHADER, GLES3_TEXT_FRAGMENT_SHADER);\n    glUseProgram(renderer->textShader);\n\n    // Link sampler uniforms in the text shader to the correct texture units.\n    // Each uniform tells the shader which unit to read from.\n    glUniform1i(glGetUniformLocation(renderer->textShader, \"uTex0\"), 0);\n    glUniform1i(glGetUniformLocation(renderer->textShader, \"uTex1\"), 1);\n    glUniform1i(glGetUniformLocation(renderer->textShader, \"uTex2\"), 2);\n    glUniform1i(glGetUniformLocation(renderer->textShader, \"uTex3\"), 3);\n}\n\nvoid Gles3_SetRenderTextFunction(\n    Gles3_Renderer *renderer,\n    void (*renderTextFunction)(\n        Clay_RenderCommand *cmd, Gles3_GlyphVtxArray *accum, void *userData),\n    void *userData)\n{\n    renderer->renderTextFunction = renderTextFunction;\n}\n\nvoid Gles3_Render(\n    Gles3_Renderer *renderer,\n    Clay_RenderCommandArray cmds,\n    void *userData // eg. fonts\n)\n{\n    Clay_Dimensions layoutDimensions = Clay_GetCurrentContext()->layoutDimensions;\n    renderer->screenWidth = layoutDimensions.width;\n    renderer->screenHeight = layoutDimensions.height;\n\n    Gles3_QuadInstanceArray *quads = &renderer->quadInstanceArray;\n    Gles3_GlyphVtxArray *gVerts = &renderer->glyphVtxArray;\n\n    gVerts->count = 0;\n\n    for (int i = 0; i < cmds.length; i++)\n    {\n        Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&cmds, i);\n        Clay_BoundingBox boundingBox = (Clay_BoundingBox){\n            .x = roundf(cmd->boundingBox.x),\n            .y = roundf(cmd->boundingBox.y),\n            .width = roundf(cmd->boundingBox.width),\n            .height = roundf(cmd->boundingBox.height),\n        };\n\n        bool scissorChanged = false;\n        switch (cmd->commandType)\n        {\n        case CLAY_RENDER_COMMAND_TYPE_TEXT:\n        {\n            renderer->renderTextFunction(\n                cmd,\n                &renderer->glyphVtxArray,\n                userData);\n            break;\n        }\n        case CLAY_RENDER_COMMAND_TYPE_RECTANGLE:\n        case CLAY_RENDER_COMMAND_TYPE_IMAGE:\n        {\n            Clay_RectangleRenderData *config = &cmd->renderData.rectangle;\n            Clay_Color c = config->backgroundColor;\n\n            // Convert to float 0..1\n            float rf = c.r / 255.0f;\n            float gf = c.g / 255.0f;\n            float bf = c.b / 255.0f;\n            float af = c.a / 255.0f;\n\n            bool isImage = cmd->commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE;\n\n            // Ensure we don't overflow the capacity\n            if (quads->count >= quads->capacity)\n            {\n                printf(\"Clay renderer: instance overflow!\\n\");\n                break;\n            }\n\n            int idx = quads->count;\n            RectInstance *dst = &quads->instData[idx];\n            dst->x = boundingBox.x;\n            dst->y = boundingBox.y;\n            dst->w = boundingBox.width;\n            dst->h = boundingBox.height;\n\n            if (isImage)\n            {\n                Gles3_ImageConfig *imgConf = (Gles3_ImageConfig *)cmd->renderData.image.imageData;\n                dst->u0 = imgConf->u0;\n                dst->v0 = imgConf->v0;\n                dst->u1 = imgConf->u1;\n                dst->v1 = imgConf->v1;\n                dst->texToUse = (float)imgConf->textureToUse;\n            }\n            else\n            {\n                dst->u0 = dst->v0 = 0.0f;\n                dst->u1 = dst->v1 = 1.0f;\n                dst->texToUse = -1.0f; // This means no image, use albedo color\n            }\n\n            // colour\n            dst->r = rf;\n            dst->g = gf;\n            dst->b = bf;\n            dst->a = af;\n\n            // corner radii\n            Clay_CornerRadius r = config->cornerRadius;\n            dst->radiusTL = r.topLeft;\n            dst->radiusTR = r.topRight;\n            dst->radiusBL = r.bottomLeft;\n            dst->radiusBR = r.bottomRight;\n\n            dst->borderT = 0.0f;\n            dst->borderR = 0.0f;\n            dst->borderB = 0.0f;\n            dst->borderL = 0.0f;\n\n            quads->count++;\n            break;\n        }\n        case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START:\n        {\n            scissorChanged = true;\n            break;\n        }\n        case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END:\n        {\n            scissorChanged = true;\n            break;\n        }\n        case CLAY_RENDER_COMMAND_TYPE_BORDER:\n        {\n            Clay_BorderRenderData *br = &cmd->renderData.border;\n\n            float rf = br->color.r / 255.0f;\n            float gf = br->color.g / 255.0f;\n            float bf = br->color.b / 255.0f;\n            float af = br->color.a / 255.0f;\n\n            float x = boundingBox.x;\n            float y = boundingBox.y;\n            float w = boundingBox.width;\n            float h = boundingBox.height;\n\n            float top = br->width.top;\n            float bottom = br->width.bottom;\n            float left = br->width.left;\n            float right = br->width.right;\n\n            int idx = quads->count;\n            RectInstance *dst = &quads->instData[idx];\n\n            dst->x = x - left;\n            dst->y = y - top;\n            dst->w = w + right;\n            dst->h = h + bottom;\n\n            dst->borderB = bottom;\n            dst->borderL = left;\n            dst->borderT = top;\n            dst->borderR = right;\n\n            // Clay borders are inset, but adding support to outset borders\n            // Is as easy as this + some minor changes in shader too\n            bool CLAY_BORDERS_ARE_INSET = true;\n            if (CLAY_BORDERS_ARE_INSET)\n            {\n                // Normal behaviour\n                dst->x = x;\n                dst->y = y;\n                dst->w = w;\n                dst->h = h;\n            }\n            else\n            {\n                // Hypotethical behaviour, if the borders were outside\n                dst->x = x - left;\n                dst->y = y - top;\n                dst->w = w + left + right;\n                dst->h = h + top + bottom;\n            }\n\n            dst->u0 = 0.0f;\n            dst->v0 = 0.0f;\n            dst->u1 = 1.0f;\n            dst->v1 = 1.0f;\n\n            dst->r = rf;\n            dst->g = gf;\n            dst->b = bf;\n            dst->a = af;\n\n            dst->radiusTL = br->cornerRadius.topLeft;\n            dst->radiusTR = br->cornerRadius.topRight;\n            dst->radiusBR = br->cornerRadius.bottomRight;\n            dst->radiusBL = br->cornerRadius.bottomLeft;\n\n            dst->texToUse = -1.0f;\n\n            quads->count++;\n            break;\n        }\n\n        case CLAY_RENDER_COMMAND_TYPE_CUSTOM:\n        {\n            // printf(\"Unhandled clay cmd: custom\\n\");\n            break;\n        }\n        default:\n        {\n            printf(\"Error: unhandled render command\\n\");\n            exit(1);\n        }\n        }\n\n        // Flush draw calls if scissors about to change in this iteration\n        if (i == cmds.length - 1 || scissorChanged)\n        {\n            scissorChanged = false;\n            // Render Recatangles and Images\n            if (quads->count > 0)\n            {\n                glUseProgram(renderer->quadShaderId);\n\n                glActiveTexture(GL_TEXTURE0);\n                glBindTexture(GL_TEXTURE_2D, renderer->imageTextures[0]);\n                glActiveTexture(GL_TEXTURE1);\n                glBindTexture(GL_TEXTURE_2D, renderer->imageTextures[1]);\n                glActiveTexture(GL_TEXTURE2);\n                glBindTexture(GL_TEXTURE_2D, renderer->imageTextures[2]);\n                glActiveTexture(GL_TEXTURE3);\n                glBindTexture(GL_TEXTURE_2D, renderer->imageTextures[3]);\n\n                // set uniforms\n                GLint locScreen = glGetUniformLocation(renderer->quadShaderId, \"uScreen\");\n                glUniform2f(locScreen,\n                            (float)renderer->screenWidth,\n                            (float)renderer->screenHeight);\n\n                glBindVertexArray(renderer->quadVAO);\n\n                // upload all instances at once\n                glBindBuffer(GL_ARRAY_BUFFER, renderer->quadInstanceVBO);\n\n                // rectangles are solid colour — disable atlas use\n                glBufferSubData(GL_ARRAY_BUFFER,\n                                0,\n                                quads->count * sizeof(RectInstance),\n                                quads->instData);\n\n                // draw unit quad (4 verts) instanced\n                glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, quads->count);\n                renderer->totalDrawCallsToOpenGl += 1;\n\n                glBindVertexArray(0);\n                glUseProgram(0);\n            }\n            // Clrear instance arrays, as they were flushed to their render calls\n            quads->count = 0;\n\n            // Text rendering\n            if (renderer->glyphVtxArray.count > 0)\n            {\n                glUseProgram(renderer->textShader);\n\n                glActiveTexture(GL_TEXTURE0);\n                glBindTexture(GL_TEXTURE_2D, renderer->fontTextures[0]);\n\n                glActiveTexture(GL_TEXTURE1);\n                glBindTexture(GL_TEXTURE_2D, renderer->fontTextures[1]);\n\n                glActiveTexture(GL_TEXTURE2);\n                glBindTexture(GL_TEXTURE_2D, renderer->fontTextures[2]);\n\n                glActiveTexture(GL_TEXTURE3);\n                glBindTexture(GL_TEXTURE_2D, renderer->fontTextures[3]);\n\n                GLint uScreenLoc = glGetUniformLocation(renderer->textShader, \"uScreen\");\n                glUniform2f(uScreenLoc, renderer->screenWidth, renderer->screenHeight);\n\n                glBindVertexArray(renderer->textVAO);\n                glBindBuffer(GL_ARRAY_BUFFER, renderer->textVBO);\n\n                glBufferSubData(\n                    GL_ARRAY_BUFFER,\n                    0,\n                    sizeof(struct GlyphVtx) * 6 * gVerts->count,\n                    renderer->glyphVtxArray.instData);\n\n                glDrawArrays(GL_TRIANGLES, 0, renderer->glyphVtxArray.count * 6);\n                renderer->totalDrawCallsToOpenGl += 1;\n\n                glBindVertexArray(0);\n                glBindTexture(GL_TEXTURE_2D, 0);\n            }\n            renderer->glyphVtxArray.count = 0;\n\n            if (cmd->commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_START)\n            {\n                Clay_BoundingBox bb = cmd->boundingBox;\n                GLint x = (GLint)bb.x;\n                GLint y = (GLint)(renderer->screenHeight - (bb.y + bb.height));\n                GLsizei w = (GLsizei)bb.width;\n                GLsizei h = (GLsizei)bb.height;\n\n                glEnable(GL_SCISSOR_TEST);\n                glScissor(x, y, w, h);\n            }\n            else\n            {\n                glDisable(GL_SCISSOR_TEST);\n            }\n        }\n    }\n}\n#endif\n#endif"
  },
  {
    "path": "renderers/GLES3/clay_renderer_gles3_loader_stb.c",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <clay.h>\n\n#include <stb_image.h>\n#include <stb_truetype.h>\n\n#include \"clay_renderer_gles3.h\"\n\ntypedef struct LoadedImage\n{\n    unsigned char *data;\n    int width;\n    int height;\n    int channels;\n} LoadedImage;\n\ntypedef struct LoadedImageInternal\n{\n    LoadedImage pub;\n} LoadedImageInternal;\n\nstatic LoadedImageInternal g_imageSlot;\n\nconst LoadedImage *loadImage(const char *path, bool flip)\n{\n    if (!path)\n        return NULL;\n\n    stbi_set_flip_vertically_on_load(flip ? 1 : 0);\n\n    int w = 0;\n    int h = 0;\n    int c = 0;\n\n    unsigned char *data = stbi_load(path, &w, &h, &c, 0);\n    if (!data)\n    {\n        // Failed\n        g_imageSlot.pub.data = NULL;\n        g_imageSlot.pub.width = 0;\n        g_imageSlot.pub.height = 0;\n        g_imageSlot.pub.channels = 0;\n        return NULL;\n    }\n\n    g_imageSlot.pub.data = data;\n    g_imageSlot.pub.width = w;\n    g_imageSlot.pub.height = h;\n    g_imageSlot.pub.channels = c;\n\n    return &g_imageSlot.pub;\n}\n\nvoid freeImage(const LoadedImage *img)\n{\n    if (!img || !img->data)\n        return;\n\n    // cast back to internal container\n    stbi_image_free((void *)img->data);\n\n    // reset slot\n    g_imageSlot.pub.data = NULL;\n    g_imageSlot.pub.width = 0;\n    g_imageSlot.pub.height = 0;\n    g_imageSlot.pub.channels = 0;\n}\n\ntypedef struct Stb_FontData\n{\n    float bakePxH;   // font baking height (e.g. 48.0f)\n    float ascentPx;  // in baked pixels (at bake_px size)\n    float descentPx; // usually negative (at bake_px size)\n    int firstChar;   // e.g. 32\n    int charCount;   // e.g. 96\n    stbtt_bakedchar *cdata;\n    int atlasW;\n    int atlasH;\n} Stb_FontData;\n\nbool Stb_LoadFont(\n    GLuint *textureOut,\n    Stb_FontData *fontOut,\n    const char *ttfPath,\n    float bakePxH, // Height of a char in pixels\n    int atlasW,    // Width of atlas in pixels\n    int atlasH     // Height of atlas in pixels\n)\n{\n    fontOut->firstChar = 32; // ASCII space\n    fontOut->charCount = 96; // 32..127\n    fontOut->bakePxH = bakePxH;\n    fontOut->atlasW = atlasW;\n    fontOut->atlasH = atlasH;\n\n    // allocate baked-char array\n    fontOut->cdata = (stbtt_bakedchar *)malloc(\n        sizeof(stbtt_bakedchar) // Store baked info\n        * fontOut->charCount    // For each char\n    );\n    if (!fontOut->cdata)\n    {\n        fprintf(stderr, \"Cannot allocate cdata\\n\");\n        return false;\n    }\n\n    FILE *f = fopen(ttfPath, \"rb\");\n    if (!f)\n    {\n        fprintf(stderr, \"Could not open font: %s\\n\", ttfPath);\n        return false;\n    }\n\n    fseek(f, 0, SEEK_END);\n    long sz = ftell(f);\n    fseek(f, 0, SEEK_SET);\n\n    unsigned char *ttf_buf = (unsigned char *)malloc(sz);\n    fread(ttf_buf, 1, sz, f);\n    fclose(f);\n\n    // temporary atlas memory\n    unsigned char *atlas = (unsigned char *)malloc(atlasW * atlasH);\n    memset(atlas, 0, atlasW * atlasH);\n\n    // bake\n    int res = stbtt_BakeFontBitmap(\n        ttf_buf,            // raw TTF file\n        0,                  // font index inside TTF (0 = first font)\n        bakePxH,            // pixel height of glyphs to generate\n        atlas,              // OUT: bitmap buffer (unsigned char*)\n        atlasW, atlasH,     // size of bitmap buffer\n        fontOut->firstChar, // first character to bake (e.g., 32 = space)\n        fontOut->charCount, // how many sequential chars to bake\n        fontOut->cdata      // OUT: array of stbtt_bakedchar\n    );\n\n    stbtt_fontinfo fi;\n    if (!stbtt_InitFont(&fi, ttf_buf, stbtt_GetFontOffsetForIndex(ttf_buf, 0)))\n    {\n        return false;\n    }\n\n    int ascent, descent, lineGap;\n    stbtt_GetFontVMetrics(&fi, &ascent, &descent, &lineGap);\n\n    // Convert the font's \"font units\" to pixels proportional to bakePxH size:\n    float scaleForBake = stbtt_ScaleForPixelHeight(&fi, bakePxH);\n\n    fontOut->ascentPx = ascent * scaleForBake;\n    fontOut->descentPx = descent * scaleForBake; // this is typically negative\n\n    free(ttf_buf);\n\n    if (res <= 0)\n    {\n        fprintf(stderr, \"Font baking failed\\n\");\n        free(atlas);\n        free(fontOut->cdata);\n        fontOut->cdata = NULL;\n        return false;\n    }\n\n    // Creating glyphVtxArray atlas texture\n    glGenTextures(1, textureOut);\n    glBindTexture(GL_TEXTURE_2D, *textureOut);\n\n    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);\n\n    glTexImage2D(GL_TEXTURE_2D, 0, GL_R8,\n                 atlasW, atlasH,\n                 0, GL_RED, GL_UNSIGNED_BYTE, atlas);\n\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\n    glBindTexture(GL_TEXTURE_2D, 0);\n\n    free(atlas);\n\n    return true;\n}\n\nstatic inline Clay_Dimensions Stb_MeasureText(\n    Clay_StringSlice glyphVtxArray,\n    Clay_TextElementConfig *config,\n    void *userData)\n{\n    Stb_FontData *fontData = (Stb_FontData *)userData;\n\n    if (!fontData->cdata)\n    {\n        fprintf(\n            stderr,\n            \"MeasureText cannot do anything when cdata is not baked: '%.*s' → %d x %d px\\n\",\n            (int)glyphVtxArray.length, glyphVtxArray.chars, 0, 0);\n        return (Clay_Dimensions){.width = 0, .height = 0};\n    }\n\n    float x = 0.0f;\n    float y = 0.0f;\n\n    const char *str = glyphVtxArray.chars;\n    int len = glyphVtxArray.length;\n\n    float scale = config->fontSize / fontData->bakePxH;\n\n    float letterSpacing = (float)config->letterSpacing;\n    float lineHeight = (config->lineHeight > 0)\n                           ? (float)config->lineHeight\n                           : fontData->bakePxH;\n\n    for (int i = 0; i < len; i++)\n    {\n        unsigned char c = str[i];\n\n        if (c < fontData->firstChar                           // before range\n            || c >= fontData->firstChar + fontData->charCount // after range\n        )\n        {\n            // Unsupported char: treat as space\n            fprintf(stderr, \"illegal char %d\\n\", (int)c);\n            x += fontData->bakePxH * 0.25f;\n            continue;\n        }\n\n        stbtt_bakedchar *b = &fontData->cdata[c - fontData->firstChar];\n\n        // horizontal advance while moving along word characters\n        x += b->xadvance * scale + letterSpacing;\n    }\n\n    float ascent = fontData->ascentPx * scale;\n    float descent = fontData->descentPx * scale; // negative\n    float lineH = (ascent - descent);            // total line height in pixels (at requested fontSize)\n\n    return (Clay_Dimensions){\n        .width = x,\n        .height = y + lineH,\n    };\n}\n\nstatic inline void Stb_RenderText(\n    Clay_RenderCommand *cmd,\n    Gles3_GlyphVtxArray *glyphVtxArray,\n    void *userData)\n{\n    const Clay_TextRenderData *tr = &cmd->renderData.text;\n\n    float cr = tr->textColor.r / 255.0f;\n    float cg = tr->textColor.g / 255.0f;\n    float cb = tr->textColor.b / 255.0f;\n    float ca = tr->textColor.a / 255.0f;\n    float fontToUse = (float)tr->fontId;\n\n    Stb_FontData *fontArray = (Stb_FontData *)userData;\n    Stb_FontData *stbFontData = &fontArray[tr->fontId];\n    if (!stbFontData->cdata)\n        return;\n\n    Clay_StringSlice ss = tr->stringContents;\n    const char *txt = ss.chars;\n    int len = (int)ss.length;\n\n    float scale = tr->fontSize / stbFontData->bakePxH;\n    float ascent = stbFontData->ascentPx * scale; // pixels above baseline\n    float x = cmd->boundingBox.x;\n    float y = cmd->boundingBox.y + ascent; // baseline (note: no descent)\n\n    for (int i = 0; i < len; i++)\n    {\n        char ch = txt[i];\n\n        int idx = ch - stbFontData->firstChar;\n        if (idx < 0 || idx >= stbFontData->charCount)\n        {\n            continue;\n        }\n\n        stbtt_bakedchar *bc = &stbFontData->cdata[idx];\n\n        float gw = (float)(bc->x1 - bc->x0); // glyph width in atlas pixels\n        float gh = (float)(bc->y1 - bc->y0); // glyph height\n\n        float sw = gw * scale; // scaled width on screen\n        float sh = gh * scale; // scaled height\n\n        float ox = bc->xoff * scale; // baseline offset\n        float oy = bc->yoff * scale;\n\n        // top-left corner on screen (pixel coords)\n        float x0 = x + ox;\n        float y0 = y + oy;\n        float x1 = x0 + sw;\n        float y1 = y0 + sh;\n\n        // atlas size (you can make it configurable later)\n        float atlasW = stbFontData->atlasW;\n        float atlasH = stbFontData->atlasH;\n\n        float u0 = bc->x0 / atlasW;\n        float v0 = bc->y0 / atlasH;\n        float u1 = bc->x1 / atlasW;\n        float v1 = bc->y1 / atlasH;\n\n        // append 6 vertices (two triangles) to your buffer\n        GlyphVtx *v = &glyphVtxArray->instData[glyphVtxArray->count * 6];\n\n        v[0] = (GlyphVtx){x0, y0, u0, v0, cr, cg, cb, ca, fontToUse};\n        v[1] = (GlyphVtx){x1, y0, u1, v0, cr, cg, cb, ca, fontToUse};\n        v[2] = (GlyphVtx){x0, y1, u0, v1, cr, cg, cb, ca, fontToUse};\n\n        v[3] = (GlyphVtx){x0, y1, u0, v1, cr, cg, cb, ca, fontToUse};\n        v[4] = (GlyphVtx){x1, y0, u1, v0, cr, cg, cb, ca, fontToUse};\n        v[5] = (GlyphVtx){x1, y1, u1, v1, cr, cg, cb, ca, fontToUse};\n\n        // advance pen by baked xadvance + letter spacing\n        x += (bc->xadvance * scale) + tr->letterSpacing;\n\n        // prevent buffer overrun\n        if (glyphVtxArray->count >= glyphVtxArray->capacity)\n        {\n            break;\n        }\n        glyphVtxArray->count++;\n    }\n}\n\nbool Stb_LoadImage(GLuint *textureOut, const char *path)\n{\n    const LoadedImage *li = loadImage(path, false);\n    if (!li || !li->data)\n    {\n        fprintf(stderr, \"Failed to load texture at: %s\\n\", path);\n        return false;\n    }\n\n    glGenTextures(1, textureOut);\n    glBindTexture(GL_TEXTURE_2D, *textureOut);\n\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);\n\n    GLenum format = (li->channels == 4) ? GL_RGBA : GL_RGB;\n\n    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);\n\n    glTexImage2D(\n        GL_TEXTURE_2D, // target\n        0,             // level\n        format,        // internal format int\n        li->width,\n        li->height,\n        0,                // border\n        format,           // format, GLEnum\n        GL_UNSIGNED_BYTE, // Type\n        li->data          // pixels\n    );\n\n    glGenerateMipmap(GL_TEXTURE_2D);\n\n    freeImage(li);\n    return true;\n}"
  },
  {
    "path": "renderers/SDL2/README",
    "content": "Note: on Mac OSX, SDL2 for some reason decides to automatically disable momentum scrolling on macbook trackpads.\nYou can re enable it in objective C using:\n\n```C\n[[NSUserDefaults standardUserDefaults] setBool: YES\n                                       forKey: @\"AppleMomentumScrollSupported\"];\n```\n"
  },
  {
    "path": "renderers/SDL2/clay_renderer_SDL2.c",
    "content": "#include \"../../clay.h\"\n#include <SDL.h>\n#include <SDL_ttf.h>\n#include <SDL_image.h>\n#include <stdio.h>\n#include <math.h>\n\n#ifndef M_PI\n    #define M_PI 3.14159\n#endif\n\n#define CLAY_COLOR_TO_SDL_COLOR_ARGS(color) color.r, color.g, color.b, color.a\n\ntypedef struct\n{\n    uint32_t fontId;\n    TTF_Font *font;\n} SDL2_Font;\n\n\nstatic Clay_Dimensions SDL2_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData)\n{\n    SDL2_Font *fonts = (SDL2_Font*)userData;\n\n    TTF_Font *font = fonts[config->fontId].font;\n    TTF_SetFontSize(font, config->fontSize);\n    char *chars = (char *)calloc(text.length + 1, 1);\n    memcpy(chars, text.chars, text.length);\n    int width = 0;\n    int height = 0;\n    if (TTF_SizeUTF8(font, chars, &width, &height) < 0) {\n        fprintf(stderr, \"Error: could not measure text: %s\\n\", TTF_GetError());\n        exit(1);\n    }\n    free(chars);\n    return (Clay_Dimensions) {\n            .width = (float)width,\n            .height = (float)height,\n    };\n}\n\n/* Global for convenience. Even in 4K this is enough for smooth curves (low radius or rect size coupled with\n * no AA or low resolution might make it appear as jagged curves) */\nstatic int NUM_CIRCLE_SEGMENTS = 16;\n\n//all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles.\nstatic void SDL_RenderFillRoundedRect(SDL_Renderer* renderer, const SDL_FRect rect, const float cornerRadius, const Clay_Color _color) {\n    const SDL_Color color = (SDL_Color) {\n            .r = (Uint8)_color.r,\n            .g = (Uint8)_color.g,\n            .b = (Uint8)_color.b,\n            .a = (Uint8)_color.a,\n    };\n\n    int indexCount = 0, vertexCount = 0;\n\n    const float maxRadius = SDL_min(rect.w, rect.h) / 2.0f;\n    const float clampedRadius = SDL_min(cornerRadius, maxRadius);\n\n    const int numCircleSegments = SDL_max(NUM_CIRCLE_SEGMENTS, (int)clampedRadius * 0.5f);\n\n    SDL_Vertex vertices[512];\n    int indices[512];\n\n    //define center rectangle\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + clampedRadius}, color, {0, 0} }; //0 center TL\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + clampedRadius}, color, {1, 0} }; //1 center TR\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //2 center BR\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //3 center BL\n\n    indices[indexCount++] = 0;\n    indices[indexCount++] = 1;\n    indices[indexCount++] = 3;\n    indices[indexCount++] = 1;\n    indices[indexCount++] = 2;\n    indices[indexCount++] = 3;\n\n    //define rounded corners as triangle fans\n    const float step = (M_PI / 2) / numCircleSegments;\n    for (int i = 0; i < numCircleSegments; i++) {\n        const float angle1 = (float)i * step;\n        const float angle2 = ((float)i + 1.0f) * step;\n\n        for (int j = 0; j < 4; j++) {  // Iterate over four corners\n            float cx, cy, signX, signY;\n\n            switch (j) {\n            case 0: cx = rect.x + clampedRadius; cy = rect.y + clampedRadius; signX = -1; signY = -1; break; // Top-left\n            case 1: cx = rect.x + rect.w - clampedRadius; cy = rect.y + clampedRadius; signX = 1; signY = -1; break; // Top-right\n            case 2: cx = rect.x + rect.w - clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = 1; signY = 1; break; // Bottom-right\n            case 3: cx = rect.x + clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = -1; signY = 1; break; // Bottom-left\n            default: return;\n            }\n\n            vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle1) * clampedRadius * signX, cy + SDL_sinf(angle1) * clampedRadius * signY}, color, {0, 0} };\n            vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle2) * clampedRadius * signX, cy + SDL_sinf(angle2) * clampedRadius * signY}, color, {0, 0} };\n\n            indices[indexCount++] = j;  // Connect to corresponding central rectangle vertex\n            indices[indexCount++] = vertexCount - 2;\n            indices[indexCount++] = vertexCount - 1;\n        }\n    }\n\n    //Define edge rectangles\n    // Top edge\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y}, color, {0, 0} }; //TL\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y}, color, {1, 0} }; //TR\n\n    indices[indexCount++] = 0;\n    indices[indexCount++] = vertexCount - 2; //TL\n    indices[indexCount++] = vertexCount - 1; //TR\n    indices[indexCount++] = 1;\n    indices[indexCount++] = 0;\n    indices[indexCount++] = vertexCount - 1; //TR\n    // Right edge\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + clampedRadius}, color, {1, 0} }; //RT\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //RB\n\n    indices[indexCount++] = 1;\n    indices[indexCount++] = vertexCount - 2; //RT\n    indices[indexCount++] = vertexCount - 1; //RB\n    indices[indexCount++] = 2;\n    indices[indexCount++] = 1;\n    indices[indexCount++] = vertexCount - 1; //RB\n    // Bottom edge\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h}, color, {1, 1} }; //BR\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h}, color, {0, 1} }; //BL\n\n    indices[indexCount++] = 2;\n    indices[indexCount++] = vertexCount - 2; //BR\n    indices[indexCount++] = vertexCount - 1; //BL\n    indices[indexCount++] = 3;\n    indices[indexCount++] = 2;\n    indices[indexCount++] = vertexCount - 1; //BL\n    // Left edge\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //LB\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + clampedRadius}, color, {0, 0} }; //LT\n\n    indices[indexCount++] = 3;\n    indices[indexCount++] = vertexCount - 2; //LB\n    indices[indexCount++] = vertexCount - 1; //LT\n    indices[indexCount++] = 0;\n    indices[indexCount++] = 3;\n    indices[indexCount++] = vertexCount - 1; //LT\n\n    // Render everything\n    SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount);\n}\n\n//all rendering is performed by a single SDL call, using twi sets of arcing triangles, inner and outer, that fit together; along with two tringles to fill the end gaps.\nstatic void SDL_RenderCornerBorder(SDL_Renderer *renderer, Clay_BoundingBox* boundingBox, Clay_BorderRenderData* config, int cornerIndex, Clay_Color _color){\n    /////////////////////////////////\n    //The arc is constructed of outer triangles and inner triangles (if needed).\n    //First three vertices are first outer triangle's vertices\n    //Each two vertices after that are the inner-middle and second-outer vertex of \n    //each outer triangle after the first, because there first-outer vertex is equal to the\n    //second-outer vertex of the previous triangle. Indices set accordingly.\n    //The final two vertices are the missing vertices for the first and last inner triangles (if needed)\n    //Everything is in clockwise order (CW).\n    /////////////////////////////////\n    const SDL_Color color = (SDL_Color) {\n        .r = (Uint8)_color.r,\n        .g = (Uint8)_color.g,\n        .b = (Uint8)_color.b,\n        .a = (Uint8)_color.a,\n    };\n\n    float centerX, centerY, outerRadius, clampedRadius, startAngle, borderWidth;\n    const float maxRadius = SDL_min(boundingBox->width, boundingBox->height) / 2.0f;\n    \n    SDL_Vertex vertices[512];\n    int indices[512];\n    int indexCount = 0, vertexCount = 0;\n\n    switch (cornerIndex) {\n        case(0):\n            startAngle = M_PI; \n            outerRadius = SDL_min(config->cornerRadius.topLeft, maxRadius);\n            centerX = boundingBox->x + outerRadius; \n            centerY = boundingBox->y + outerRadius; \n            borderWidth = config->width.top;\n        break;\n        case(1):\n            startAngle = 3*M_PI/2;\n            outerRadius = SDL_min(config->cornerRadius.topRight, maxRadius);\n            centerX = boundingBox->x + boundingBox->width - outerRadius; \n            centerY = boundingBox->y + outerRadius; \n            borderWidth = config->width.top;\n            break;\n        case(2):\n            startAngle = 0;\n            outerRadius = SDL_min(config->cornerRadius.bottomRight, maxRadius);\n            centerX = boundingBox->x + boundingBox->width - outerRadius; \n            centerY = boundingBox->y + boundingBox->height - outerRadius; \n            borderWidth = config->width.bottom;\n            break;\n        case(3):\n            startAngle = M_PI/2;\n            outerRadius = SDL_min(config->cornerRadius.bottomLeft, maxRadius);\n            centerX = boundingBox->x + outerRadius; \n            centerY = boundingBox->y + boundingBox->height - outerRadius; \n            borderWidth = config->width.bottom;\n            break;\n        default: break;\n    }\n    \n    const float innerRadius = outerRadius - borderWidth;\n    const int minNumOuterTriangles = NUM_CIRCLE_SEGMENTS;\n    const int numOuterTriangles = SDL_max(minNumOuterTriangles, ceilf(outerRadius * 0.5f));\n    const float angleStep = M_PI / (2.0*(float)numOuterTriangles);\n\n    //outer triangles, in CW order\n    for (int i = 0; i < numOuterTriangles; i++) { \n        float angle1 =  startAngle + i*angleStep; //first-outer vertex angle\n        float angle2 =  startAngle + ((float)i + 0.5) * angleStep; //inner-middle vertex angle\n        float angle3 =  startAngle + (i+1)*angleStep; // second-outer vertex angle\n\n        if( i == 0){ //first outer triangle\n            vertices[vertexCount++] = (SDL_Vertex){ {centerX + SDL_cosf(angle1) * outerRadius, centerY + SDL_sinf(angle1) * outerRadius}, color, {0, 0} }; //vertex index = 0\n        }\n        indices[indexCount++] = vertexCount - 1; //will be second-outer vertex of last outer triangle if not first outer triangle.\n\n        vertices[vertexCount++] = (innerRadius > 0)?\n            (SDL_Vertex){ {centerX + SDL_cosf(angle2) * (innerRadius), centerY + SDL_sinf(angle2) * (innerRadius)}, color, {0, 0}}:\n            (SDL_Vertex){ {centerX, centerY }, color, {0, 0}};\n        indices[indexCount++] = vertexCount - 1;\n\n        vertices[vertexCount++] = (SDL_Vertex){ {centerX + SDL_cosf(angle3) * outerRadius, centerY + SDL_sinf(angle3) * outerRadius}, color, {0, 0} };\n        indices[indexCount++] = vertexCount - 1;\n    }\n\n    if(innerRadius > 0){\n        // inner triangles in CW order (except the first and last)\n        for (int i = 0; i < numOuterTriangles - 1; i++){ //skip the last outer triangle\n            if(i==0){ //first outer triangle -> second inner triangle\n                indices[indexCount++] = 1; //inner-middle vertex of first outer triangle\n                indices[indexCount++] = 2; //second-outer vertex of first outer triangle\n                indices[indexCount++] = 3; //innder-middle vertex of second-outer triangle\n            }else{\n                int baseIndex = 3; //skip first outer triangle\n                indices[indexCount++] = baseIndex + (i-1)*2; // inner-middle vertex of current outer triangle\n                indices[indexCount++] = baseIndex + (i-1)*2 + 1; // second-outer vertex of current outer triangle\n                indices[indexCount++] = baseIndex + (i-1)*2 + 2; // inner-middle vertex of next outer triangle\n            }\n        }\n\n        float endAngle = startAngle + M_PI/2.0;\n\n        //last inner triangle\n        indices[indexCount++] = vertexCount - 2; //inner-middle vertex of last outer triangle\n        indices[indexCount++] = vertexCount - 1; //second-outer vertex of last outer triangle\n        vertices[vertexCount++] = (SDL_Vertex){ {centerX + SDL_cosf(endAngle) * innerRadius, centerY + SDL_sinf(endAngle) * innerRadius}, color, {0, 0} }; //missing vertex\n        indices[indexCount++] = vertexCount - 1; \n        \n        // //first inner triangle\n        indices[indexCount++] = 0; //first-outer vertex of first outer triangle\n        indices[indexCount++] = 1; //inner-middle vertex of first outer triangle\n        vertices[vertexCount++] = (SDL_Vertex){ {centerX + SDL_cosf(startAngle) * innerRadius, centerY + SDL_sinf(startAngle) * innerRadius}, color, {0, 0} }; //missing vertex\n        indices[indexCount++] = vertexCount - 1; \n    }\n\n    SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount);\n}\n\nSDL_Rect currentClippingRectangle;\n\nstatic void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray renderCommands, SDL2_Font *fonts)\n{\n    for (uint32_t i = 0; i < renderCommands.length; i++)\n    {\n        Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i);\n        Clay_BoundingBox boundingBox = renderCommand->boundingBox;\n        switch (renderCommand->commandType)\n        {\n            case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;\n                Clay_Color color = config->backgroundColor;\n                SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);\n                SDL_FRect rect = (SDL_FRect) {\n                        .x = boundingBox.x,\n                        .y = boundingBox.y,\n                        .w = boundingBox.width,\n                        .h = boundingBox.height,\n                };\n                if (config->cornerRadius.topLeft > 0) {\n                    SDL_RenderFillRoundedRect(renderer, rect, config->cornerRadius.topLeft, color);\n                }\n                else {\n                    SDL_RenderFillRectF(renderer, &rect);\n                }\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_TEXT: {\n                Clay_TextRenderData *config = &renderCommand->renderData.text;\n                char *cloned = (char *)calloc(config->stringContents.length + 1, 1);\n                memcpy(cloned, config->stringContents.chars, config->stringContents.length);\n                TTF_Font* font = fonts[config->fontId].font;\n                TTF_SetFontSize(font, config->fontSize);\n                SDL_Surface *surface = TTF_RenderUTF8_Blended(font, cloned, (SDL_Color) {\n                        .r = (Uint8)config->textColor.r,\n                        .g = (Uint8)config->textColor.g,\n                        .b = (Uint8)config->textColor.b,\n                        .a = (Uint8)config->textColor.a,\n                });\n                SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);\n\n                SDL_Rect destination = (SDL_Rect){\n                        .x = boundingBox.x,\n                        .y = boundingBox.y,\n                        .w = boundingBox.width,\n                        .h = boundingBox.height,\n                };\n                SDL_RenderCopy(renderer, texture, NULL, &destination);\n\n                SDL_DestroyTexture(texture);\n                SDL_FreeSurface(surface);\n                free(cloned);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {\n                currentClippingRectangle = (SDL_Rect) {\n                        .x = boundingBox.x,\n                        .y = boundingBox.y,\n                        .w = boundingBox.width,\n                        .h = boundingBox.height,\n                };\n                SDL_RenderSetClipRect(renderer, &currentClippingRectangle);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {\n                SDL_RenderSetClipRect(renderer, NULL);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_IMAGE: {\n                Clay_ImageRenderData *config = &renderCommand->renderData.image;\n\n                SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, config->imageData);\n\n                SDL_Rect destination = (SDL_Rect){\n                    .x = boundingBox.x,\n                    .y = boundingBox.y,\n                    .w = boundingBox.width,\n                    .h = boundingBox.height,\n                };\n\n                SDL_RenderCopy(renderer, texture, NULL, &destination);\n\n                SDL_DestroyTexture(texture);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_BORDER: {\n                Clay_BorderRenderData *config = &renderCommand->renderData.border;\n                SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->color));\n\n                if(boundingBox.width > 0 & boundingBox.height > 0){\n                    const float maxRadius = SDL_min(boundingBox.width, boundingBox.height) / 2.0f;\n\n                    if (config->width.left > 0) {\n                        const float clampedRadiusTop = SDL_min((float)config->cornerRadius.topLeft, maxRadius);\n                        const float clampedRadiusBottom = SDL_min((float)config->cornerRadius.bottomLeft, maxRadius);\n                        SDL_FRect rect = { \n                            boundingBox.x, \n                            boundingBox.y + clampedRadiusTop, \n                            (float)config->width.left, \n                            (float)boundingBox.height - clampedRadiusTop - clampedRadiusBottom\n                        };\n                        SDL_RenderFillRectF(renderer, &rect);\n                    }\n    \n                    if (config->width.right > 0) {\n                        const float clampedRadiusTop = SDL_min((float)config->cornerRadius.topRight, maxRadius);\n                        const float clampedRadiusBottom = SDL_min((float)config->cornerRadius.bottomRight, maxRadius);\n                        SDL_FRect rect = { \n                            boundingBox.x + boundingBox.width - config->width.right,\n                            boundingBox.y + clampedRadiusTop,\n                            (float)config->width.right,\n                            (float)boundingBox.height - clampedRadiusTop - clampedRadiusBottom\n                        };\n                        SDL_RenderFillRectF(renderer, &rect);\n                    }\n    \n                    if (config->width.top > 0) {\n                        const float clampedRadiusLeft = SDL_min((float)config->cornerRadius.topLeft, maxRadius);\n                        const float clampedRadiusRight = SDL_min((float)config->cornerRadius.topRight, maxRadius);\n                        SDL_FRect rect = { \n                            boundingBox.x + clampedRadiusLeft, \n                            boundingBox.y, \n                            boundingBox.width - clampedRadiusLeft - clampedRadiusRight, \n                            (float)config->width.top };\n                        SDL_RenderFillRectF(renderer, &rect);\n                    }\n    \n                    if (config->width.bottom > 0) {\n                        const float clampedRadiusLeft = SDL_min((float)config->cornerRadius.bottomLeft, maxRadius);\n                        const float clampedRadiusRight = SDL_min((float)config->cornerRadius.bottomRight, maxRadius);\n                        SDL_FRect rect = { \n                            boundingBox.x + clampedRadiusLeft, \n                            boundingBox.y + boundingBox.height - config->width.bottom, \n                            boundingBox.width - clampedRadiusLeft - clampedRadiusRight, \n                            (float)config->width.bottom \n                        };\n                        SDL_RenderFillRectF(renderer, &rect);\n                    }\n    \n                    //corner index: 0->3 topLeft -> CW -> bottonLeft\n                    if (config->width.top > 0 & config->cornerRadius.topLeft > 0) {\n                        SDL_RenderCornerBorder(renderer, &boundingBox, config, 0, config->color);\n                    }\n\n                    if (config->width.top > 0 & config->cornerRadius.topRight> 0) {\n                        SDL_RenderCornerBorder(renderer, &boundingBox, config, 1, config->color);\n                    }\n\n                    if (config->width.bottom > 0 & config->cornerRadius.bottomRight > 0) {\n                        SDL_RenderCornerBorder(renderer, &boundingBox, config, 2, config->color);\n                    }\n\n                    if (config->width.bottom > 0 & config->cornerRadius.bottomLeft > 0) {\n                        SDL_RenderCornerBorder(renderer, &boundingBox, config, 3, config->color);\n                    }\n                }\n\n                break;\n            }\n            default: {\n                fprintf(stderr, \"Error: unhandled render command: %d\\n\", renderCommand->commandType);\n                exit(1);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "renderers/SDL3/clay_renderer_SDL3.c",
    "content": "#include \"../../clay.h\"\n#include <SDL3/SDL_main.h>\n#include <SDL3/SDL.h>\n#include <SDL3_ttf/SDL_ttf.h>\n#include <SDL3_image/SDL_image.h>\n\ntypedef struct {\n    SDL_Renderer *renderer;\n    TTF_TextEngine *textEngine;\n    TTF_Font **fonts;\n} Clay_SDL3RendererData;\n\n/* Global for convenience. Even in 4K this is enough for smooth curves (low radius or rect size coupled with\n * no AA or low resolution might make it appear as jagged curves) */\nstatic int NUM_CIRCLE_SEGMENTS = 16;\n\n//all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles.\nstatic void SDL_Clay_RenderFillRoundedRect(Clay_SDL3RendererData *rendererData, const SDL_FRect rect, const float cornerRadius, const Clay_Color _color) {\n    const SDL_FColor color = { _color.r/255, _color.g/255, _color.b/255, _color.a/255 };\n\n    int indexCount = 0, vertexCount = 0;\n\n    const float minRadius = SDL_min(rect.w, rect.h) / 2.0f;\n    const float clampedRadius = SDL_min(cornerRadius, minRadius);\n\n    const int numCircleSegments = SDL_max(NUM_CIRCLE_SEGMENTS, (int) clampedRadius * 0.5f);\n\n    int totalVertices = 4 + (4 * (numCircleSegments * 2)) + 2*4;\n    int totalIndices = 6 + (4 * (numCircleSegments * 3)) + 6*4;\n\n    SDL_Vertex vertices[totalVertices];\n    int indices[totalIndices];\n\n    //define center rectangle\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + clampedRadius}, color, {0, 0} }; //0 center TL\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + clampedRadius}, color, {1, 0} }; //1 center TR\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //2 center BR\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //3 center BL\n\n    indices[indexCount++] = 0;\n    indices[indexCount++] = 1;\n    indices[indexCount++] = 3;\n    indices[indexCount++] = 1;\n    indices[indexCount++] = 2;\n    indices[indexCount++] = 3;\n\n    //define rounded corners as triangle fans\n    const float step = (SDL_PI_F/2) / numCircleSegments;\n    for (int i = 0; i < numCircleSegments; i++) {\n        const float angle1 = (float)i * step;\n        const float angle2 = ((float)i + 1.0f) * step;\n\n        for (int j = 0; j < 4; j++) {  // Iterate over four corners\n            float cx, cy, signX, signY;\n\n            switch (j) {\n                case 0: cx = rect.x + clampedRadius; cy = rect.y + clampedRadius; signX = -1; signY = -1; break; // Top-left\n                case 1: cx = rect.x + rect.w - clampedRadius; cy = rect.y + clampedRadius; signX = 1; signY = -1; break; // Top-right\n                case 2: cx = rect.x + rect.w - clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = 1; signY = 1; break; // Bottom-right\n                case 3: cx = rect.x + clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = -1; signY = 1; break; // Bottom-left\n                default: return;\n            }\n\n            vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle1) * clampedRadius * signX, cy + SDL_sinf(angle1) * clampedRadius * signY}, color, {0, 0} };\n            vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle2) * clampedRadius * signX, cy + SDL_sinf(angle2) * clampedRadius * signY}, color, {0, 0} };\n\n            indices[indexCount++] = j;  // Connect to corresponding central rectangle vertex\n            indices[indexCount++] = vertexCount - 2;\n            indices[indexCount++] = vertexCount - 1;\n        }\n    }\n\n    //Define edge rectangles\n    // Top edge\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y}, color, {0, 0} }; //TL\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y}, color, {1, 0} }; //TR\n\n    indices[indexCount++] = 0;\n    indices[indexCount++] = vertexCount - 2; //TL\n    indices[indexCount++] = vertexCount - 1; //TR\n    indices[indexCount++] = 1;\n    indices[indexCount++] = 0;\n    indices[indexCount++] = vertexCount - 1; //TR\n    // Right edge\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + clampedRadius}, color, {1, 0} }; //RT\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //RB\n\n    indices[indexCount++] = 1;\n    indices[indexCount++] = vertexCount - 2; //RT\n    indices[indexCount++] = vertexCount - 1; //RB\n    indices[indexCount++] = 2;\n    indices[indexCount++] = 1;\n    indices[indexCount++] = vertexCount - 1; //RB\n    // Bottom edge\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h}, color, {1, 1} }; //BR\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h}, color, {0, 1} }; //BL\n\n    indices[indexCount++] = 2;\n    indices[indexCount++] = vertexCount - 2; //BR\n    indices[indexCount++] = vertexCount - 1; //BL\n    indices[indexCount++] = 3;\n    indices[indexCount++] = 2;\n    indices[indexCount++] = vertexCount - 1; //BL\n    // Left edge\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //LB\n    vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + clampedRadius}, color, {0, 0} }; //LT\n\n    indices[indexCount++] = 3;\n    indices[indexCount++] = vertexCount - 2; //LB\n    indices[indexCount++] = vertexCount - 1; //LT\n    indices[indexCount++] = 0;\n    indices[indexCount++] = 3;\n    indices[indexCount++] = vertexCount - 1; //LT\n\n    // Render everything\n    SDL_RenderGeometry(rendererData->renderer, NULL, vertices, vertexCount, indices, indexCount);\n}\n\nstatic void SDL_Clay_RenderArc(Clay_SDL3RendererData *rendererData, const SDL_FPoint center, const float radius, const float startAngle, const float endAngle, const float thickness, const Clay_Color color) {\n    SDL_SetRenderDrawColor(rendererData->renderer, color.r, color.g, color.b, color.a);\n\n    const float radStart = startAngle * (SDL_PI_F / 180.0f);\n    const float radEnd = endAngle * (SDL_PI_F / 180.0f);\n\n    const int numCircleSegments = SDL_max(NUM_CIRCLE_SEGMENTS, (int)(radius * 1.5f)); //increase circle segments for larger circles, 1.5 is arbitrary.\n\n    const float angleStep = (radEnd - radStart) / (float)numCircleSegments;\n    const float thicknessStep = 0.4f; //arbitrary value to avoid overlapping lines. Changing THICKNESS_STEP or numCircleSegments might cause artifacts.\n\n    for (float t = thicknessStep; t < thickness - thicknessStep; t += thicknessStep) {\n        SDL_FPoint points[numCircleSegments + 1];\n        const float clampedRadius = SDL_max(radius - t, 1.0f);\n\n        for (int i = 0; i <= numCircleSegments; i++) {\n            const float angle = radStart + i * angleStep;\n            points[i] = (SDL_FPoint){\n                    SDL_roundf(center.x + SDL_cosf(angle) * clampedRadius),\n                    SDL_roundf(center.y + SDL_sinf(angle) * clampedRadius) };\n        }\n        SDL_RenderLines(rendererData->renderer, points, numCircleSegments + 1);\n    }\n}\n\nSDL_Rect currentClippingRectangle;\n\nstatic void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Clay_RenderCommandArray *rcommands)\n{\n    for (size_t i = 0; i < rcommands->length; i++) {\n        Clay_RenderCommand *rcmd = Clay_RenderCommandArray_Get(rcommands, i);\n        const Clay_BoundingBox bounding_box = rcmd->boundingBox;\n        const SDL_FRect rect = { (int)bounding_box.x, (int)bounding_box.y, (int)bounding_box.width, (int)bounding_box.height };\n\n        switch (rcmd->commandType) {\n            case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                Clay_RectangleRenderData *config = &rcmd->renderData.rectangle;\n                SDL_SetRenderDrawBlendMode(rendererData->renderer, SDL_BLENDMODE_BLEND);\n                SDL_SetRenderDrawColor(rendererData->renderer, config->backgroundColor.r, config->backgroundColor.g, config->backgroundColor.b, config->backgroundColor.a);\n                if (config->cornerRadius.topLeft > 0) {\n                    SDL_Clay_RenderFillRoundedRect(rendererData, rect, config->cornerRadius.topLeft, config->backgroundColor);\n                } else {\n                    SDL_RenderFillRect(rendererData->renderer, &rect);\n                }\n            } break;\n            case CLAY_RENDER_COMMAND_TYPE_TEXT: {\n                Clay_TextRenderData *config = &rcmd->renderData.text;\n                TTF_Font *font = rendererData->fonts[config->fontId];\n                TTF_SetFontSize(font, config->fontSize);\n                TTF_Text *text = TTF_CreateText(rendererData->textEngine, font, config->stringContents.chars, config->stringContents.length);\n                TTF_SetTextColor(text, config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a);\n                TTF_DrawRendererText(text, rect.x, rect.y);\n                TTF_DestroyText(text);\n            } break;\n            case CLAY_RENDER_COMMAND_TYPE_BORDER: {\n                Clay_BorderRenderData *config = &rcmd->renderData.border;\n\n                const float minRadius = SDL_min(rect.w, rect.h) / 2.0f;\n                const Clay_CornerRadius clampedRadii = {\n                    .topLeft = SDL_min(config->cornerRadius.topLeft, minRadius),\n                    .topRight = SDL_min(config->cornerRadius.topRight, minRadius),\n                    .bottomLeft = SDL_min(config->cornerRadius.bottomLeft, minRadius),\n                    .bottomRight = SDL_min(config->cornerRadius.bottomRight, minRadius)\n                };\n                //edges\n                SDL_SetRenderDrawColor(rendererData->renderer, config->color.r, config->color.g, config->color.b, config->color.a);\n                if (config->width.left > 0) {\n                    const float starting_y = rect.y + clampedRadii.topLeft;\n                    const float length = rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft;\n                    SDL_FRect line = { rect.x - 1, starting_y, config->width.left, length };\n                    SDL_RenderFillRect(rendererData->renderer, &line);\n                }\n                if (config->width.right > 0) {\n                    const float starting_x = rect.x + rect.w - (float)config->width.right + 1;\n                    const float starting_y = rect.y + clampedRadii.topRight;\n                    const float length = rect.h - clampedRadii.topRight - clampedRadii.bottomRight;\n                    SDL_FRect line = { starting_x, starting_y, config->width.right, length };\n                    SDL_RenderFillRect(rendererData->renderer, &line);\n                }\n                if (config->width.top > 0) {\n                    const float starting_x = rect.x + clampedRadii.topLeft;\n                    const float length = rect.w - clampedRadii.topLeft - clampedRadii.topRight;\n                    SDL_FRect line = { starting_x, rect.y - 1, length, config->width.top };\n                    SDL_RenderFillRect(rendererData->renderer, &line);\n                }\n                if (config->width.bottom > 0) {\n                    const float starting_x = rect.x + clampedRadii.bottomLeft;\n                    const float starting_y = rect.y + rect.h - (float)config->width.bottom + 1;\n                    const float length = rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight;\n                    SDL_FRect line = { starting_x, starting_y, length, config->width.bottom };\n                    SDL_SetRenderDrawColor(rendererData->renderer, config->color.r, config->color.g, config->color.b, config->color.a);\n                    SDL_RenderFillRect(rendererData->renderer, &line);\n                }\n                //corners\n                if (config->cornerRadius.topLeft > 0) {\n                    const float centerX = rect.x + clampedRadii.topLeft -1;\n                    const float centerY = rect.y + clampedRadii.topLeft - 1;\n                    SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topLeft,\n                        180.0f, 270.0f, config->width.top, config->color);\n                }\n                if (config->cornerRadius.topRight > 0) {\n                    const float centerX = rect.x + rect.w - clampedRadii.topRight;\n                    const float centerY = rect.y + clampedRadii.topRight - 1;\n                    SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topRight,\n                        270.0f, 360.0f, config->width.top, config->color);\n                }\n                if (config->cornerRadius.bottomLeft > 0) {\n                    const float centerX = rect.x + clampedRadii.bottomLeft -1;\n                    const float centerY = rect.y + rect.h - clampedRadii.bottomLeft;\n                    SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomLeft,\n                        90.0f, 180.0f, config->width.bottom, config->color);\n                }\n                if (config->cornerRadius.bottomRight > 0) {\n                    const float centerX = rect.x + rect.w - clampedRadii.bottomRight;\n                    const float centerY = rect.y + rect.h - clampedRadii.bottomRight;\n                    SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomRight,\n                        0.0f, 90.0f, config->width.bottom, config->color);\n                }\n\n            } break;\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {\n                Clay_BoundingBox boundingBox = rcmd->boundingBox;\n                currentClippingRectangle = (SDL_Rect) {\n                        .x = boundingBox.x,\n                        .y = boundingBox.y,\n                        .w = boundingBox.width,\n                        .h = boundingBox.height,\n                };\n                SDL_SetRenderClipRect(rendererData->renderer, &currentClippingRectangle);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {\n                SDL_SetRenderClipRect(rendererData->renderer, NULL);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_IMAGE: {\n                SDL_Texture *texture = (SDL_Texture *)rcmd->renderData.image.imageData;\n                const SDL_FRect dest = { rect.x, rect.y, rect.w, rect.h };\n                SDL_RenderTexture(rendererData->renderer, texture, NULL, &dest);\n                break;\n            }\n            default:\n                SDL_Log(\"Unknown render command type: %d\", rcmd->commandType);\n        }\n    }\n}\n"
  },
  {
    "path": "renderers/cairo/clay_renderer_cairo.c",
    "content": "// Copyright (c) 2024 Justin Andreas Lacoste (@27justin)\n//\n// This software is provided 'as-is', without any express or implied warranty.\n// In no event will the authors be held liable for any damages arising from the\n// use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n//     1. The origin of this software must not be misrepresented; you must not\n//     claim that you wrote the original software. If you use this software in a\n//     product, an acknowledgment in the product documentation would be\n//     appreciated but is not required.\n//\n//     2. Altered source versions must be plainly marked as such, and must not\n//     be misrepresented as being the original software.\n//\n//     3. This notice may not be removed or altered from any source\n//     distribution.\n//\n// SPDX-License-Identifier: Zlib\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n\n#define CLAY_IMPLEMENTATION\n#include \"../../clay.h\"\n\n#include <cairo/cairo.h>\n\n////////////////////////////////\n//\n// Public API\n//\n\n// Initialize the internal cairo pointer with the user provided instance.\n// This is REQUIRED before calling Clay_Cairo_Render.\nvoid Clay_Cairo_Initialize(cairo_t *cairo);\n\n// Render the command queue to the `cairo_t*` instance you called\n// `Clay_Cairo_Initialize` on.\nvoid Clay_Cairo_Render(Clay_RenderCommandArray commands, char** fonts);\n////////////////////////////////\n\n\n////////////////////////////////\n// Convencience macros\n//\n#define CLAY_TO_CAIRO(color) color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0\n#define DEG2RAD(degrees) (degrees * ( M_PI / 180.0 ) )\n////////////////////////////////\n\n\n////////////////////////////////\n// Implementation\n//\n\n// Cairo instance\nstatic cairo_t *Clay__Cairo = NULL;\n\n// Return a null-terminated copy of Clay_String `str`.\n// Callee is required to free.\nstatic inline char *Clay_Cairo__NullTerminate(Clay_String *str) {\n\tchar *copy = (char*) malloc(str->length + 1);\n\tif (!copy) {\n\t\tfprintf(stderr, \"Memory allocation failed\\n\");\n\t\treturn NULL;\n\t}\n\tmemcpy(copy, str->chars, str->length);\n\tcopy[str->length] = '\\0';\n\treturn copy;\n}\n\n// Measure text using cairo's *toy* text API.\nstatic inline Clay_Dimensions Clay_Cairo_MeasureText(Clay_StringSlice str, Clay_TextElementConfig *config, void *userData) {\n\t// Edge case: Clay computes the width of a whitespace character\n\t// once.  Cairo does not factor in whitespaces when computing text\n\t// extents, this edge-case serves as a short-circuit to introduce\n\t// (somewhat) sensible values into Clay.\n    char** fonts = (char**)userData;\n\tif(str.length == 1 && str.chars[0] == ' ') {\n\t\tcairo_text_extents_t te;\n\t\tcairo_text_extents(Clay__Cairo, \" \", &te);\n\t\treturn (Clay_Dimensions) {\n\t\t\t// The multiplication here follows no real logic, just\n\t\t\t// brute-forcing it until the text boundaries look\n\t\t\t// okay-ish.  You should probably rather use a proper text\n\t\t\t// shaping engine like HarfBuzz or Pango.\n\t\t\t.width = ((float) te.x_advance) * 1.9f,\n\t\t\t.height = (float) config->fontSize\n\t\t};\n\t}\n\n\t// Ensure string is null-terminated for Cairo\n    Clay_String toTerminate = (Clay_String){ .chars = str.chars, .length = str.length, .isStaticallyAllocated = false };\n\tchar *text = Clay_Cairo__NullTerminate(&toTerminate);\n\tchar *font_family = fonts[config->fontId];\n\n\t// Save and reset the Cairo context to avoid unwanted transformations\n\tcairo_save(Clay__Cairo);\n\tcairo_identity_matrix(Clay__Cairo);\n\n\t// Set font properties\n\tcairo_select_font_face(Clay__Cairo, font_family, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);\n\tcairo_set_font_size(Clay__Cairo, config->fontSize);\n\n\t// Use glyph extents for better precision\n\tcairo_scaled_font_t *scaled_font = cairo_get_scaled_font(Clay__Cairo);\n\tif (!scaled_font) {\n\t\tfprintf(stderr, \"Failed to get scaled font\\n\");\n\t\tcairo_restore(Clay__Cairo);\n\t\tfree(text);\n\t\treturn (Clay_Dimensions){0, 0};\n\t}\n\n\tcairo_glyph_t *glyphs = NULL;\n\tint num_glyphs = 0;\n\tcairo_status_t status = cairo_scaled_font_text_to_glyphs(\n\t\tscaled_font, 0, 0, text, -1, &glyphs, &num_glyphs, NULL, NULL, NULL\n\t);\n\n\tif (status != CAIRO_STATUS_SUCCESS || !glyphs || num_glyphs == 0) {\n\t\tfprintf(stderr, \"Failed to generate glyphs: %s\\n\", cairo_status_to_string(status));\n\t\tcairo_restore(Clay__Cairo);\n\t\tfree(text);\n\t\treturn (Clay_Dimensions){0, 0};\n\t}\n\n\t// Measure the glyph extents\n\tcairo_text_extents_t glyph_extents;\n\tcairo_glyph_extents(Clay__Cairo, glyphs, num_glyphs, &glyph_extents);\n\n\t// Clean up glyphs\n\tcairo_glyph_free(glyphs);\n\n\t// Restore the Cairo context\n\tcairo_restore(Clay__Cairo);\n\n\t// Free temporary strings\n\tfree(text);\n\n\t// Return dimensions\n\treturn (Clay_Dimensions){\n\t\t.width =  (float) glyph_extents.width,\n\t\t.height = (float) glyph_extents.height\n\t};\n}\n\n\nvoid Clay_Cairo_Initialize(cairo_t *cairo) {\n\tClay__Cairo = cairo;\n}\n\n// Internally used to copy images onto our document/active workspace.\nvoid Clay_Cairo__Blit_Surface(cairo_surface_t *src_surface, cairo_surface_t *dest_surface,\n\t\t\t\t\t\t\t  double x, double y, double scale_x, double scale_y) {\n\t// Create a cairo context for the destination surface\n\tcairo_t *cr = cairo_create(dest_surface);\n\n\t// Save the context's state\n\tcairo_save(cr);\n\n\t// Apply translation to position the source at (x, y)\n\tcairo_translate(cr, x, y);\n\n\t// Apply scaling to the context\n\tcairo_scale(cr, scale_x, scale_y);\n\n\t// Set the source surface at (0, 0) after applying transformations\n\tcairo_set_source_surface(cr, src_surface, 0, 0);\n\n\t// Paint the scaled source surface onto the destination surface\n\tcairo_paint(cr);\n\n\t// Restore the context's state to remove transformations\n\tcairo_restore(cr);\n\n\t// Clean up\n\tcairo_destroy(cr);\n}\n\nvoid Clay_Cairo_Render(Clay_RenderCommandArray commands, char** fonts) {\n\tcairo_t *cr = Clay__Cairo;\n\tfor(size_t i = 0; i < commands.length; i++) {\n\t\tClay_RenderCommand *command = Clay_RenderCommandArray_Get(&commands, i);\n\n\t\tswitch(command->commandType) {\n\t\tcase CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n            Clay_RectangleRenderData *config = &command->renderData.rectangle;\n\t\t\tClay_BoundingBox bb = command->boundingBox;\n\n\t\t\tcairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->backgroundColor));\n\n\t\t\tcairo_new_sub_path(cr);\n\t\t\tcairo_arc(cr, bb.x + config->cornerRadius.topLeft,\n\t\t\t\t\t  bb.y + config->cornerRadius.topLeft,\n\t\t\t\t\t  config->cornerRadius.topLeft,\n\t\t\t\t\t  M_PI, 3 * M_PI / 2); // 180° to 270°\n\t\t\tcairo_arc(cr, bb.x + bb.width - config->cornerRadius.topRight,\n\t\t\t\t\t  bb.y + config->cornerRadius.topRight,\n\t\t\t\t\t  config->cornerRadius.topRight,\n\t\t\t\t\t  3 * M_PI / 2, 2 * M_PI); // 270° to 360°\n\t\t\tcairo_arc(cr, bb.x + bb.width - config->cornerRadius.bottomRight,\n\t\t\t\t\t  bb.y + bb.height - config->cornerRadius.bottomRight,\n\t\t\t\t\t  config->cornerRadius.bottomRight,\n\t\t\t\t\t  0, M_PI / 2); // 0° to 90°\n\t\t\tcairo_arc(cr, bb.x + config->cornerRadius.bottomLeft,\n\t\t\t\t\t  bb.y + bb.height - config->cornerRadius.bottomLeft,\n\t\t\t\t\t  config->cornerRadius.bottomLeft,\n\t\t\t\t\t  M_PI / 2, M_PI); // 90° to 180°\n\t\t\tcairo_close_path(cr);\n\n\t\t\tcairo_fill(cr);\n\t\t\tbreak;\n\t\t}\n\t\tcase CLAY_RENDER_COMMAND_TYPE_TEXT: {\n\t\t\t// Cairo expects null terminated strings, we need to clone\n\t\t\t// to temporarily introduce one.\n            Clay_TextRenderData *config = &command->renderData.text;\n            Clay_String toTerminate = (Clay_String){ .chars = config->stringContents.chars, .length = config->stringContents.length, .isStaticallyAllocated = false };\n\t\t\tchar *text = Clay_Cairo__NullTerminate(&toTerminate);\n\t\t\tchar *font_family = fonts[config->fontId];\n\n\t\t\tClay_BoundingBox bb = command->boundingBox;\n\t\t\tClay_Color color = config->textColor;\n\n\t\t\tcairo_select_font_face(Clay__Cairo, font_family, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);\n\t\t\tcairo_set_font_size(cr, config->fontSize);\n\n\t\t\tcairo_move_to(cr, bb.x, bb.y + bb.height);\n\n\t\t\tcairo_set_source_rgba(cr, CLAY_TO_CAIRO(color));\n\t\t\tcairo_show_text(cr, text);\n\t\t\tcairo_close_path(cr);\n\n\t\t\tfree(text);\n\t\t\tbreak;\n\t\t}\n\t\tcase CLAY_RENDER_COMMAND_TYPE_BORDER: {\n            Clay_BorderRenderData *config = &command->renderData.border;\n\t\t\tClay_BoundingBox bb = command->boundingBox;\n\n\t\t\tdouble top_left_radius = config->cornerRadius.topLeft / 2.0;\n\t\t\tdouble top_right_radius = config->cornerRadius.topRight / 2.0;\n\t\t\tdouble bottom_right_radius = config->cornerRadius.bottomRight / 2.0;\n\t\t\tdouble bottom_left_radius = config->cornerRadius.bottomLeft / 2.0;\n\n\t\t\t// Draw the top border\n\t\t\tif (config->width.top > 0) {\n\t\t\t\tcairo_set_line_width(cr, config->width.top);\n\t\t\t\tcairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->color));\n\n\t\t\t\tcairo_new_sub_path(cr);\n\n\t\t\t\t// Left half-arc for top-left corner\n\t\t\t\tcairo_arc(cr, bb.x + top_left_radius, bb.y + top_left_radius, top_left_radius, DEG2RAD(225), DEG2RAD(270));\n\n\t\t\t\t// Line to right half-arc\n\t\t\t\tcairo_line_to(cr, bb.x + bb.width - top_right_radius, bb.y);\n\n\t\t\t\t// Right half-arc for top-right corner\n\t\t\t\tcairo_arc(cr, bb.x + bb.width - top_right_radius, bb.y + top_right_radius, top_right_radius, DEG2RAD(270), DEG2RAD(305));\n\n\t\t\t\tcairo_stroke(cr);\n\t\t\t}\n\n\t\t\t// Draw the right border\n\t\t\tif (config->width.right > 0) {\n\t\t\t\tcairo_set_line_width(cr, config->width.right);\n\t\t\t\tcairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->color));\n\n\t\t\t\tcairo_new_sub_path(cr);\n\n\t\t\t\t// Top half-arc for top-right corner\n\t\t\t\tcairo_arc(cr, bb.x + bb.width - top_right_radius, bb.y + top_right_radius, top_right_radius, DEG2RAD(305), DEG2RAD(350));\n\n\t\t\t\t// Line to bottom half-arc\n\t\t\t\tcairo_line_to(cr, bb.x + bb.width, bb.y + bb.height - bottom_right_radius);\n\n\t\t\t\t// Bottom half-arc for bottom-right corner\n\t\t\t\tcairo_arc(cr, bb.x + bb.width - bottom_right_radius, bb.y + bb.height - bottom_right_radius, bottom_right_radius, DEG2RAD(0), DEG2RAD(45));\n\n\t\t\t\tcairo_stroke(cr);\n\t\t\t}\n\n\t\t\t// Draw the bottom border\n\t\t\tif (config->width.bottom > 0) {\n\t\t\t\tcairo_set_line_width(cr, config->width.bottom);\n\t\t\t\tcairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->color));\n\n\t\t\t\tcairo_new_sub_path(cr);\n\n\t\t\t\t// Right half-arc for bottom-right corner\n\t\t\t\tcairo_arc(cr, bb.x + bb.width - bottom_right_radius, bb.y + bb.height - bottom_right_radius, bottom_right_radius, DEG2RAD(45), DEG2RAD(90));\n\n\t\t\t\t// Line to left half-arc\n\t\t\t\tcairo_line_to(cr, bb.x + bottom_left_radius, bb.y + bb.height);\n\n\t\t\t\t// Left half-arc for bottom-left corner\n\t\t\t\tcairo_arc(cr, bb.x + bottom_left_radius, bb.y + bb.height - bottom_left_radius, bottom_left_radius, DEG2RAD(90), DEG2RAD(135));\n\n\t\t\t\tcairo_stroke(cr);\n\t\t\t}\n\n\t\t\t// Draw the left border\n\t\t\tif (config->width.left > 0) {\n\t\t\t\tcairo_set_line_width(cr, config->width.left);\n\t\t\t\tcairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->color));\n\n\t\t\t\tcairo_new_sub_path(cr);\n\n\t\t\t\t// Bottom half-arc for bottom-left corner\n\t\t\t\tcairo_arc(cr, bb.x + bottom_left_radius, bb.y + bb.height - bottom_left_radius, bottom_left_radius, DEG2RAD(135), DEG2RAD(180));\n\n\t\t\t\t// Line to top half-arc\n\t\t\t\tcairo_line_to(cr, bb.x, bb.y + top_left_radius);\n\n\t\t\t\t// Top half-arc for top-left corner\n\t\t\t\tcairo_arc(cr, bb.x + top_left_radius, bb.y + top_left_radius, top_left_radius, DEG2RAD(180), DEG2RAD(225));\n\n\t\t\t\tcairo_stroke(cr);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase CLAY_RENDER_COMMAND_TYPE_IMAGE: {\n            Clay_ImageRenderData *config = &command->renderData.image;\n\t\t\tClay_BoundingBox bb = command->boundingBox;\n\n\t\t\tchar *path = config->imageData;\n\n\t\t\tcairo_surface_t *surf = cairo_image_surface_create_from_png(path),\n\t\t\t\t\t\t\t*origin = cairo_get_target(cr);\n\n\t\t\t// Calculate the original image dimensions\n\t\t\tdouble image_w = cairo_image_surface_get_width(surf),\n\t\t\t\timage_h = cairo_image_surface_get_height(surf);\n\n\t\t\t// Calculate the scaling factor to fit within the bounding box while preserving aspect ratio\n\t\t\tdouble scale_w = bb.width / image_w;\n\t\t\tdouble scale_h = bb.height / image_h;\n\t\t\tdouble scale = (scale_w < scale_h) ? scale_w : scale_h; // Use the smaller scaling factor\n\n\t\t\t// Apply the same scale to both dimensions to preserve aspect ratio\n\t\t\tdouble scale_x = scale;\n\t\t\tdouble scale_y = scale;\n\n\t\t\t// Calculate the scaled image dimensions\n\t\t\tdouble scaled_w = image_w * scale_x;\n\t\t\tdouble scaled_h = image_h * scale_y;\n\n\t\t\t// Adjust the x and y coordinates to center the scaled image within the bounding box\n\t\t\tdouble centered_x = bb.x + (bb.width - scaled_w) / 2.0;\n\t\t\tdouble centered_y = bb.y + (bb.height - scaled_h) / 2.0;\n\n\t\t\t// Blit the scaled and centered image\n\t\t\tClay_Cairo__Blit_Surface(surf, origin, centered_x, centered_y, scale_x, scale_y);\n\n\t\t\t// Clean up the source surface\n\t\t\tcairo_surface_destroy(surf);\n\t\t\tbreak;\n\t\t}\n\t\tcase CLAY_RENDER_COMMAND_TYPE_CUSTOM: {\n\t\t\t// Slot your custom elements in here.\n\t\t}\n\t\tdefault: {\n\t\t\tfprintf(stderr, \"Unknown command type %d\\n\", (int) command->commandType);\n\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "renderers/playdate/clay_renderer_playdate.c",
    "content": "#include \"pd_api.h\"\n#include \"../../clay.h\"\n\n// Playdate drawText function expects the number of codepoints to draw, not byte length\nstatic size_t Clay_Playdate_CountUtf8Codepoints(const char *str, size_t byteLen) {\n    size_t count = 0;\n    size_t i = 0;\n    while (i < byteLen) {\n        uint8_t c = (uint8_t)str[i];\n        if ((c & 0xC0) != 0x80) {\n            count++;\n        }\n        i++;\n    }\n    return count;\n}\n\n// As the playdate can only display black and white, we need to resolve Clay_color to either black or white\n// for both color and draw mode.\nstatic LCDColor clayColorToLCDColor(Clay_Color color) {\n    if (color.r > 0 || color.g > 0 || color.b > 0) {\n        return kColorWhite;\n    }\n    return kColorBlack;\n}\n\nstatic LCDBitmapDrawMode clayColorToDrawMode(Clay_Color color) {\n    if (color.r > 0 || color.g > 0 || color.b > 0) {\n        return kDrawModeFillWhite;\n    }\n    return kDrawModeCopy;\n}\n\nstatic float clampCornerRadius(float yAxisSize, float radius) {\n    if (radius < 1.0f) {\n        return 0.0f;\n    }\n    if (radius > yAxisSize / 2) {\n        return yAxisSize / 2;\n    }\n    // Trying to draw a 2x2 ellipse seems to result in just a dot, so if\n    // there is a corner radius at minimum it must be 2\n    return CLAY__MAX(2, radius);\n}\n\nstatic void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray renderCommands, LCDFont **fonts) {\n    for (uint32_t i = 0; i < renderCommands.length; i++) {\n        Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i);\n        Clay_BoundingBox boundingBox = renderCommand->boundingBox;\n\n        switch (renderCommand->commandType) {\n            case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;\n\n                float radiusTl = clampCornerRadius(boundingBox.height, config->cornerRadius.topLeft);\n                float radiusTr = clampCornerRadius(boundingBox.height, config->cornerRadius.topRight);\n                float radiusBl = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomLeft);\n                float radiusBr = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomRight);\n\n                pd->graphics->fillEllipse(\n                    boundingBox.x, boundingBox.y,\n                    radiusTl * 2, radiusTl * 2,\n                    -90.0f, 0.0f,\n                    clayColorToLCDColor(config->backgroundColor)\n                );\n\n                pd->graphics->fillEllipse(\n                    boundingBox.x + boundingBox.width - radiusTr * 2, boundingBox.y,\n                    radiusTr * 2, radiusTr * 2,\n                    0.0f, 90.0f,\n                    clayColorToLCDColor(config->backgroundColor)\n                );\n\n                pd->graphics->fillEllipse(\n                    boundingBox.x + boundingBox.width - radiusBr * 2,\n                    boundingBox.y + boundingBox.height - radiusBr * 2,\n                    radiusBr * 2, radiusBr * 2,\n                    90.0f, 180.0f,\n                    clayColorToLCDColor(config->backgroundColor)\n                );\n\n                pd->graphics->fillEllipse(\n                    boundingBox.x,\n                    boundingBox.y + boundingBox.height - radiusBl * 2,\n                    radiusBl * 2, radiusBl * 2,\n                    180.0f, 270.0f,\n                    clayColorToLCDColor(config->backgroundColor)\n                );\n\n                // Top chunk\n                pd->graphics->fillRect(\n                    boundingBox.x + radiusTl, boundingBox.y,\n                    boundingBox.width - radiusTl - radiusTr,\n                    CLAY__MAX(radiusTl, radiusTr),\n                    clayColorToLCDColor(config->backgroundColor)\n                );\n\n                // bottom chunk\n                int bottomChunkHeight = CLAY__MAX(radiusBl, radiusBr);\n                pd->graphics->fillRect(\n                    boundingBox.x + radiusBl, boundingBox.y + boundingBox.height - bottomChunkHeight,\n                    boundingBox.width - radiusBl - radiusBr,\n                    bottomChunkHeight,\n                    clayColorToLCDColor(config->backgroundColor)\n                );\n\n                // Middle chunk\n                int middleChunkHeight = boundingBox.height - CLAY__MIN(radiusBr, radiusBl) - CLAY__MIN(radiusTr, radiusTl);\n                pd->graphics->fillRect(\n                    boundingBox.x + CLAY__MIN(radiusTl, radiusBl), boundingBox.y + CLAY__MIN(radiusTr, radiusTl),\n                    boundingBox.width - radiusBl - radiusBr,\n                    middleChunkHeight,\n                    clayColorToLCDColor(config->backgroundColor)\n                );\n\n                // Left chunk\n                int leftChunkHeight = boundingBox.height - radiusTl - radiusBl;\n                int leftChunkWidth = CLAY__MAX(radiusTl, radiusBl);\n                pd->graphics->fillRect(\n                    boundingBox.x, boundingBox.y + radiusTl,\n                    leftChunkWidth,\n                    leftChunkHeight,\n                    clayColorToLCDColor(config->backgroundColor)\n                );\n\n                // Right chunk\n                int rightChunkHeight = boundingBox.height - radiusTr - radiusBr;\n                int rightChunkWidth = CLAY__MAX(radiusTr, radiusBr);\n                pd->graphics->fillRect(\n                    boundingBox.x + boundingBox.width - rightChunkWidth, boundingBox.y + radiusTr,\n                    rightChunkWidth,\n                    rightChunkHeight,\n                    clayColorToLCDColor(config->backgroundColor)\n                );\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_TEXT: {\n                Clay_TextRenderData *config = &renderCommand->renderData.text;\n                LCDFont *font = fonts[config->fontId];\n                pd->graphics->setFont(font);\n                pd->graphics->setDrawMode(clayColorToDrawMode(config->textColor));\n                pd->graphics->drawText(\n                    renderCommand->renderData.text.stringContents.chars,\n                    Clay_Playdate_CountUtf8Codepoints(\n                        renderCommand->renderData.text.stringContents.chars,\n                        renderCommand->renderData.text.stringContents.length\n                    ),\n                    kUTF8Encoding,\n                    boundingBox.x,\n                    boundingBox.y\n                );\n                pd->graphics->setDrawMode(kDrawModeCopy);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {\n                pd->graphics->setClipRect(\n                    boundingBox.x,boundingBox.y,\n                    boundingBox.width, boundingBox.height\n                );\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {\n                pd->graphics->clearClipRect();\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_IMAGE: {\n                Clay_ImageRenderData *config = &renderCommand->renderData.image;\n                LCDBitmap *texture = config->imageData;\n                int texWidth;\n                int texHeight;\n                pd->graphics->getBitmapData(texture, &texWidth, &texHeight, NULL, NULL, NULL);\n                if (texWidth != boundingBox.width || texHeight != boundingBox.height) {\n                    pd->graphics->drawScaledBitmap(\n                        texture,\n                        boundingBox.x, boundingBox.y,\n                        boundingBox.width / texWidth,\n                        boundingBox.height / texHeight\n                    );\n                } else {\n                    pd->graphics->drawBitmap(texture, boundingBox.x, boundingBox.y, kBitmapUnflipped);\n                }\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_BORDER: {\n                Clay_BorderRenderData *config = &renderCommand->renderData.border;\n\n                float radiusTl = clampCornerRadius(boundingBox.height, config->cornerRadius.topLeft);\n                float radiusTr = clampCornerRadius(boundingBox.height, config->cornerRadius.topRight);\n                float radiusBl = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomLeft);\n                float radiusBr = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomRight);\n\n                if (config->width.top > 0) {\n                    pd->graphics->drawEllipse(\n                        boundingBox.x, boundingBox.y,\n                        radiusTl * 2, radiusTl * 2,\n                        config->width.top,\n                        -90.0f, 0.0f,\n                        clayColorToLCDColor(config->color)\n                    );\n\n                    pd->graphics->drawLine(\n                        boundingBox.x + radiusTl, boundingBox.y,\n                        boundingBox.x + boundingBox.width - radiusTr - config->width.right, boundingBox.y,\n                        config->width.top,\n                        clayColorToLCDColor(config->color)\n                    );\n\n                    pd->graphics->drawEllipse(\n                        boundingBox.x + boundingBox.width - radiusTr * 2, boundingBox.y,\n                        radiusTr * 2, radiusTr * 2,\n                        config->width.top,\n                        0.0f, 90.0f,\n                        clayColorToLCDColor(config->color)\n                    );\n                }\n\n                if (config->width.right > 0 && radiusTr + radiusBr <= boundingBox.height) {\n                    pd->graphics->drawLine(\n                        boundingBox.x + boundingBox.width - config->width.right,\n                        boundingBox.y + radiusTr,\n                        boundingBox.x + boundingBox.width - config->width.right,\n                        boundingBox.y + boundingBox.height - radiusBr - config->width.bottom,\n                        config->width.right,\n                        clayColorToLCDColor(config->color)\n                    );\n                }\n\n                if (config->width.bottom > 0) {\n                    pd->graphics->drawEllipse(\n                        boundingBox.x + boundingBox.width - radiusBr * 2,\n                        boundingBox.y + boundingBox.height - radiusBr * 2,\n                        radiusBr * 2, radiusBr * 2,\n                        config->width.bottom,\n                        90.0f, 180.0f,\n                        clayColorToLCDColor(config->color)\n                    );\n\n                    pd->graphics->drawLine(\n                        boundingBox.x + boundingBox.width - radiusBr - config->width.right,\n                        boundingBox.y + boundingBox.height - config->width.bottom,\n                        boundingBox.x + radiusBl,\n                        boundingBox.y + boundingBox.height - config->width.bottom,\n                        config->width.bottom,\n                        clayColorToLCDColor(config->color)\n                    );\n\n                    pd->graphics->drawEllipse(\n                        boundingBox.x,\n                        boundingBox.y + boundingBox.height - radiusBl * 2,\n                        radiusBl * 2, radiusBl * 2,\n                        config->width.bottom,\n                        180.0f, 270.0f,\n                        clayColorToLCDColor(config->color)\n                    );\n                }\n\n                if (config->width.left > 0 && radiusBl + radiusTl < boundingBox.height) {\n                    pd->graphics->drawLine(\n                        boundingBox.x, boundingBox.y + boundingBox.height - radiusBl - config->width.bottom,\n                        boundingBox.x, boundingBox.y + radiusTl,\n                        config->width.left,\n                        clayColorToLCDColor(config->color)\n                    );\n                }\n                break;\n            }\n            default: {\n                pd->system->logToConsole(\"Error: unhandled render command: %d\\n\", renderCommand->commandType);\n                return;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "renderers/raylib/clay_renderer_raylib.c",
    "content": "#include \"raylib.h\"\n#include \"raymath.h\"\n#include \"stdint.h\"\n#include \"string.h\"\n#include \"stdio.h\"\n#include \"stdlib.h\"\n\n#define CLAY_RECTANGLE_TO_RAYLIB_RECTANGLE(rectangle) (Rectangle) { .x = rectangle.x, .y = rectangle.y, .width = rectangle.width, .height = rectangle.height }\n#define CLAY_COLOR_TO_RAYLIB_COLOR(color) (Color) { .r = (unsigned char)roundf(color.r), .g = (unsigned char)roundf(color.g), .b = (unsigned char)roundf(color.b), .a = (unsigned char)roundf(color.a) }\n\nCamera Raylib_camera;\n\ntypedef enum\n{\n    CUSTOM_LAYOUT_ELEMENT_TYPE_3D_MODEL\n} CustomLayoutElementType;\n\ntypedef struct\n{\n    Model model;\n    float scale;\n    Vector3 position;\n    Matrix rotation;\n} CustomLayoutElement_3DModel;\n\ntypedef struct\n{\n    CustomLayoutElementType type;\n    union {\n        CustomLayoutElement_3DModel model;\n    } customData;\n} CustomLayoutElement;\n\n// Get a ray trace from the screen position (i.e mouse) within a specific section of the screen\nRay GetScreenToWorldPointWithZDistance(Vector2 position, Camera camera, int screenWidth, int screenHeight, float zDistance)\n{\n    Ray ray = { 0 };\n\n    // Calculate normalized device coordinates\n    // NOTE: y value is negative\n    float x = (2.0f*position.x)/(float)screenWidth - 1.0f;\n    float y = 1.0f - (2.0f*position.y)/(float)screenHeight;\n    float z = 1.0f;\n\n    // Store values in a vector\n    Vector3 deviceCoords = { x, y, z };\n\n    // Calculate view matrix from camera look at\n    Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);\n\n    Matrix matProj = MatrixIdentity();\n\n    if (camera.projection == CAMERA_PERSPECTIVE)\n    {\n        // Calculate projection matrix from perspective\n        matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)screenWidth/(double)screenHeight), 0.01f, zDistance);\n    }\n    else if (camera.projection == CAMERA_ORTHOGRAPHIC)\n    {\n        double aspect = (double)screenWidth/(double)screenHeight;\n        double top = camera.fovy/2.0;\n        double right = top*aspect;\n\n        // Calculate projection matrix from orthographic\n        matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);\n    }\n\n    // Unproject far/near points\n    Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);\n    Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);\n\n    // Calculate normalized direction vector\n    Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));\n\n    ray.position = farPoint;\n\n    // Apply calculated vectors to ray\n    ray.direction = direction;\n\n    return ray;\n}\n\n\nstatic inline Clay_Dimensions Raylib_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {\n    // Measure string size for Font\n    Clay_Dimensions textSize = { 0 };\n\n    float maxTextWidth = 0.0f;\n    float lineTextWidth = 0;\n    int maxLineCharCount = 0;\n    int lineCharCount = 0;\n\n    float textHeight = config->fontSize;\n    Font* fonts = (Font*)userData;\n    Font fontToUse = fonts[config->fontId];\n    // Font failed to load, likely the fonts are in the wrong place relative to the execution dir.\n    // RayLib ships with a default font, so we can continue with that built in one. \n    if (!fontToUse.glyphs) {\n        fontToUse = GetFontDefault();\n    }\n\n    float scaleFactor = config->fontSize/(float)fontToUse.baseSize;\n\n    for (int i = 0; i < text.length; ++i, lineCharCount++)\n    {\n        if (text.chars[i] == '\\n') {\n            maxTextWidth = fmax(maxTextWidth, lineTextWidth);\n            maxLineCharCount = CLAY__MAX(maxLineCharCount, lineCharCount);\n            lineTextWidth = 0;\n            lineCharCount = 0;\n            continue;\n        }\n        int index = text.chars[i] - 32;\n        if (fontToUse.glyphs[index].advanceX != 0) lineTextWidth += fontToUse.glyphs[index].advanceX;\n        else lineTextWidth += (fontToUse.recs[index].width + fontToUse.glyphs[index].offsetX);\n    }\n\n    maxTextWidth = fmax(maxTextWidth, lineTextWidth);\n    maxLineCharCount = CLAY__MAX(maxLineCharCount, lineCharCount);\n\n    textSize.width = maxTextWidth * scaleFactor + (lineCharCount * config->letterSpacing);\n    textSize.height = textHeight;\n\n    return textSize;\n}\n\nvoid Clay_Raylib_Initialize(int width, int height, const char *title, unsigned int flags) {\n    SetConfigFlags(flags);\n    InitWindow(width, height, title);\n//    EnableEventWaiting();\n}\n\n// A MALLOC'd buffer, that we keep modifying inorder to save from so many Malloc and Free Calls.\n// Call Clay_Raylib_Close() to free\nstatic char *temp_render_buffer = NULL;\nstatic int temp_render_buffer_len = 0;\n\n// Call after closing the window to clean up the render buffer\nvoid Clay_Raylib_Close()\n{\n    if(temp_render_buffer) free(temp_render_buffer);\n    temp_render_buffer_len = 0;\n\n    CloseWindow();\n}\n\n\nvoid Clay_Raylib_Render(Clay_RenderCommandArray renderCommands, Font* fonts)\n{\n    for (int j = 0; j < renderCommands.length; j++)\n    {\n        Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, j);\n        Clay_BoundingBox boundingBox = {roundf(renderCommand->boundingBox.x), roundf(renderCommand->boundingBox.y), roundf(renderCommand->boundingBox.width), roundf(renderCommand->boundingBox.height)};\n        switch (renderCommand->commandType)\n        {\n            case CLAY_RENDER_COMMAND_TYPE_TEXT: {\n                Clay_TextRenderData *textData = &renderCommand->renderData.text;\n                Font fontToUse = fonts[textData->fontId];\n    \n                int strlen = textData->stringContents.length + 1;\n    \n                if(strlen > temp_render_buffer_len) {\n                    // Grow the temp buffer if we need a larger string\n                    if(temp_render_buffer) free(temp_render_buffer);\n                    temp_render_buffer = (char *) malloc(strlen);\n                    temp_render_buffer_len = strlen;\n                }\n    \n                // Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator\n                memcpy(temp_render_buffer, textData->stringContents.chars, textData->stringContents.length);\n                temp_render_buffer[textData->stringContents.length] = '\\0';\n                DrawTextEx(fontToUse, temp_render_buffer, (Vector2){boundingBox.x, boundingBox.y}, (float)textData->fontSize, (float)textData->letterSpacing, CLAY_COLOR_TO_RAYLIB_COLOR(textData->textColor));\n    \n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_IMAGE: {\n                Texture2D imageTexture = *(Texture2D *)renderCommand->renderData.image.imageData;\n                Clay_Color tintColor = renderCommand->renderData.image.backgroundColor;\n                if (tintColor.r == 0 && tintColor.g == 0 && tintColor.b == 0 && tintColor.a == 0) {\n                    tintColor = (Clay_Color) { 255, 255, 255, 255 };\n                }\n                DrawTexturePro(\n                    imageTexture,\n                    (Rectangle) { 0, 0, imageTexture.width, imageTexture.height },\n                    (Rectangle){boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height},\n                    (Vector2) {},\n                    0,\n                    CLAY_COLOR_TO_RAYLIB_COLOR(tintColor));\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {\n                BeginScissorMode((int)roundf(boundingBox.x), (int)roundf(boundingBox.y), (int)roundf(boundingBox.width), (int)roundf(boundingBox.height));\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {\n                EndScissorMode();\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;\n                if (config->cornerRadius.topLeft > 0) {\n                    float radius = (config->cornerRadius.topLeft * 2) / (float)((boundingBox.width > boundingBox.height) ? boundingBox.height : boundingBox.width);\n                    DrawRectangleRounded((Rectangle) { boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height }, radius, 8, CLAY_COLOR_TO_RAYLIB_COLOR(config->backgroundColor));\n                } else {\n                    DrawRectangle(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, CLAY_COLOR_TO_RAYLIB_COLOR(config->backgroundColor));\n                }\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_BORDER: {\n                Clay_BorderRenderData *config = &renderCommand->renderData.border;\n                // Left border\n                if (config->width.left > 0) {\n                    DrawRectangle((int)roundf(boundingBox.x), (int)roundf(boundingBox.y + config->cornerRadius.topLeft), (int)config->width.left, (int)roundf(boundingBox.height - config->cornerRadius.topLeft - config->cornerRadius.bottomLeft), CLAY_COLOR_TO_RAYLIB_COLOR(config->color));\n                }\n                // Right border\n                if (config->width.right > 0) {\n                    DrawRectangle((int)roundf(boundingBox.x + boundingBox.width - config->width.right), (int)roundf(boundingBox.y + config->cornerRadius.topRight), (int)config->width.right, (int)roundf(boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight), CLAY_COLOR_TO_RAYLIB_COLOR(config->color));\n                }\n                // Top border\n                if (config->width.top > 0) {\n                    DrawRectangle((int)roundf(boundingBox.x + config->cornerRadius.topLeft), (int)roundf(boundingBox.y), (int)roundf(boundingBox.width - config->cornerRadius.topLeft - config->cornerRadius.topRight), (int)config->width.top, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));\n                }\n                // Bottom border\n                if (config->width.bottom > 0) {\n                    DrawRectangle((int)roundf(boundingBox.x + config->cornerRadius.bottomLeft), (int)roundf(boundingBox.y + boundingBox.height - config->width.bottom), (int)roundf(boundingBox.width - config->cornerRadius.bottomLeft - config->cornerRadius.bottomRight), (int)config->width.bottom, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));\n                }\n                if (config->cornerRadius.topLeft > 0) {\n                    DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.topLeft), roundf(boundingBox.y + config->cornerRadius.topLeft) }, roundf(config->cornerRadius.topLeft - config->width.top), config->cornerRadius.topLeft, 180, 270, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));\n                }\n                if (config->cornerRadius.topRight > 0) {\n                    DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.topRight), roundf(boundingBox.y + config->cornerRadius.topRight) }, roundf(config->cornerRadius.topRight - config->width.top), config->cornerRadius.topRight, 270, 360, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));\n                }\n                if (config->cornerRadius.bottomLeft > 0) {\n                    DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.bottomLeft), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomLeft) }, roundf(config->cornerRadius.bottomLeft - config->width.bottom), config->cornerRadius.bottomLeft, 90, 180, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));\n                }\n                if (config->cornerRadius.bottomRight > 0) {\n                    DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.bottomRight), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomRight) }, roundf(config->cornerRadius.bottomRight - config->width.bottom), config->cornerRadius.bottomRight, 0.1, 90, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));\n                }\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {\n                Clay_CustomRenderData *config = &renderCommand->renderData.custom;\n                CustomLayoutElement *customElement = (CustomLayoutElement *)config->customData;\n                if (!customElement) continue;\n                switch (customElement->type) {\n                    case CUSTOM_LAYOUT_ELEMENT_TYPE_3D_MODEL: {\n                        Clay_BoundingBox rootBox = renderCommands.internalArray[0].boundingBox;\n                        float scaleValue = CLAY__MIN(CLAY__MIN(1, 768 / rootBox.height) * CLAY__MAX(1, rootBox.width / 1024), 1.5f);\n                        Ray positionRay = GetScreenToWorldPointWithZDistance((Vector2) { renderCommand->boundingBox.x + renderCommand->boundingBox.width / 2, renderCommand->boundingBox.y + (renderCommand->boundingBox.height / 2) + 20 }, Raylib_camera, (int)roundf(rootBox.width), (int)roundf(rootBox.height), 140);\n                        BeginMode3D(Raylib_camera);\n                            DrawModel(customElement->customData.model.model, positionRay.position, customElement->customData.model.scale * scaleValue, WHITE);        // Draw 3d model with texture\n                        EndMode3D();\n                        break;\n                    }\n                    default: break;\n                }\n                break;\n            }\n            default: {\n                printf(\"Error: unhandled render command.\");\n                exit(1);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "renderers/raylib/raylib.h",
    "content": "/**********************************************************************************************\n*\n*   raylib v5.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)\n*\n*   FEATURES:\n*       - NO external dependencies, all required libraries included with raylib\n*       - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly,\n*                        MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5.\n*       - Written in plain C code (C99) in PascalCase/camelCase notation\n*       - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3, ES2, ES3 - choose at compile)\n*       - Unique OpenGL abstraction layer (usable as standalone module): [rlgl]\n*       - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts)\n*       - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC)\n*       - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more!\n*       - Flexible Materials system, supporting classic maps and PBR maps\n*       - Animated 3D models supported (skeletal bones animation) (IQM, M3D, GLTF)\n*       - Shaders support, including Model shaders and Postprocessing shaders\n*       - Powerful math module for Vector, Matrix and Quaternion operations: [raymath]\n*       - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, QOA, XM, MOD)\n*       - VR stereo rendering with configurable HMD device parameters\n*       - Bindings to multiple programming languages available!\n*\n*   NOTES:\n*       - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text]\n*       - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2)\n*       - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2)\n*       - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2)\n*\n*   DEPENDENCIES (included):\n*       [rcore][GLFW] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input\n*       [rcore][RGFW] rgfw (ColleagueRiley - github.com/ColleagueRiley/RGFW) for window/context management and input\n*       [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading\n*       [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management\n*\n*   OPTIONAL DEPENDENCIES (included):\n*       [rcore] msf_gif (Miles Fogle) for GIF recording\n*       [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm\n*       [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm\n*       [rcore] rprand (Ramon Snatamaria) for pseudo-random numbers generation\n*       [rtextures] qoi (Dominic Szablewski - https://phoboslab.org) for QOI image manage\n*       [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...)\n*       [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)\n*       [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms\n*       [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation\n*       [rtext] stb_truetype (Sean Barret) for ttf fonts loading\n*       [rtext] stb_rect_pack (Sean Barret) for rectangles packing\n*       [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation\n*       [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL)\n*       [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF)\n*       [rmodels] m3d (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d)\n*       [rmodels] vox_loader (Johann Nadalutti) for models loading (VOX)\n*       [raudio] dr_wav (David Reid) for WAV audio file loading\n*       [raudio] dr_flac (David Reid) for FLAC audio file loading\n*       [raudio] dr_mp3 (David Reid) for MP3 audio file loading\n*       [raudio] stb_vorbis (Sean Barret) for OGG audio loading\n*       [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading\n*       [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading\n*       [raudio] qoa (Dominic Szablewski - https://phoboslab.org) for QOA audio manage\n*\n*\n*   LICENSE: zlib/libpng\n*\n*   raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified,\n*   BSD-like license that allows static linking with closed source software:\n*\n*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)\n*\n*   This software is provided \"as-is\", without any express or implied warranty. In no event\n*   will the authors be held liable for any damages arising from the use of this software.\n*\n*   Permission is granted to anyone to use this software for any purpose, including commercial\n*   applications, and to alter it and redistribute it freely, subject to the following restrictions:\n*\n*     1. The origin of this software must not be misrepresented; you must not claim that you\n*     wrote the original software. If you use this software in a product, an acknowledgment\n*     in the product documentation would be appreciated but is not required.\n*\n*     2. Altered source versions must be plainly marked as such, and must not be misrepresented\n*     as being the original software.\n*\n*     3. This notice may not be removed or altered from any source distribution.\n*\n**********************************************************************************************/\n\n#ifndef RAYLIB_H\n#define RAYLIB_H\n\n#include <stdarg.h>     // Required for: va_list - Only used by TraceLogCallback\n\n#define RAYLIB_VERSION_MAJOR 5\n#define RAYLIB_VERSION_MINOR 5\n#define RAYLIB_VERSION_PATCH 0\n#define RAYLIB_VERSION  \"5.5\"\n\n// Function specifiers in case library is build/used as a shared library\n// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll\n// NOTE: visibility(\"default\") attribute makes symbols \"visible\" when compiled with -fvisibility=hidden\n#if defined(_WIN32)\n    #if defined(__TINYC__)\n        #define __declspec(x) __attribute__((x))\n    #endif\n    #if defined(BUILD_LIBTYPE_SHARED)\n        #define RLAPI __declspec(dllexport)     // We are building the library as a Win32 shared library (.dll)\n    #elif defined(USE_LIBTYPE_SHARED)\n        #define RLAPI __declspec(dllimport)     // We are using the library as a Win32 shared library (.dll)\n    #endif\n#else\n    #if defined(BUILD_LIBTYPE_SHARED)\n        #define RLAPI __attribute__((visibility(\"default\"))) // We are building as a Unix shared library (.so/.dylib)\n    #endif\n#endif\n\n#ifndef RLAPI\n    #define RLAPI       // Functions defined as 'extern' by default (implicit specifiers)\n#endif\n\n//----------------------------------------------------------------------------------\n// Some basic Defines\n//----------------------------------------------------------------------------------\n#ifndef PI\n    #define PI 3.14159265358979323846f\n#endif\n#ifndef DEG2RAD\n    #define DEG2RAD (PI/180.0f)\n#endif\n#ifndef RAD2DEG\n    #define RAD2DEG (180.0f/PI)\n#endif\n\n// Allow custom memory allocators\n// NOTE: Require recompiling raylib sources\n#ifndef RL_MALLOC\n    #define RL_MALLOC(sz)       malloc(sz)\n#endif\n#ifndef RL_CALLOC\n    #define RL_CALLOC(n,sz)     calloc(n,sz)\n#endif\n#ifndef RL_REALLOC\n    #define RL_REALLOC(ptr,sz)  realloc(ptr,sz)\n#endif\n#ifndef RL_FREE\n    #define RL_FREE(ptr)        free(ptr)\n#endif\n\n// NOTE: MSVC C++ compiler does not support compound literals (C99 feature)\n// Plain structures in C++ (without constructors) can be initialized with { }\n// This is called aggregate initialization (C++11 feature)\n#if defined(__cplusplus)\n    #define CLITERAL(type)      type\n#else\n    #define CLITERAL(type)      (type)\n#endif\n\n// Some compilers (mostly macos clang) default to C++98,\n// where aggregate initialization can't be used\n// So, give a more clear error stating how to fix this\n#if !defined(_MSC_VER) && (defined(__cplusplus) && __cplusplus < 201103L)\n    #error \"C++11 or later is required. Add -std=c++11\"\n#endif\n\n// NOTE: We set some defines with some data types declared by raylib\n// Other modules (raymath, rlgl) also require some of those types, so,\n// to be able to use those other modules as standalone (not depending on raylib)\n// this defines are very useful for internal check and avoid type (re)definitions\n#define RL_COLOR_TYPE\n#define RL_RECTANGLE_TYPE\n#define RL_VECTOR2_TYPE\n#define RL_VECTOR3_TYPE\n#define RL_VECTOR4_TYPE\n#define RL_QUATERNION_TYPE\n#define RL_MATRIX_TYPE\n\n// Some Basic Colors\n// NOTE: Custom raylib color palette for amazing visuals on WHITE background\n#define LIGHTGRAY  CLITERAL(Color){ 200, 200, 200, 255 }   // Light Gray\n#define GRAY       CLITERAL(Color){ 130, 130, 130, 255 }   // Gray\n#define DARKGRAY   CLITERAL(Color){ 80, 80, 80, 255 }      // Dark Gray\n#define YELLOW     CLITERAL(Color){ 253, 249, 0, 255 }     // Yellow\n#define GOLD       CLITERAL(Color){ 255, 203, 0, 255 }     // Gold\n#define ORANGE     CLITERAL(Color){ 255, 161, 0, 255 }     // Orange\n#define PINK       CLITERAL(Color){ 255, 109, 194, 255 }   // Pink\n#define RED        CLITERAL(Color){ 230, 41, 55, 255 }     // Red\n#define MAROON     CLITERAL(Color){ 190, 33, 55, 255 }     // Maroon\n#define GREEN      CLITERAL(Color){ 0, 228, 48, 255 }      // Green\n#define LIME       CLITERAL(Color){ 0, 158, 47, 255 }      // Lime\n#define DARKGREEN  CLITERAL(Color){ 0, 117, 44, 255 }      // Dark Green\n#define SKYBLUE    CLITERAL(Color){ 102, 191, 255, 255 }   // Sky Blue\n#define BLUE       CLITERAL(Color){ 0, 121, 241, 255 }     // Blue\n#define DARKBLUE   CLITERAL(Color){ 0, 82, 172, 255 }      // Dark Blue\n#define PURPLE     CLITERAL(Color){ 200, 122, 255, 255 }   // Purple\n#define VIOLET     CLITERAL(Color){ 135, 60, 190, 255 }    // Violet\n#define DARKPURPLE CLITERAL(Color){ 112, 31, 126, 255 }    // Dark Purple\n#define BEIGE      CLITERAL(Color){ 211, 176, 131, 255 }   // Beige\n#define BROWN      CLITERAL(Color){ 127, 106, 79, 255 }    // Brown\n#define DARKBROWN  CLITERAL(Color){ 76, 63, 47, 255 }      // Dark Brown\n\n#define WHITE      CLITERAL(Color){ 255, 255, 255, 255 }   // White\n#define BLACK      CLITERAL(Color){ 0, 0, 0, 255 }         // Black\n#define BLANK      CLITERAL(Color){ 0, 0, 0, 0 }           // Blank (Transparent)\n#define MAGENTA    CLITERAL(Color){ 255, 0, 255, 255 }     // Magenta\n#define RAYWHITE   CLITERAL(Color){ 245, 245, 245, 255 }   // My own White (raylib logo)\n\n//----------------------------------------------------------------------------------\n// Structures Definition\n//----------------------------------------------------------------------------------\n// Boolean type\n#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800)\n    #include <stdbool.h>\n#elif !defined(__cplusplus) && !defined(bool)\n    typedef enum bool { false = 0, true = !false } bool;\n    #define RL_BOOL_TYPE\n#endif\n\n// Vector2, 2 components\ntypedef struct Vector2 {\n    float x;                // Vector x component\n    float y;                // Vector y component\n} Vector2;\n\n// Vector3, 3 components\ntypedef struct Vector3 {\n    float x;                // Vector x component\n    float y;                // Vector y component\n    float z;                // Vector z component\n} Vector3;\n\n// Vector4, 4 components\ntypedef struct Vector4 {\n    float x;                // Vector x component\n    float y;                // Vector y component\n    float z;                // Vector z component\n    float w;                // Vector w component\n} Vector4;\n\n// Quaternion, 4 components (Vector4 alias)\ntypedef Vector4 Quaternion;\n\n// Matrix, 4x4 components, column major, OpenGL style, right-handed\ntypedef struct Matrix {\n    float m0, m4, m8, m12;  // Matrix first row (4 components)\n    float m1, m5, m9, m13;  // Matrix second row (4 components)\n    float m2, m6, m10, m14; // Matrix third row (4 components)\n    float m3, m7, m11, m15; // Matrix fourth row (4 components)\n} Matrix;\n\n// Color, 4 components, R8G8B8A8 (32bit)\ntypedef struct Color {\n    unsigned char r;        // Color red value\n    unsigned char g;        // Color green value\n    unsigned char b;        // Color blue value\n    unsigned char a;        // Color alpha value\n} Color;\n\n// Rectangle, 4 components\ntypedef struct Rectangle {\n    float x;                // Rectangle top-left corner position x\n    float y;                // Rectangle top-left corner position y\n    float width;            // Rectangle width\n    float height;           // Rectangle height\n} Rectangle;\n\n// Image, pixel data stored in CPU memory (RAM)\ntypedef struct Image {\n    void *data;             // Image raw data\n    int width;              // Image base width\n    int height;             // Image base height\n    int mipmaps;            // Mipmap levels, 1 by default\n    int format;             // Data format (PixelFormat type)\n} Image;\n\n// Texture, tex data stored in GPU memory (VRAM)\ntypedef struct Texture {\n    unsigned int id;        // OpenGL texture id\n    int width;              // Texture base width\n    int height;             // Texture base height\n    int mipmaps;            // Mipmap levels, 1 by default\n    int format;             // Data format (PixelFormat type)\n} Texture;\n\n// Texture2D, same as Texture\ntypedef Texture Texture2D;\n\n// TextureCubemap, same as Texture\ntypedef Texture TextureCubemap;\n\n// RenderTexture, fbo for texture rendering\ntypedef struct RenderTexture {\n    unsigned int id;        // OpenGL framebuffer object id\n    Texture texture;        // Color buffer attachment texture\n    Texture depth;          // Depth buffer attachment texture\n} RenderTexture;\n\n// RenderTexture2D, same as RenderTexture\ntypedef RenderTexture RenderTexture2D;\n\n// NPatchInfo, n-patch layout info\ntypedef struct NPatchInfo {\n    Rectangle source;       // Texture source rectangle\n    int left;               // Left border offset\n    int top;                // Top border offset\n    int right;              // Right border offset\n    int bottom;             // Bottom border offset\n    int layout;             // Layout of the n-patch: 3x3, 1x3 or 3x1\n} NPatchInfo;\n\n// GlyphInfo, font characters glyphs info\ntypedef struct GlyphInfo {\n    int value;              // Character value (Unicode)\n    int offsetX;            // Character offset X when drawing\n    int offsetY;            // Character offset Y when drawing\n    int advanceX;           // Character advance position X\n    Image image;            // Character image data\n} GlyphInfo;\n\n// Font, font texture and GlyphInfo array data\ntypedef struct Font {\n    int baseSize;           // Base size (default chars height)\n    int glyphCount;         // Number of glyph characters\n    int glyphPadding;       // Padding around the glyph characters\n    Texture2D texture;      // Texture atlas containing the glyphs\n    Rectangle *recs;        // Rectangles in texture for the glyphs\n    GlyphInfo *glyphs;      // Glyphs info data\n} Font;\n\n// Camera, defines position/orientation in 3d space\ntypedef struct Camera3D {\n    Vector3 position;       // Camera position\n    Vector3 target;         // Camera target it looks-at\n    Vector3 up;             // Camera up vector (rotation over its axis)\n    float fovy;             // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic\n    int projection;         // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC\n} Camera3D;\n\ntypedef Camera3D Camera;    // Camera type fallback, defaults to Camera3D\n\n// Camera2D, defines position/orientation in 2d space\ntypedef struct Camera2D {\n    Vector2 offset;         // Camera offset (displacement from target)\n    Vector2 target;         // Camera target (rotation and zoom origin)\n    float rotation;         // Camera rotation in degrees\n    float zoom;             // Camera zoom (scaling), should be 1.0f by default\n} Camera2D;\n\n// Mesh, vertex data and vao/vbo\ntypedef struct Mesh {\n    int vertexCount;        // Number of vertices stored in arrays\n    int triangleCount;      // Number of triangles stored (indexed or not)\n\n    // Vertex attributes data\n    float *vertices;        // Vertex position (XYZ - 3 components per vertex) (shader-location = 0)\n    float *texcoords;       // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)\n    float *texcoords2;      // Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5)\n    float *normals;         // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2)\n    float *tangents;        // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)\n    unsigned char *colors;      // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)\n    unsigned short *indices;    // Vertex indices (in case vertex data comes indexed)\n\n    // Animation vertex data\n    float *animVertices;    // Animated vertex positions (after bones transformations)\n    float *animNormals;     // Animated normals (after bones transformations)\n    unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)\n    float *boneWeights;     // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)\n    Matrix *boneMatrices;   // Bones animated transformation matrices\n    int boneCount;          // Number of bones\n\n    // OpenGL identifiers\n    unsigned int vaoId;     // OpenGL Vertex Array Object id\n    unsigned int *vboId;    // OpenGL Vertex Buffer Objects id (default vertex data)\n} Mesh;\n\n// Shader\ntypedef struct Shader {\n    unsigned int id;        // Shader program id\n    int *locs;              // Shader locations array (RL_MAX_SHADER_LOCATIONS)\n} Shader;\n\n// MaterialMap\ntypedef struct MaterialMap {\n    Texture2D texture;      // Material map texture\n    Color color;            // Material map color\n    float value;            // Material map value\n} MaterialMap;\n\n// Material, includes shader and maps\ntypedef struct Material {\n    Shader shader;          // Material shader\n    MaterialMap *maps;      // Material maps array (MAX_MATERIAL_MAPS)\n    float params[4];        // Material generic parameters (if required)\n} Material;\n\n// Transform, vertex transformation data\ntypedef struct Transform {\n    Vector3 translation;    // Translation\n    Quaternion rotation;    // Rotation\n    Vector3 scale;          // Scale\n} Transform;\n\n// Bone, skeletal animation bone\ntypedef struct BoneInfo {\n    char name[32];          // Bone name\n    int parent;             // Bone parent\n} BoneInfo;\n\n// Model, meshes, materials and animation data\ntypedef struct Model {\n    Matrix transform;       // Local transform matrix\n\n    int meshCount;          // Number of meshes\n    int materialCount;      // Number of materials\n    Mesh *meshes;           // Meshes array\n    Material *materials;    // Materials array\n    int *meshMaterial;      // Mesh material number\n\n    // Animation data\n    int boneCount;          // Number of bones\n    BoneInfo *bones;        // Bones information (skeleton)\n    Transform *bindPose;    // Bones base transformation (pose)\n} Model;\n\n// ModelAnimation\ntypedef struct ModelAnimation {\n    int boneCount;          // Number of bones\n    int frameCount;         // Number of animation frames\n    BoneInfo *bones;        // Bones information (skeleton)\n    Transform **framePoses; // Poses array by frame\n    char name[32];          // Animation name\n} ModelAnimation;\n\n// Ray, ray for raycasting\ntypedef struct Ray {\n    Vector3 position;       // Ray position (origin)\n    Vector3 direction;      // Ray direction (normalized)\n} Ray;\n\n// RayCollision, ray hit information\ntypedef struct RayCollision {\n    bool hit;               // Did the ray hit something?\n    float distance;         // Distance to the nearest hit\n    Vector3 point;          // Point of the nearest hit\n    Vector3 normal;         // Surface normal of hit\n} RayCollision;\n\n// BoundingBox\ntypedef struct BoundingBox {\n    Vector3 min;            // Minimum vertex box-corner\n    Vector3 max;            // Maximum vertex box-corner\n} BoundingBox;\n\n// Wave, audio wave data\ntypedef struct Wave {\n    unsigned int frameCount;    // Total number of frames (considering channels)\n    unsigned int sampleRate;    // Frequency (samples per second)\n    unsigned int sampleSize;    // Bit depth (bits per sample): 8, 16, 32 (24 not supported)\n    unsigned int channels;      // Number of channels (1-mono, 2-stereo, ...)\n    void *data;                 // Buffer data pointer\n} Wave;\n\n// Opaque structs declaration\n// NOTE: Actual structs are defined internally in raudio module\ntypedef struct rAudioBuffer rAudioBuffer;\ntypedef struct rAudioProcessor rAudioProcessor;\n\n// AudioStream, custom audio stream\ntypedef struct AudioStream {\n    rAudioBuffer *buffer;       // Pointer to internal data used by the audio system\n    rAudioProcessor *processor; // Pointer to internal data processor, useful for audio effects\n\n    unsigned int sampleRate;    // Frequency (samples per second)\n    unsigned int sampleSize;    // Bit depth (bits per sample): 8, 16, 32 (24 not supported)\n    unsigned int channels;      // Number of channels (1-mono, 2-stereo, ...)\n} AudioStream;\n\n// Sound\ntypedef struct Sound {\n    AudioStream stream;         // Audio stream\n    unsigned int frameCount;    // Total number of frames (considering channels)\n} Sound;\n\n// Music, audio stream, anything longer than ~10 seconds should be streamed\ntypedef struct Music {\n    AudioStream stream;         // Audio stream\n    unsigned int frameCount;    // Total number of frames (considering channels)\n    bool looping;               // Music looping enable\n\n    int ctxType;                // Type of music context (audio filetype)\n    void *ctxData;              // Audio context data, depends on type\n} Music;\n\n// VrDeviceInfo, Head-Mounted-Display device parameters\ntypedef struct VrDeviceInfo {\n    int hResolution;                // Horizontal resolution in pixels\n    int vResolution;                // Vertical resolution in pixels\n    float hScreenSize;              // Horizontal size in meters\n    float vScreenSize;              // Vertical size in meters\n    float eyeToScreenDistance;      // Distance between eye and display in meters\n    float lensSeparationDistance;   // Lens separation distance in meters\n    float interpupillaryDistance;   // IPD (distance between pupils) in meters\n    float lensDistortionValues[4];  // Lens distortion constant parameters\n    float chromaAbCorrection[4];    // Chromatic aberration correction parameters\n} VrDeviceInfo;\n\n// VrStereoConfig, VR stereo rendering configuration for simulator\ntypedef struct VrStereoConfig {\n    Matrix projection[2];           // VR projection matrices (per eye)\n    Matrix viewOffset[2];           // VR view offset matrices (per eye)\n    float leftLensCenter[2];        // VR left lens center\n    float rightLensCenter[2];       // VR right lens center\n    float leftScreenCenter[2];      // VR left screen center\n    float rightScreenCenter[2];     // VR right screen center\n    float scale[2];                 // VR distortion scale\n    float scaleIn[2];               // VR distortion scale in\n} VrStereoConfig;\n\n// File path list\ntypedef struct FilePathList {\n    unsigned int capacity;          // Filepaths max entries\n    unsigned int count;             // Filepaths entries count\n    char **paths;                   // Filepaths entries\n} FilePathList;\n\n// Automation event\ntypedef struct AutomationEvent {\n    unsigned int frame;             // Event frame\n    unsigned int type;              // Event type (AutomationEventType)\n    int params[4];                  // Event parameters (if required)\n} AutomationEvent;\n\n// Automation event list\ntypedef struct AutomationEventList {\n    unsigned int capacity;          // Events max entries (MAX_AUTOMATION_EVENTS)\n    unsigned int count;             // Events entries count\n    AutomationEvent *events;        // Events entries\n} AutomationEventList;\n\n//----------------------------------------------------------------------------------\n// Enumerators Definition\n//----------------------------------------------------------------------------------\n// System/Window config flags\n// NOTE: Every bit registers one state (use it with bit masks)\n// By default all flags are set to 0\ntypedef enum {\n    FLAG_VSYNC_HINT         = 0x00000040,   // Set to try enabling V-Sync on GPU\n    FLAG_FULLSCREEN_MODE    = 0x00000002,   // Set to run program in fullscreen\n    FLAG_WINDOW_RESIZABLE   = 0x00000004,   // Set to allow resizable window\n    FLAG_WINDOW_UNDECORATED = 0x00000008,   // Set to disable window decoration (frame and buttons)\n    FLAG_WINDOW_HIDDEN      = 0x00000080,   // Set to hide window\n    FLAG_WINDOW_MINIMIZED   = 0x00000200,   // Set to minimize window (iconify)\n    FLAG_WINDOW_MAXIMIZED   = 0x00000400,   // Set to maximize window (expanded to monitor)\n    FLAG_WINDOW_UNFOCUSED   = 0x00000800,   // Set to window non focused\n    FLAG_WINDOW_TOPMOST     = 0x00001000,   // Set to window always on top\n    FLAG_WINDOW_ALWAYS_RUN  = 0x00000100,   // Set to allow windows running while minimized\n    FLAG_WINDOW_TRANSPARENT = 0x00000010,   // Set to allow transparent framebuffer\n    FLAG_WINDOW_HIGHDPI     = 0x00002000,   // Set to support HighDPI\n    FLAG_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED\n    FLAG_BORDERLESS_WINDOWED_MODE = 0x00008000, // Set to run program in borderless windowed mode\n    FLAG_MSAA_4X_HINT       = 0x00000020,   // Set to try enabling MSAA 4X\n    FLAG_INTERLACED_HINT    = 0x00010000    // Set to try enabling interlaced video format (for V3D)\n} ConfigFlags;\n\n// Trace log level\n// NOTE: Organized by priority level\ntypedef enum {\n    LOG_ALL = 0,        // Display all logs\n    LOG_TRACE,          // Trace logging, intended for internal use only\n    LOG_DEBUG,          // Debug logging, used for internal debugging, it should be disabled on release builds\n    LOG_INFO,           // Info logging, used for program execution info\n    LOG_WARNING,        // Warning logging, used on recoverable failures\n    LOG_ERROR,          // Error logging, used on unrecoverable failures\n    LOG_FATAL,          // Fatal logging, used to abort program: exit(EXIT_FAILURE)\n    LOG_NONE            // Disable logging\n} TraceLogLevel;\n\n// Keyboard keys (US keyboard layout)\n// NOTE: Use GetKeyPressed() to allow redefining\n// required keys for alternative layouts\ntypedef enum {\n    KEY_NULL            = 0,        // Key: NULL, used for no key pressed\n    // Alphanumeric keys\n    KEY_APOSTROPHE      = 39,       // Key: '\n    KEY_COMMA           = 44,       // Key: ,\n    KEY_MINUS           = 45,       // Key: -\n    KEY_PERIOD          = 46,       // Key: .\n    KEY_SLASH           = 47,       // Key: /\n    KEY_ZERO            = 48,       // Key: 0\n    KEY_ONE             = 49,       // Key: 1\n    KEY_TWO             = 50,       // Key: 2\n    KEY_THREE           = 51,       // Key: 3\n    KEY_FOUR            = 52,       // Key: 4\n    KEY_FIVE            = 53,       // Key: 5\n    KEY_SIX             = 54,       // Key: 6\n    KEY_SEVEN           = 55,       // Key: 7\n    KEY_EIGHT           = 56,       // Key: 8\n    KEY_NINE            = 57,       // Key: 9\n    KEY_SEMICOLON       = 59,       // Key: ;\n    KEY_EQUAL           = 61,       // Key: =\n    KEY_A               = 65,       // Key: A | a\n    KEY_B               = 66,       // Key: B | b\n    KEY_C               = 67,       // Key: C | c\n    KEY_D               = 68,       // Key: D | d\n    KEY_E               = 69,       // Key: E | e\n    KEY_F               = 70,       // Key: F | f\n    KEY_G               = 71,       // Key: G | g\n    KEY_H               = 72,       // Key: H | h\n    KEY_I               = 73,       // Key: I | i\n    KEY_J               = 74,       // Key: J | j\n    KEY_K               = 75,       // Key: K | k\n    KEY_L               = 76,       // Key: L | l\n    KEY_M               = 77,       // Key: M | m\n    KEY_N               = 78,       // Key: N | n\n    KEY_O               = 79,       // Key: O | o\n    KEY_P               = 80,       // Key: P | p\n    KEY_Q               = 81,       // Key: Q | q\n    KEY_R               = 82,       // Key: R | r\n    KEY_S               = 83,       // Key: S | s\n    KEY_T               = 84,       // Key: T | t\n    KEY_U               = 85,       // Key: U | u\n    KEY_V               = 86,       // Key: V | v\n    KEY_W               = 87,       // Key: W | w\n    KEY_X               = 88,       // Key: X | x\n    KEY_Y               = 89,       // Key: Y | y\n    KEY_Z               = 90,       // Key: Z | z\n    KEY_LEFT_BRACKET    = 91,       // Key: [\n    KEY_BACKSLASH       = 92,       // Key: '\\'\n    KEY_RIGHT_BRACKET   = 93,       // Key: ]\n    KEY_GRAVE           = 96,       // Key: `\n    // Function keys\n    KEY_SPACE           = 32,       // Key: Space\n    KEY_ESCAPE          = 256,      // Key: Esc\n    KEY_ENTER           = 257,      // Key: Enter\n    KEY_TAB             = 258,      // Key: Tab\n    KEY_BACKSPACE       = 259,      // Key: Backspace\n    KEY_INSERT          = 260,      // Key: Ins\n    KEY_DELETE          = 261,      // Key: Del\n    KEY_RIGHT           = 262,      // Key: Cursor right\n    KEY_LEFT            = 263,      // Key: Cursor left\n    KEY_DOWN            = 264,      // Key: Cursor down\n    KEY_UP              = 265,      // Key: Cursor up\n    KEY_PAGE_UP         = 266,      // Key: Page up\n    KEY_PAGE_DOWN       = 267,      // Key: Page down\n    KEY_HOME            = 268,      // Key: Home\n    KEY_END             = 269,      // Key: End\n    KEY_CAPS_LOCK       = 280,      // Key: Caps lock\n    KEY_SCROLL_LOCK     = 281,      // Key: Scroll down\n    KEY_NUM_LOCK        = 282,      // Key: Num lock\n    KEY_PRINT_SCREEN    = 283,      // Key: Print screen\n    KEY_PAUSE           = 284,      // Key: Pause\n    KEY_F1              = 290,      // Key: F1\n    KEY_F2              = 291,      // Key: F2\n    KEY_F3              = 292,      // Key: F3\n    KEY_F4              = 293,      // Key: F4\n    KEY_F5              = 294,      // Key: F5\n    KEY_F6              = 295,      // Key: F6\n    KEY_F7              = 296,      // Key: F7\n    KEY_F8              = 297,      // Key: F8\n    KEY_F9              = 298,      // Key: F9\n    KEY_F10             = 299,      // Key: F10\n    KEY_F11             = 300,      // Key: F11\n    KEY_F12             = 301,      // Key: F12\n    KEY_LEFT_SHIFT      = 340,      // Key: Shift left\n    KEY_LEFT_CONTROL    = 341,      // Key: Control left\n    KEY_LEFT_ALT        = 342,      // Key: Alt left\n    KEY_LEFT_SUPER      = 343,      // Key: Super left\n    KEY_RIGHT_SHIFT     = 344,      // Key: Shift right\n    KEY_RIGHT_CONTROL   = 345,      // Key: Control right\n    KEY_RIGHT_ALT       = 346,      // Key: Alt right\n    KEY_RIGHT_SUPER     = 347,      // Key: Super right\n    KEY_KB_MENU         = 348,      // Key: KB menu\n    // Keypad keys\n    KEY_KP_0            = 320,      // Key: Keypad 0\n    KEY_KP_1            = 321,      // Key: Keypad 1\n    KEY_KP_2            = 322,      // Key: Keypad 2\n    KEY_KP_3            = 323,      // Key: Keypad 3\n    KEY_KP_4            = 324,      // Key: Keypad 4\n    KEY_KP_5            = 325,      // Key: Keypad 5\n    KEY_KP_6            = 326,      // Key: Keypad 6\n    KEY_KP_7            = 327,      // Key: Keypad 7\n    KEY_KP_8            = 328,      // Key: Keypad 8\n    KEY_KP_9            = 329,      // Key: Keypad 9\n    KEY_KP_DECIMAL      = 330,      // Key: Keypad .\n    KEY_KP_DIVIDE       = 331,      // Key: Keypad /\n    KEY_KP_MULTIPLY     = 332,      // Key: Keypad *\n    KEY_KP_SUBTRACT     = 333,      // Key: Keypad -\n    KEY_KP_ADD          = 334,      // Key: Keypad +\n    KEY_KP_ENTER        = 335,      // Key: Keypad Enter\n    KEY_KP_EQUAL        = 336,      // Key: Keypad =\n    // Android key buttons\n    KEY_BACK            = 4,        // Key: Android back button\n    KEY_MENU            = 5,        // Key: Android menu button\n    KEY_VOLUME_UP       = 24,       // Key: Android volume up button\n    KEY_VOLUME_DOWN     = 25        // Key: Android volume down button\n} KeyboardKey;\n\n// Add backwards compatibility support for deprecated names\n#define MOUSE_LEFT_BUTTON   MOUSE_BUTTON_LEFT\n#define MOUSE_RIGHT_BUTTON  MOUSE_BUTTON_RIGHT\n#define MOUSE_MIDDLE_BUTTON MOUSE_BUTTON_MIDDLE\n\n// Mouse buttons\ntypedef enum {\n    MOUSE_BUTTON_LEFT    = 0,       // Mouse button left\n    MOUSE_BUTTON_RIGHT   = 1,       // Mouse button right\n    MOUSE_BUTTON_MIDDLE  = 2,       // Mouse button middle (pressed wheel)\n    MOUSE_BUTTON_SIDE    = 3,       // Mouse button side (advanced mouse device)\n    MOUSE_BUTTON_EXTRA   = 4,       // Mouse button extra (advanced mouse device)\n    MOUSE_BUTTON_FORWARD = 5,       // Mouse button forward (advanced mouse device)\n    MOUSE_BUTTON_BACK    = 6,       // Mouse button back (advanced mouse device)\n} MouseButton;\n\n// Mouse cursor\ntypedef enum {\n    MOUSE_CURSOR_DEFAULT       = 0,     // Default pointer shape\n    MOUSE_CURSOR_ARROW         = 1,     // Arrow shape\n    MOUSE_CURSOR_IBEAM         = 2,     // Text writing cursor shape\n    MOUSE_CURSOR_CROSSHAIR     = 3,     // Cross shape\n    MOUSE_CURSOR_POINTING_HAND = 4,     // Pointing hand cursor\n    MOUSE_CURSOR_RESIZE_EW     = 5,     // Horizontal resize/move arrow shape\n    MOUSE_CURSOR_RESIZE_NS     = 6,     // Vertical resize/move arrow shape\n    MOUSE_CURSOR_RESIZE_NWSE   = 7,     // Top-left to bottom-right diagonal resize/move arrow shape\n    MOUSE_CURSOR_RESIZE_NESW   = 8,     // The top-right to bottom-left diagonal resize/move arrow shape\n    MOUSE_CURSOR_RESIZE_ALL    = 9,     // The omnidirectional resize/move cursor shape\n    MOUSE_CURSOR_NOT_ALLOWED   = 10     // The operation-not-allowed shape\n} MouseCursor;\n\n// Gamepad buttons\ntypedef enum {\n    GAMEPAD_BUTTON_UNKNOWN = 0,         // Unknown button, just for error checking\n    GAMEPAD_BUTTON_LEFT_FACE_UP,        // Gamepad left DPAD up button\n    GAMEPAD_BUTTON_LEFT_FACE_RIGHT,     // Gamepad left DPAD right button\n    GAMEPAD_BUTTON_LEFT_FACE_DOWN,      // Gamepad left DPAD down button\n    GAMEPAD_BUTTON_LEFT_FACE_LEFT,      // Gamepad left DPAD left button\n    GAMEPAD_BUTTON_RIGHT_FACE_UP,       // Gamepad right button up (i.e. PS3: Triangle, Xbox: Y)\n    GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,    // Gamepad right button right (i.e. PS3: Circle, Xbox: B)\n    GAMEPAD_BUTTON_RIGHT_FACE_DOWN,     // Gamepad right button down (i.e. PS3: Cross, Xbox: A)\n    GAMEPAD_BUTTON_RIGHT_FACE_LEFT,     // Gamepad right button left (i.e. PS3: Square, Xbox: X)\n    GAMEPAD_BUTTON_LEFT_TRIGGER_1,      // Gamepad top/back trigger left (first), it could be a trailing button\n    GAMEPAD_BUTTON_LEFT_TRIGGER_2,      // Gamepad top/back trigger left (second), it could be a trailing button\n    GAMEPAD_BUTTON_RIGHT_TRIGGER_1,     // Gamepad top/back trigger right (first), it could be a trailing button\n    GAMEPAD_BUTTON_RIGHT_TRIGGER_2,     // Gamepad top/back trigger right (second), it could be a trailing button\n    GAMEPAD_BUTTON_MIDDLE_LEFT,         // Gamepad center buttons, left one (i.e. PS3: Select)\n    GAMEPAD_BUTTON_MIDDLE,              // Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX)\n    GAMEPAD_BUTTON_MIDDLE_RIGHT,        // Gamepad center buttons, right one (i.e. PS3: Start)\n    GAMEPAD_BUTTON_LEFT_THUMB,          // Gamepad joystick pressed button left\n    GAMEPAD_BUTTON_RIGHT_THUMB          // Gamepad joystick pressed button right\n} GamepadButton;\n\n// Gamepad axis\ntypedef enum {\n    GAMEPAD_AXIS_LEFT_X        = 0,     // Gamepad left stick X axis\n    GAMEPAD_AXIS_LEFT_Y        = 1,     // Gamepad left stick Y axis\n    GAMEPAD_AXIS_RIGHT_X       = 2,     // Gamepad right stick X axis\n    GAMEPAD_AXIS_RIGHT_Y       = 3,     // Gamepad right stick Y axis\n    GAMEPAD_AXIS_LEFT_TRIGGER  = 4,     // Gamepad back trigger left, pressure level: [1..-1]\n    GAMEPAD_AXIS_RIGHT_TRIGGER = 5      // Gamepad back trigger right, pressure level: [1..-1]\n} GamepadAxis;\n\n// Material map index\ntypedef enum {\n    MATERIAL_MAP_ALBEDO = 0,        // Albedo material (same as: MATERIAL_MAP_DIFFUSE)\n    MATERIAL_MAP_METALNESS,         // Metalness material (same as: MATERIAL_MAP_SPECULAR)\n    MATERIAL_MAP_NORMAL,            // Normal material\n    MATERIAL_MAP_ROUGHNESS,         // Roughness material\n    MATERIAL_MAP_OCCLUSION,         // Ambient occlusion material\n    MATERIAL_MAP_EMISSION,          // Emission material\n    MATERIAL_MAP_HEIGHT,            // Heightmap material\n    MATERIAL_MAP_CUBEMAP,           // Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP)\n    MATERIAL_MAP_IRRADIANCE,        // Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP)\n    MATERIAL_MAP_PREFILTER,         // Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP)\n    MATERIAL_MAP_BRDF               // Brdf material\n} MaterialMapIndex;\n\n#define MATERIAL_MAP_DIFFUSE      MATERIAL_MAP_ALBEDO\n#define MATERIAL_MAP_SPECULAR     MATERIAL_MAP_METALNESS\n\n// Shader location index\ntypedef enum {\n    SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position\n    SHADER_LOC_VERTEX_TEXCOORD01,   // Shader location: vertex attribute: texcoord01\n    SHADER_LOC_VERTEX_TEXCOORD02,   // Shader location: vertex attribute: texcoord02\n    SHADER_LOC_VERTEX_NORMAL,       // Shader location: vertex attribute: normal\n    SHADER_LOC_VERTEX_TANGENT,      // Shader location: vertex attribute: tangent\n    SHADER_LOC_VERTEX_COLOR,        // Shader location: vertex attribute: color\n    SHADER_LOC_MATRIX_MVP,          // Shader location: matrix uniform: model-view-projection\n    SHADER_LOC_MATRIX_VIEW,         // Shader location: matrix uniform: view (camera transform)\n    SHADER_LOC_MATRIX_PROJECTION,   // Shader location: matrix uniform: projection\n    SHADER_LOC_MATRIX_MODEL,        // Shader location: matrix uniform: model (transform)\n    SHADER_LOC_MATRIX_NORMAL,       // Shader location: matrix uniform: normal\n    SHADER_LOC_VECTOR_VIEW,         // Shader location: vector uniform: view\n    SHADER_LOC_COLOR_DIFFUSE,       // Shader location: vector uniform: diffuse color\n    SHADER_LOC_COLOR_SPECULAR,      // Shader location: vector uniform: specular color\n    SHADER_LOC_COLOR_AMBIENT,       // Shader location: vector uniform: ambient color\n    SHADER_LOC_MAP_ALBEDO,          // Shader location: sampler2d texture: albedo (same as: SHADER_LOC_MAP_DIFFUSE)\n    SHADER_LOC_MAP_METALNESS,       // Shader location: sampler2d texture: metalness (same as: SHADER_LOC_MAP_SPECULAR)\n    SHADER_LOC_MAP_NORMAL,          // Shader location: sampler2d texture: normal\n    SHADER_LOC_MAP_ROUGHNESS,       // Shader location: sampler2d texture: roughness\n    SHADER_LOC_MAP_OCCLUSION,       // Shader location: sampler2d texture: occlusion\n    SHADER_LOC_MAP_EMISSION,        // Shader location: sampler2d texture: emission\n    SHADER_LOC_MAP_HEIGHT,          // Shader location: sampler2d texture: height\n    SHADER_LOC_MAP_CUBEMAP,         // Shader location: samplerCube texture: cubemap\n    SHADER_LOC_MAP_IRRADIANCE,      // Shader location: samplerCube texture: irradiance\n    SHADER_LOC_MAP_PREFILTER,       // Shader location: samplerCube texture: prefilter\n    SHADER_LOC_MAP_BRDF,            // Shader location: sampler2d texture: brdf\n    SHADER_LOC_VERTEX_BONEIDS,      // Shader location: vertex attribute: boneIds\n    SHADER_LOC_VERTEX_BONEWEIGHTS,  // Shader location: vertex attribute: boneWeights\n    SHADER_LOC_BONE_MATRICES        // Shader location: array of matrices uniform: boneMatrices\n} ShaderLocationIndex;\n\n#define SHADER_LOC_MAP_DIFFUSE      SHADER_LOC_MAP_ALBEDO\n#define SHADER_LOC_MAP_SPECULAR     SHADER_LOC_MAP_METALNESS\n\n// Shader uniform data type\ntypedef enum {\n    SHADER_UNIFORM_FLOAT = 0,       // Shader uniform type: float\n    SHADER_UNIFORM_VEC2,            // Shader uniform type: vec2 (2 float)\n    SHADER_UNIFORM_VEC3,            // Shader uniform type: vec3 (3 float)\n    SHADER_UNIFORM_VEC4,            // Shader uniform type: vec4 (4 float)\n    SHADER_UNIFORM_INT,             // Shader uniform type: int\n    SHADER_UNIFORM_IVEC2,           // Shader uniform type: ivec2 (2 int)\n    SHADER_UNIFORM_IVEC3,           // Shader uniform type: ivec3 (3 int)\n    SHADER_UNIFORM_IVEC4,           // Shader uniform type: ivec4 (4 int)\n    SHADER_UNIFORM_SAMPLER2D        // Shader uniform type: sampler2d\n} ShaderUniformDataType;\n\n// Shader attribute data types\ntypedef enum {\n    SHADER_ATTRIB_FLOAT = 0,        // Shader attribute type: float\n    SHADER_ATTRIB_VEC2,             // Shader attribute type: vec2 (2 float)\n    SHADER_ATTRIB_VEC3,             // Shader attribute type: vec3 (3 float)\n    SHADER_ATTRIB_VEC4              // Shader attribute type: vec4 (4 float)\n} ShaderAttributeDataType;\n\n// Pixel formats\n// NOTE: Support depends on OpenGL version and platform\ntypedef enum {\n    PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha)\n    PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,    // 8*2 bpp (2 channels)\n    PIXELFORMAT_UNCOMPRESSED_R5G6B5,        // 16 bpp\n    PIXELFORMAT_UNCOMPRESSED_R8G8B8,        // 24 bpp\n    PIXELFORMAT_UNCOMPRESSED_R5G5B5A1,      // 16 bpp (1 bit alpha)\n    PIXELFORMAT_UNCOMPRESSED_R4G4B4A4,      // 16 bpp (4 bit alpha)\n    PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,      // 32 bpp\n    PIXELFORMAT_UNCOMPRESSED_R32,           // 32 bpp (1 channel - float)\n    PIXELFORMAT_UNCOMPRESSED_R32G32B32,     // 32*3 bpp (3 channels - float)\n    PIXELFORMAT_UNCOMPRESSED_R32G32B32A32,  // 32*4 bpp (4 channels - float)\n    PIXELFORMAT_UNCOMPRESSED_R16,           // 16 bpp (1 channel - half float)\n    PIXELFORMAT_UNCOMPRESSED_R16G16B16,     // 16*3 bpp (3 channels - half float)\n    PIXELFORMAT_UNCOMPRESSED_R16G16B16A16,  // 16*4 bpp (4 channels - half float)\n    PIXELFORMAT_COMPRESSED_DXT1_RGB,        // 4 bpp (no alpha)\n    PIXELFORMAT_COMPRESSED_DXT1_RGBA,       // 4 bpp (1 bit alpha)\n    PIXELFORMAT_COMPRESSED_DXT3_RGBA,       // 8 bpp\n    PIXELFORMAT_COMPRESSED_DXT5_RGBA,       // 8 bpp\n    PIXELFORMAT_COMPRESSED_ETC1_RGB,        // 4 bpp\n    PIXELFORMAT_COMPRESSED_ETC2_RGB,        // 4 bpp\n    PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA,   // 8 bpp\n    PIXELFORMAT_COMPRESSED_PVRT_RGB,        // 4 bpp\n    PIXELFORMAT_COMPRESSED_PVRT_RGBA,       // 4 bpp\n    PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA,   // 8 bpp\n    PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA    // 2 bpp\n} PixelFormat;\n\n// Texture parameters: filter mode\n// NOTE 1: Filtering considers mipmaps if available in the texture\n// NOTE 2: Filter is accordingly set for minification and magnification\ntypedef enum {\n    TEXTURE_FILTER_POINT = 0,               // No filter, just pixel approximation\n    TEXTURE_FILTER_BILINEAR,                // Linear filtering\n    TEXTURE_FILTER_TRILINEAR,               // Trilinear filtering (linear with mipmaps)\n    TEXTURE_FILTER_ANISOTROPIC_4X,          // Anisotropic filtering 4x\n    TEXTURE_FILTER_ANISOTROPIC_8X,          // Anisotropic filtering 8x\n    TEXTURE_FILTER_ANISOTROPIC_16X,         // Anisotropic filtering 16x\n} TextureFilter;\n\n// Texture parameters: wrap mode\ntypedef enum {\n    TEXTURE_WRAP_REPEAT = 0,                // Repeats texture in tiled mode\n    TEXTURE_WRAP_CLAMP,                     // Clamps texture to edge pixel in tiled mode\n    TEXTURE_WRAP_MIRROR_REPEAT,             // Mirrors and repeats the texture in tiled mode\n    TEXTURE_WRAP_MIRROR_CLAMP               // Mirrors and clamps to border the texture in tiled mode\n} TextureWrap;\n\n// Cubemap layouts\ntypedef enum {\n    CUBEMAP_LAYOUT_AUTO_DETECT = 0,         // Automatically detect layout type\n    CUBEMAP_LAYOUT_LINE_VERTICAL,           // Layout is defined by a vertical line with faces\n    CUBEMAP_LAYOUT_LINE_HORIZONTAL,         // Layout is defined by a horizontal line with faces\n    CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR,     // Layout is defined by a 3x4 cross with cubemap faces\n    CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE     // Layout is defined by a 4x3 cross with cubemap faces\n} CubemapLayout;\n\n// Font type, defines generation method\ntypedef enum {\n    FONT_DEFAULT = 0,               // Default font generation, anti-aliased\n    FONT_BITMAP,                    // Bitmap font generation, no anti-aliasing\n    FONT_SDF                        // SDF font generation, requires external shader\n} FontType;\n\n// Color blending modes (pre-defined)\ntypedef enum {\n    BLEND_ALPHA = 0,                // Blend textures considering alpha (default)\n    BLEND_ADDITIVE,                 // Blend textures adding colors\n    BLEND_MULTIPLIED,               // Blend textures multiplying colors\n    BLEND_ADD_COLORS,               // Blend textures adding colors (alternative)\n    BLEND_SUBTRACT_COLORS,          // Blend textures subtracting colors (alternative)\n    BLEND_ALPHA_PREMULTIPLY,        // Blend premultiplied textures considering alpha\n    BLEND_CUSTOM,                   // Blend textures using custom src/dst factors (use rlSetBlendFactors())\n    BLEND_CUSTOM_SEPARATE           // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate())\n} BlendMode;\n\n// Gesture\n// NOTE: Provided as bit-wise flags to enable only desired gestures\ntypedef enum {\n    GESTURE_NONE        = 0,        // No gesture\n    GESTURE_TAP         = 1,        // Tap gesture\n    GESTURE_DOUBLETAP   = 2,        // Double tap gesture\n    GESTURE_HOLD        = 4,        // Hold gesture\n    GESTURE_DRAG        = 8,        // Drag gesture\n    GESTURE_SWIPE_RIGHT = 16,       // Swipe right gesture\n    GESTURE_SWIPE_LEFT  = 32,       // Swipe left gesture\n    GESTURE_SWIPE_UP    = 64,       // Swipe up gesture\n    GESTURE_SWIPE_DOWN  = 128,      // Swipe down gesture\n    GESTURE_PINCH_IN    = 256,      // Pinch in gesture\n    GESTURE_PINCH_OUT   = 512       // Pinch out gesture\n} Gesture;\n\n// Camera system modes\ntypedef enum {\n    CAMERA_CUSTOM = 0,              // Camera custom, controlled by user (UpdateCamera() does nothing)\n    CAMERA_FREE,                    // Camera free mode\n    CAMERA_ORBITAL,                 // Camera orbital, around target, zoom supported\n    CAMERA_FIRST_PERSON,            // Camera first person\n    CAMERA_THIRD_PERSON             // Camera third person\n} CameraMode;\n\n// Camera projection\ntypedef enum {\n    CAMERA_PERSPECTIVE = 0,         // Perspective projection\n    CAMERA_ORTHOGRAPHIC             // Orthographic projection\n} CameraProjection;\n\n// N-patch layout\ntypedef enum {\n    NPATCH_NINE_PATCH = 0,          // Npatch layout: 3x3 tiles\n    NPATCH_THREE_PATCH_VERTICAL,    // Npatch layout: 1x3 tiles\n    NPATCH_THREE_PATCH_HORIZONTAL   // Npatch layout: 3x1 tiles\n} NPatchLayout;\n\n// Callbacks to hook some internal functions\n// WARNING: These callbacks are intended for advanced users\ntypedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args);  // Logging: Redirect trace log messages\ntypedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize);    // FileIO: Load binary data\ntypedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize);   // FileIO: Save binary data\ntypedef char *(*LoadFileTextCallback)(const char *fileName);            // FileIO: Load text data\ntypedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data\n\n//------------------------------------------------------------------------------------\n// Global Variables Definition\n//------------------------------------------------------------------------------------\n// It's lonely here...\n\n//------------------------------------------------------------------------------------\n// Window and Graphics Device Functions (Module: core)\n//------------------------------------------------------------------------------------\n\n#if defined(__cplusplus)\nextern \"C\" {            // Prevents name mangling of functions\n#endif\n\n// Window-related functions\nRLAPI void InitWindow(int width, int height, const char *title);  // Initialize window and OpenGL context\nRLAPI void CloseWindow(void);                                     // Close window and unload OpenGL context\nRLAPI bool WindowShouldClose(void);                               // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked)\nRLAPI bool IsWindowReady(void);                                   // Check if window has been initialized successfully\nRLAPI bool IsWindowFullscreen(void);                              // Check if window is currently fullscreen\nRLAPI bool IsWindowHidden(void);                                  // Check if window is currently hidden\nRLAPI bool IsWindowMinimized(void);                               // Check if window is currently minimized\nRLAPI bool IsWindowMaximized(void);                               // Check if window is currently maximized\nRLAPI bool IsWindowFocused(void);                                 // Check if window is currently focused\nRLAPI bool IsWindowResized(void);                                 // Check if window has been resized last frame\nRLAPI bool IsWindowState(unsigned int flag);                      // Check if one specific window flag is enabled\nRLAPI void SetWindowState(unsigned int flags);                    // Set window configuration state using flags\nRLAPI void ClearWindowState(unsigned int flags);                  // Clear window configuration state flags\nRLAPI void ToggleFullscreen(void);                                // Toggle window state: fullscreen/windowed, resizes monitor to match window resolution\nRLAPI void ToggleBorderlessWindowed(void);                        // Toggle window state: borderless windowed, resizes window to match monitor resolution\nRLAPI void MaximizeWindow(void);                                  // Set window state: maximized, if resizable\nRLAPI void MinimizeWindow(void);                                  // Set window state: minimized, if resizable\nRLAPI void RestoreWindow(void);                                   // Set window state: not minimized/maximized\nRLAPI void SetWindowIcon(Image image);                            // Set icon for window (single image, RGBA 32bit)\nRLAPI void SetWindowIcons(Image *images, int count);              // Set icon for window (multiple images, RGBA 32bit)\nRLAPI void SetWindowTitle(const char *title);                     // Set title for window\nRLAPI void SetWindowPosition(int x, int y);                       // Set window position on screen\nRLAPI void SetWindowMonitor(int monitor);                         // Set monitor for the current window\nRLAPI void SetWindowMinSize(int width, int height);               // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)\nRLAPI void SetWindowMaxSize(int width, int height);               // Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE)\nRLAPI void SetWindowSize(int width, int height);                  // Set window dimensions\nRLAPI void SetWindowOpacity(float opacity);                       // Set window opacity [0.0f..1.0f]\nRLAPI void SetWindowFocused(void);                                // Set window focused\nRLAPI void *GetWindowHandle(void);                                // Get native window handle\nRLAPI int GetScreenWidth(void);                                   // Get current screen width\nRLAPI int GetScreenHeight(void);                                  // Get current screen height\nRLAPI int GetRenderWidth(void);                                   // Get current render width (it considers HiDPI)\nRLAPI int GetRenderHeight(void);                                  // Get current render height (it considers HiDPI)\nRLAPI int GetMonitorCount(void);                                  // Get number of connected monitors\nRLAPI int GetCurrentMonitor(void);                                // Get current monitor where window is placed\nRLAPI Vector2 GetMonitorPosition(int monitor);                    // Get specified monitor position\nRLAPI int GetMonitorWidth(int monitor);                           // Get specified monitor width (current video mode used by monitor)\nRLAPI int GetMonitorHeight(int monitor);                          // Get specified monitor height (current video mode used by monitor)\nRLAPI int GetMonitorPhysicalWidth(int monitor);                   // Get specified monitor physical width in millimetres\nRLAPI int GetMonitorPhysicalHeight(int monitor);                  // Get specified monitor physical height in millimetres\nRLAPI int GetMonitorRefreshRate(int monitor);                     // Get specified monitor refresh rate\nRLAPI Vector2 GetWindowPosition(void);                            // Get window position XY on monitor\nRLAPI Vector2 GetWindowScaleDPI(void);                            // Get window scale DPI factor\nRLAPI const char *GetMonitorName(int monitor);                    // Get the human-readable, UTF-8 encoded name of the specified monitor\nRLAPI void SetClipboardText(const char *text);                    // Set clipboard text content\nRLAPI const char *GetClipboardText(void);                         // Get clipboard text content\nRLAPI Image GetClipboardImage(void);                              // Get clipboard image content\nRLAPI void EnableEventWaiting(void);                              // Enable waiting for events on EndDrawing(), no automatic event polling\nRLAPI void DisableEventWaiting(void);                             // Disable waiting for events on EndDrawing(), automatic events polling\n\n// Cursor-related functions\nRLAPI void ShowCursor(void);                                      // Shows cursor\nRLAPI void HideCursor(void);                                      // Hides cursor\nRLAPI bool IsCursorHidden(void);                                  // Check if cursor is not visible\nRLAPI void EnableCursor(void);                                    // Enables cursor (unlock cursor)\nRLAPI void DisableCursor(void);                                   // Disables cursor (lock cursor)\nRLAPI bool IsCursorOnScreen(void);                                // Check if cursor is on the screen\n\n// Drawing-related functions\nRLAPI void ClearBackground(Color color);                          // Set background color (framebuffer clear color)\nRLAPI void BeginDrawing(void);                                    // Setup canvas (framebuffer) to start drawing\nRLAPI void EndDrawing(void);                                      // End canvas drawing and swap buffers (double buffering)\nRLAPI void BeginMode2D(Camera2D camera);                          // Begin 2D mode with custom camera (2D)\nRLAPI void EndMode2D(void);                                       // Ends 2D mode with custom camera\nRLAPI void BeginMode3D(Camera3D camera);                          // Begin 3D mode with custom camera (3D)\nRLAPI void EndMode3D(void);                                       // Ends 3D mode and returns to default 2D orthographic mode\nRLAPI void BeginTextureMode(RenderTexture2D target);              // Begin drawing to render texture\nRLAPI void EndTextureMode(void);                                  // Ends drawing to render texture\nRLAPI void BeginShaderMode(Shader shader);                        // Begin custom shader drawing\nRLAPI void EndShaderMode(void);                                   // End custom shader drawing (use default shader)\nRLAPI void BeginBlendMode(int mode);                              // Begin blending mode (alpha, additive, multiplied, subtract, custom)\nRLAPI void EndBlendMode(void);                                    // End blending mode (reset to default: alpha blending)\nRLAPI void BeginScissorMode(int x, int y, int width, int height); // Begin scissor mode (define screen area for following drawing)\nRLAPI void EndScissorMode(void);                                  // End scissor mode\nRLAPI void BeginVrStereoMode(VrStereoConfig config);              // Begin stereo rendering (requires VR simulator)\nRLAPI void EndVrStereoMode(void);                                 // End stereo rendering (requires VR simulator)\n\n// VR stereo config functions for VR simulator\nRLAPI VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device);     // Load VR stereo config for VR simulator device parameters\nRLAPI void UnloadVrStereoConfig(VrStereoConfig config);           // Unload VR stereo config\n\n// Shader management functions\n// NOTE: Shader functionality is not available on OpenGL 1.1\nRLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName);   // Load shader from files and bind default locations\nRLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations\nRLAPI bool IsShaderValid(Shader shader);                                   // Check if a shader is valid (loaded on GPU)\nRLAPI int GetShaderLocation(Shader shader, const char *uniformName);       // Get shader uniform location\nRLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName);  // Get shader attribute location\nRLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType);               // Set shader uniform value\nRLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count);   // Set shader uniform value vector\nRLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat);         // Set shader uniform value (matrix 4x4)\nRLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value for texture (sampler2d)\nRLAPI void UnloadShader(Shader shader);                                    // Unload shader from GPU memory (VRAM)\n\n// Screen-space-related functions\n#define GetMouseRay GetScreenToWorldRay     // Compatibility hack for previous raylib versions\nRLAPI Ray GetScreenToWorldRay(Vector2 position, Camera camera);         // Get a ray trace from screen position (i.e mouse)\nRLAPI Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height); // Get a ray trace from screen position (i.e mouse) in a viewport\nRLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera);        // Get the screen space position for a 3d world space position\nRLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position\nRLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera);    // Get the screen space position for a 2d camera world space position\nRLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera);    // Get the world space position for a 2d camera screen space position\nRLAPI Matrix GetCameraMatrix(Camera camera);                            // Get camera transform matrix (view matrix)\nRLAPI Matrix GetCameraMatrix2D(Camera2D camera);                        // Get camera 2d transform matrix\n\n// Timing-related functions\nRLAPI void SetTargetFPS(int fps);                                 // Set target FPS (maximum)\nRLAPI float GetFrameTime(void);                                   // Get time in seconds for last frame drawn (delta time)\nRLAPI double GetTime(void);                                       // Get elapsed time in seconds since InitWindow()\nRLAPI int GetFPS(void);                                           // Get current FPS\n\n// Custom frame control functions\n// NOTE: Those functions are intended for advanced users that want full control over the frame processing\n// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()\n// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL\nRLAPI void SwapScreenBuffer(void);                                // Swap back buffer with front buffer (screen drawing)\nRLAPI void PollInputEvents(void);                                 // Register all input events\nRLAPI void WaitTime(double seconds);                              // Wait for some time (halt program execution)\n\n// Random values generation functions\nRLAPI void SetRandomSeed(unsigned int seed);                      // Set the seed for the random number generator\nRLAPI int GetRandomValue(int min, int max);                       // Get a random value between min and max (both included)\nRLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated\nRLAPI void UnloadRandomSequence(int *sequence);                   // Unload random values sequence\n\n// Misc. functions\nRLAPI void TakeScreenshot(const char *fileName);                  // Takes a screenshot of current screen (filename extension defines format)\nRLAPI void SetConfigFlags(unsigned int flags);                    // Setup init configuration flags (view FLAGS)\nRLAPI void OpenURL(const char *url);                              // Open URL with default system browser (if available)\n\n// NOTE: Following functions implemented in module [utils]\n//------------------------------------------------------------------\nRLAPI void TraceLog(int logLevel, const char *text, ...);         // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)\nRLAPI void SetTraceLogLevel(int logLevel);                        // Set the current threshold (minimum) log level\nRLAPI void *MemAlloc(unsigned int size);                          // Internal memory allocator\nRLAPI void *MemRealloc(void *ptr, unsigned int size);             // Internal memory reallocator\nRLAPI void MemFree(void *ptr);                                    // Internal memory free\n\n// Set custom callbacks\n// WARNING: Callbacks setup is intended for advanced users\nRLAPI void SetTraceLogCallback(TraceLogCallback callback);         // Set custom trace log\nRLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader\nRLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver\nRLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader\nRLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver\n\n// Files management functions\nRLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read)\nRLAPI void UnloadFileData(unsigned char *data);                   // Unload file data allocated by LoadFileData()\nRLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write), returns true on success\nRLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName); // Export data to code (.h), returns true on success\nRLAPI char *LoadFileText(const char *fileName);                   // Load text data from file (read), returns a '\\0' terminated string\nRLAPI void UnloadFileText(char *text);                            // Unload file text data allocated by LoadFileText()\nRLAPI bool SaveFileText(const char *fileName, char *text);        // Save text data to file (write), string must be '\\0' terminated, returns true on success\n//------------------------------------------------------------------\n\n// File system functions\nRLAPI bool FileExists(const char *fileName);                      // Check if file exists\nRLAPI bool DirectoryExists(const char *dirPath);                  // Check if a directory path exists\nRLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav)\nRLAPI int GetFileLength(const char *fileName);                    // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h)\nRLAPI const char *GetFileExtension(const char *fileName);         // Get pointer to extension for a filename string (includes dot: '.png')\nRLAPI const char *GetFileName(const char *filePath);              // Get pointer to filename for a path string\nRLAPI const char *GetFileNameWithoutExt(const char *filePath);    // Get filename string without extension (uses static string)\nRLAPI const char *GetDirectoryPath(const char *filePath);         // Get full path for a given fileName with path (uses static string)\nRLAPI const char *GetPrevDirectoryPath(const char *dirPath);      // Get previous directory path for a given path (uses static string)\nRLAPI const char *GetWorkingDirectory(void);                      // Get current working directory (uses static string)\nRLAPI const char *GetApplicationDirectory(void);                  // Get the directory of the running application (uses static string)\nRLAPI int MakeDirectory(const char *dirPath);                     // Create directories (including full path requested), returns 0 on success\nRLAPI bool ChangeDirectory(const char *dir);                      // Change working directory, return true on success\nRLAPI bool IsPathFile(const char *path);                          // Check if a given path is a file or a directory\nRLAPI bool IsFileNameValid(const char *fileName);                 // Check if fileName is valid for the platform/OS\nRLAPI FilePathList LoadDirectoryFiles(const char *dirPath);       // Load directory filepaths\nRLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result\nRLAPI void UnloadDirectoryFiles(FilePathList files);              // Unload filepaths\nRLAPI bool IsFileDropped(void);                                   // Check if a file has been dropped into window\nRLAPI FilePathList LoadDroppedFiles(void);                        // Load dropped filepaths\nRLAPI void UnloadDroppedFiles(FilePathList files);                // Unload dropped filepaths\nRLAPI long GetFileModTime(const char *fileName);                  // Get file modification time (last write time)\n\n// Compression/Encoding functionality\nRLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize);        // Compress data (DEFLATE algorithm), memory must be MemFree()\nRLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize);  // Decompress data (DEFLATE algorithm), memory must be MemFree()\nRLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize);               // Encode data to Base64 string, memory must be MemFree()\nRLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize);                    // Decode Base64 string data, memory must be MemFree()\nRLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize);     // Compute CRC32 hash code\nRLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize);      // Compute MD5 hash code, returns static int[4] (16 bytes)\nRLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize);      // Compute SHA1 hash code, returns static int[5] (20 bytes)\n\n\n// Automation events functionality\nRLAPI AutomationEventList LoadAutomationEventList(const char *fileName);                // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS\nRLAPI void UnloadAutomationEventList(AutomationEventList list);                         // Unload automation events list from file\nRLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName);   // Export automation events list as text file\nRLAPI void SetAutomationEventList(AutomationEventList *list);                           // Set automation event list to record to\nRLAPI void SetAutomationEventBaseFrame(int frame);                                      // Set automation event internal base frame to start recording\nRLAPI void StartAutomationEventRecording(void);                                         // Start recording automation events (AutomationEventList must be set)\nRLAPI void StopAutomationEventRecording(void);                                          // Stop recording automation events\nRLAPI void PlayAutomationEvent(AutomationEvent event);                                  // Play a recorded automation event\n\n//------------------------------------------------------------------------------------\n// Input Handling Functions (Module: core)\n//------------------------------------------------------------------------------------\n\n// Input-related functions: keyboard\nRLAPI bool IsKeyPressed(int key);                             // Check if a key has been pressed once\nRLAPI bool IsKeyPressedRepeat(int key);                       // Check if a key has been pressed again\nRLAPI bool IsKeyDown(int key);                                // Check if a key is being pressed\nRLAPI bool IsKeyReleased(int key);                            // Check if a key has been released once\nRLAPI bool IsKeyUp(int key);                                  // Check if a key is NOT being pressed\nRLAPI int GetKeyPressed(void);                                // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty\nRLAPI int GetCharPressed(void);                               // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty\nRLAPI void SetExitKey(int key);                               // Set a custom key to exit program (default is ESC)\n\n// Input-related functions: gamepads\nRLAPI bool IsGamepadAvailable(int gamepad);                                        // Check if a gamepad is available\nRLAPI const char *GetGamepadName(int gamepad);                                     // Get gamepad internal name id\nRLAPI bool IsGamepadButtonPressed(int gamepad, int button);                        // Check if a gamepad button has been pressed once\nRLAPI bool IsGamepadButtonDown(int gamepad, int button);                           // Check if a gamepad button is being pressed\nRLAPI bool IsGamepadButtonReleased(int gamepad, int button);                       // Check if a gamepad button has been released once\nRLAPI bool IsGamepadButtonUp(int gamepad, int button);                             // Check if a gamepad button is NOT being pressed\nRLAPI int GetGamepadButtonPressed(void);                                           // Get the last gamepad button pressed\nRLAPI int GetGamepadAxisCount(int gamepad);                                        // Get gamepad axis count for a gamepad\nRLAPI float GetGamepadAxisMovement(int gamepad, int axis);                         // Get axis movement value for a gamepad axis\nRLAPI int SetGamepadMappings(const char *mappings);                                // Set internal gamepad mappings (SDL_GameControllerDB)\nRLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration); // Set gamepad vibration for both motors (duration in seconds)\n\n// Input-related functions: mouse\nRLAPI bool IsMouseButtonPressed(int button);                  // Check if a mouse button has been pressed once\nRLAPI bool IsMouseButtonDown(int button);                     // Check if a mouse button is being pressed\nRLAPI bool IsMouseButtonReleased(int button);                 // Check if a mouse button has been released once\nRLAPI bool IsMouseButtonUp(int button);                       // Check if a mouse button is NOT being pressed\nRLAPI int GetMouseX(void);                                    // Get mouse position X\nRLAPI int GetMouseY(void);                                    // Get mouse position Y\nRLAPI Vector2 GetMousePosition(void);                         // Get mouse position XY\nRLAPI Vector2 GetMouseDelta(void);                            // Get mouse delta between frames\nRLAPI void SetMousePosition(int x, int y);                    // Set mouse position XY\nRLAPI void SetMouseOffset(int offsetX, int offsetY);          // Set mouse offset\nRLAPI void SetMouseScale(float scaleX, float scaleY);         // Set mouse scaling\nRLAPI float GetMouseWheelMove(void);                          // Get mouse wheel movement for X or Y, whichever is larger\nRLAPI Vector2 GetMouseWheelMoveV(void);                       // Get mouse wheel movement for both X and Y\nRLAPI void SetMouseCursor(int cursor);                        // Set mouse cursor\n\n// Input-related functions: touch\nRLAPI int GetTouchX(void);                                    // Get touch position X for touch point 0 (relative to screen size)\nRLAPI int GetTouchY(void);                                    // Get touch position Y for touch point 0 (relative to screen size)\nRLAPI Vector2 GetTouchPosition(int index);                    // Get touch position XY for a touch point index (relative to screen size)\nRLAPI int GetTouchPointId(int index);                         // Get touch point identifier for given index\nRLAPI int GetTouchPointCount(void);                           // Get number of touch points\n\n//------------------------------------------------------------------------------------\n// Gestures and Touch Handling Functions (Module: rgestures)\n//------------------------------------------------------------------------------------\nRLAPI void SetGesturesEnabled(unsigned int flags);      // Enable a set of gestures using flags\nRLAPI bool IsGestureDetected(unsigned int gesture);     // Check if a gesture have been detected\nRLAPI int GetGestureDetected(void);                     // Get latest detected gesture\nRLAPI float GetGestureHoldDuration(void);               // Get gesture hold time in seconds\nRLAPI Vector2 GetGestureDragVector(void);               // Get gesture drag vector\nRLAPI float GetGestureDragAngle(void);                  // Get gesture drag angle\nRLAPI Vector2 GetGesturePinchVector(void);              // Get gesture pinch delta\nRLAPI float GetGesturePinchAngle(void);                 // Get gesture pinch angle\n\n//------------------------------------------------------------------------------------\n// Camera System Functions (Module: rcamera)\n//------------------------------------------------------------------------------------\nRLAPI void UpdateCamera(Camera *camera, int mode);      // Update camera position for selected mode\nRLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom); // Update camera movement/rotation\n\n//------------------------------------------------------------------------------------\n// Basic Shapes Drawing Functions (Module: shapes)\n//------------------------------------------------------------------------------------\n// Set texture and rectangle to be used on shapes drawing\n// NOTE: It can be useful when using basic shapes and one single font,\n// defining a font char white rectangle would allow drawing everything in a single draw call\nRLAPI void SetShapesTexture(Texture2D texture, Rectangle source);       // Set texture and rectangle to be used on shapes drawing\nRLAPI Texture2D GetShapesTexture(void);                                 // Get texture that is used for shapes drawing\nRLAPI Rectangle GetShapesTextureRectangle(void);                        // Get texture source rectangle that is used for shapes drawing\n\n// Basic shapes drawing functions\nRLAPI void DrawPixel(int posX, int posY, Color color);                                                   // Draw a pixel using geometry [Can be slow, use with care]\nRLAPI void DrawPixelV(Vector2 position, Color color);                                                    // Draw a pixel using geometry (Vector version) [Can be slow, use with care]\nRLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color);                // Draw a line\nRLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color);                                     // Draw a line (using gl lines)\nRLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color);                       // Draw a line (using triangles/quads)\nRLAPI void DrawLineStrip(const Vector2 *points, int pointCount, Color color);                            // Draw lines sequence (using gl lines)\nRLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color);                   // Draw line segment cubic-bezier in-out interpolation\nRLAPI void DrawCircle(int centerX, int centerY, float radius, Color color);                              // Draw a color-filled circle\nRLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color);      // Draw a piece of a circle\nRLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline\nRLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer);         // Draw a gradient-filled circle\nRLAPI void DrawCircleV(Vector2 center, float radius, Color color);                                       // Draw a color-filled circle (Vector version)\nRLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color);                         // Draw circle outline\nRLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color);                                  // Draw circle outline (Vector version)\nRLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color);             // Draw ellipse\nRLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color);        // Draw ellipse outline\nRLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring\nRLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color);    // Draw ring outline\nRLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color);                        // Draw a color-filled rectangle\nRLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color);                                  // Draw a color-filled rectangle (Vector version)\nRLAPI void DrawRectangleRec(Rectangle rec, Color color);                                                 // Draw a color-filled rectangle\nRLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color);                 // Draw a color-filled rectangle with pro parameters\nRLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom);   // Draw a vertical-gradient-filled rectangle\nRLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right);   // Draw a horizontal-gradient-filled rectangle\nRLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors\nRLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color);                   // Draw rectangle outline\nRLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color);                            // Draw rectangle outline with extended parameters\nRLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color);              // Draw rectangle with rounded edges\nRLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color);         // Draw rectangle lines with rounded edges\nRLAPI void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline\nRLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color);                                // Draw a color-filled triangle (vertex in counter-clockwise order!)\nRLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color);                           // Draw triangle outline (vertex in counter-clockwise order!)\nRLAPI void DrawTriangleFan(const Vector2 *points, int pointCount, Color color);                          // Draw a triangle fan defined by points (first vertex is the center)\nRLAPI void DrawTriangleStrip(const Vector2 *points, int pointCount, Color color);                        // Draw a triangle strip defined by points\nRLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color);               // Draw a regular polygon (Vector version)\nRLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color);          // Draw a polygon outline of n sides\nRLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters\n\n// Splines drawing functions\nRLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color);                  // Draw spline: Linear, minimum 2 points\nRLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color);                   // Draw spline: B-Spline, minimum 4 points\nRLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color);              // Draw spline: Catmull-Rom, minimum 4 points\nRLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color);         // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]\nRLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color);             // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]\nRLAPI void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color);                    // Draw spline segment: Linear, 2 points\nRLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: B-Spline, 4 points\nRLAPI void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: Catmull-Rom, 4 points\nRLAPI void DrawSplineSegmentBezierQuadratic(Vector2 p1, Vector2 c2, Vector2 p3, float thick, Color color); // Draw spline segment: Quadratic Bezier, 2 points, 1 control point\nRLAPI void DrawSplineSegmentBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float thick, Color color); // Draw spline segment: Cubic Bezier, 2 points, 2 control points\n\n// Spline segment point evaluation functions, for a given t [0.0f .. 1.0f]\nRLAPI Vector2 GetSplinePointLinear(Vector2 startPos, Vector2 endPos, float t);                           // Get (evaluate) spline point: Linear\nRLAPI Vector2 GetSplinePointBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t);              // Get (evaluate) spline point: B-Spline\nRLAPI Vector2 GetSplinePointCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t);         // Get (evaluate) spline point: Catmull-Rom\nRLAPI Vector2 GetSplinePointBezierQuad(Vector2 p1, Vector2 c2, Vector2 p3, float t);                     // Get (evaluate) spline point: Quadratic Bezier\nRLAPI Vector2 GetSplinePointBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float t);        // Get (evaluate) spline point: Cubic Bezier\n\n// Basic shapes collision detection functions\nRLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);                                           // Check collision between two rectangles\nRLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2);        // Check collision between two circles\nRLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec);                         // Check collision between circle and rectangle\nRLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2);               // Check if circle collides with a line created betweeen two points [p1] and [p2]\nRLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec);                                         // Check if point is inside rectangle\nRLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius);                       // Check if point is inside circle\nRLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3);               // Check if point is inside a triangle\nRLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold);                // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]\nRLAPI bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCount);                // Check if point is within a polygon described by array of vertices\nRLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference\nRLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2);                                         // Get collision rectangle for two rectangles collision\n\n//------------------------------------------------------------------------------------\n// Texture Loading and Drawing Functions (Module: textures)\n//------------------------------------------------------------------------------------\n\n// Image loading functions\n// NOTE: These functions do not require GPU access\nRLAPI Image LoadImage(const char *fileName);                                                             // Load image from file into CPU memory (RAM)\nRLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize);       // Load image from RAW file data\nRLAPI Image LoadImageAnim(const char *fileName, int *frames);                                            // Load image sequence from file (frames appended to image.data)\nRLAPI Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames); // Load image sequence from memory buffer\nRLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize);      // Load image from memory buffer, fileType refers to extension: i.e. '.png'\nRLAPI Image LoadImageFromTexture(Texture2D texture);                                                     // Load image from GPU texture data\nRLAPI Image LoadImageFromScreen(void);                                                                   // Load image from screen buffer and (screenshot)\nRLAPI bool IsImageValid(Image image);                                                                    // Check if an image is valid (data and parameters)\nRLAPI void UnloadImage(Image image);                                                                     // Unload image from CPU memory (RAM)\nRLAPI bool ExportImage(Image image, const char *fileName);                                               // Export image data to file, returns true on success\nRLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize);              // Export image to memory buffer\nRLAPI bool ExportImageAsCode(Image image, const char *fileName);                                         // Export image as code file defining an array of bytes, returns true on success\n\n// Image generation functions\nRLAPI Image GenImageColor(int width, int height, Color color);                                           // Generate image: plain color\nRLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end);        // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient\nRLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer);      // Generate image: radial gradient\nRLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer);      // Generate image: square gradient\nRLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2);    // Generate image: checked\nRLAPI Image GenImageWhiteNoise(int width, int height, float factor);                                     // Generate image: white noise\nRLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale);           // Generate image: perlin noise\nRLAPI Image GenImageCellular(int width, int height, int tileSize);                                       // Generate image: cellular algorithm, bigger tileSize means bigger cells\nRLAPI Image GenImageText(int width, int height, const char *text);                                       // Generate image: grayscale image from text data\n\n// Image manipulation functions\nRLAPI Image ImageCopy(Image image);                                                                      // Create an image duplicate (useful for transformations)\nRLAPI Image ImageFromImage(Image image, Rectangle rec);                                                  // Create an image from another image piece\nRLAPI Image ImageFromChannel(Image image, int selectedChannel);                                          // Create an image from a selected channel of another image (GRAYSCALE)\nRLAPI Image ImageText(const char *text, int fontSize, Color color);                                      // Create an image from text (default font)\nRLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint);         // Create an image from text (custom sprite font)\nRLAPI void ImageFormat(Image *image, int newFormat);                                                     // Convert image data to desired format\nRLAPI void ImageToPOT(Image *image, Color fill);                                                         // Convert image to POT (power-of-two)\nRLAPI void ImageCrop(Image *image, Rectangle crop);                                                      // Crop an image to a defined rectangle\nRLAPI void ImageAlphaCrop(Image *image, float threshold);                                                // Crop image depending on alpha value\nRLAPI void ImageAlphaClear(Image *image, Color color, float threshold);                                  // Clear alpha channel to desired color\nRLAPI void ImageAlphaMask(Image *image, Image alphaMask);                                                // Apply alpha mask to image\nRLAPI void ImageAlphaPremultiply(Image *image);                                                          // Premultiply alpha channel\nRLAPI void ImageBlurGaussian(Image *image, int blurSize);                                                // Apply Gaussian blur using a box blur approximation\nRLAPI void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize);                    // Apply custom square convolution kernel to image\nRLAPI void ImageResize(Image *image, int newWidth, int newHeight);                                       // Resize image (Bicubic scaling algorithm)\nRLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight);                                      // Resize image (Nearest-Neighbor scaling algorithm)\nRLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color\nRLAPI void ImageMipmaps(Image *image);                                                                   // Compute all mipmap levels for a provided image\nRLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp);                            // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)\nRLAPI void ImageFlipVertical(Image *image);                                                              // Flip image vertically\nRLAPI void ImageFlipHorizontal(Image *image);                                                            // Flip image horizontally\nRLAPI void ImageRotate(Image *image, int degrees);                                                       // Rotate image by input angle in degrees (-359 to 359)\nRLAPI void ImageRotateCW(Image *image);                                                                  // Rotate image clockwise 90deg\nRLAPI void ImageRotateCCW(Image *image);                                                                 // Rotate image counter-clockwise 90deg\nRLAPI void ImageColorTint(Image *image, Color color);                                                    // Modify image color: tint\nRLAPI void ImageColorInvert(Image *image);                                                               // Modify image color: invert\nRLAPI void ImageColorGrayscale(Image *image);                                                            // Modify image color: grayscale\nRLAPI void ImageColorContrast(Image *image, float contrast);                                             // Modify image color: contrast (-100 to 100)\nRLAPI void ImageColorBrightness(Image *image, int brightness);                                           // Modify image color: brightness (-255 to 255)\nRLAPI void ImageColorReplace(Image *image, Color color, Color replace);                                  // Modify image color: replace color\nRLAPI Color *LoadImageColors(Image image);                                                               // Load color data from image as a Color array (RGBA - 32bit)\nRLAPI Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount);                         // Load colors palette from image as a Color array (RGBA - 32bit)\nRLAPI void UnloadImageColors(Color *colors);                                                             // Unload color data loaded with LoadImageColors()\nRLAPI void UnloadImagePalette(Color *colors);                                                            // Unload colors palette loaded with LoadImagePalette()\nRLAPI Rectangle GetImageAlphaBorder(Image image, float threshold);                                       // Get image alpha border rectangle\nRLAPI Color GetImageColor(Image image, int x, int y);                                                    // Get image pixel color at (x, y) position\n\n// Image drawing functions\n// NOTE: Image software-rendering functions (CPU)\nRLAPI void ImageClearBackground(Image *dst, Color color);                                                // Clear image background with given color\nRLAPI void ImageDrawPixel(Image *dst, int posX, int posY, Color color);                                  // Draw pixel within an image\nRLAPI void ImageDrawPixelV(Image *dst, Vector2 position, Color color);                                   // Draw pixel within an image (Vector version)\nRLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw line within an image\nRLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color);                          // Draw line within an image (Vector version)\nRLAPI void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color color);              // Draw a line defining thickness within an image\nRLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color);               // Draw a filled circle within an image\nRLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color);                        // Draw a filled circle within an image (Vector version)\nRLAPI void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color);          // Draw circle outline within an image\nRLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color);                   // Draw circle outline within an image (Vector version)\nRLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color);       // Draw rectangle within an image\nRLAPI void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color);                 // Draw rectangle within an image (Vector version)\nRLAPI void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color);                                // Draw rectangle within an image\nRLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color);                   // Draw rectangle lines within an image\nRLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color);               // Draw triangle within an image\nRLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image\nRLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color);          // Draw triangle outline within an image\nRLAPI void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color);               // Draw a triangle fan defined by points within an image (first vertex is the center)\nRLAPI void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color);             // Draw a triangle strip defined by points within an image\nRLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint);             // Draw a source image within a destination image (tint applied to source)\nRLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color);   // Draw text (using default font) within an image (destination)\nRLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination)\n\n// Texture loading functions\n// NOTE: These functions require GPU access\nRLAPI Texture2D LoadTexture(const char *fileName);                                                       // Load texture from file into GPU memory (VRAM)\nRLAPI Texture2D LoadTextureFromImage(Image image);                                                       // Load texture from image data\nRLAPI TextureCubemap LoadTextureCubemap(Image image, int layout);                                        // Load cubemap from image, multiple image cubemap layouts supported\nRLAPI RenderTexture2D LoadRenderTexture(int width, int height);                                          // Load texture for rendering (framebuffer)\nRLAPI bool IsTextureValid(Texture2D texture);                                                            // Check if a texture is valid (loaded in GPU)\nRLAPI void UnloadTexture(Texture2D texture);                                                             // Unload texture from GPU memory (VRAM)\nRLAPI bool IsRenderTextureValid(RenderTexture2D target);                                                 // Check if a render texture is valid (loaded in GPU)\nRLAPI void UnloadRenderTexture(RenderTexture2D target);                                                  // Unload render texture from GPU memory (VRAM)\nRLAPI void UpdateTexture(Texture2D texture, const void *pixels);                                         // Update GPU texture with new data\nRLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels);                       // Update GPU texture rectangle with new data\n\n// Texture configuration functions\nRLAPI void GenTextureMipmaps(Texture2D *texture);                                                        // Generate GPU mipmaps for a texture\nRLAPI void SetTextureFilter(Texture2D texture, int filter);                                              // Set texture scaling filter mode\nRLAPI void SetTextureWrap(Texture2D texture, int wrap);                                                  // Set texture wrapping mode\n\n// Texture drawing functions\nRLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint);                               // Draw a Texture2D\nRLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint);                                // Draw a Texture2D with position defined as Vector2\nRLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint);  // Draw a Texture2D with extended parameters\nRLAPI void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint);            // Draw a part of a texture defined by a rectangle\nRLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters\nRLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely\n\n// Color/pixel related functions\nRLAPI bool ColorIsEqual(Color col1, Color col2);                            // Check if two colors are equal\nRLAPI Color Fade(Color color, float alpha);                                 // Get color with alpha applied, alpha goes from 0.0f to 1.0f\nRLAPI int ColorToInt(Color color);                                          // Get hexadecimal value for a Color (0xRRGGBBAA)\nRLAPI Vector4 ColorNormalize(Color color);                                  // Get Color normalized as float [0..1]\nRLAPI Color ColorFromNormalized(Vector4 normalized);                        // Get Color from normalized values [0..1]\nRLAPI Vector3 ColorToHSV(Color color);                                      // Get HSV values for a Color, hue [0..360], saturation/value [0..1]\nRLAPI Color ColorFromHSV(float hue, float saturation, float value);         // Get a Color from HSV values, hue [0..360], saturation/value [0..1]\nRLAPI Color ColorTint(Color color, Color tint);                             // Get color multiplied with another color\nRLAPI Color ColorBrightness(Color color, float factor);                     // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f\nRLAPI Color ColorContrast(Color color, float contrast);                     // Get color with contrast correction, contrast values between -1.0f and 1.0f\nRLAPI Color ColorAlpha(Color color, float alpha);                           // Get color with alpha applied, alpha goes from 0.0f to 1.0f\nRLAPI Color ColorAlphaBlend(Color dst, Color src, Color tint);              // Get src alpha-blended into dst color with tint\nRLAPI Color ColorLerp(Color color1, Color color2, float factor);            // Get color lerp interpolation between two colors, factor [0.0f..1.0f]\nRLAPI Color GetColor(unsigned int hexValue);                                // Get Color structure from hexadecimal value\nRLAPI Color GetPixelColor(void *srcPtr, int format);                        // Get Color from a source pixel pointer of certain format\nRLAPI void SetPixelColor(void *dstPtr, Color color, int format);            // Set color formatted into destination pixel pointer\nRLAPI int GetPixelDataSize(int width, int height, int format);              // Get pixel data size in bytes for certain format\n\n//------------------------------------------------------------------------------------\n// Font Loading and Text Drawing Functions (Module: text)\n//------------------------------------------------------------------------------------\n\n// Font loading/unloading functions\nRLAPI Font GetFontDefault(void);                                                            // Get the default Font\nRLAPI Font LoadFont(const char *fileName);                                                  // Load font from file into GPU memory (VRAM)\nRLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height\nRLAPI Font LoadFontFromImage(Image image, Color key, int firstChar);                        // Load font from Image (XNA style)\nRLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'\nRLAPI bool IsFontValid(Font font);                                                          // Check if a font is valid (font data loaded, WARNING: GPU texture not checked)\nRLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use\nRLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info\nRLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount);                               // Unload font chars info data (RAM)\nRLAPI void UnloadFont(Font font);                                                           // Unload font from GPU memory (VRAM)\nRLAPI bool ExportFontAsCode(Font font, const char *fileName);                               // Export font as code file, returns true on success\n\n// Text drawing functions\nRLAPI void DrawFPS(int posX, int posY);                                                     // Draw current FPS\nRLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color);       // Draw text (using default font)\nRLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters\nRLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation)\nRLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint)\nRLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint)\n\n// Text font info functions\nRLAPI void SetTextLineSpacing(int spacing);                                                 // Set vertical line spacing when drawing with line-breaks\nRLAPI int MeasureText(const char *text, int fontSize);                                      // Measure string width for default font\nRLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing);    // Measure string size for Font\nRLAPI int GetGlyphIndex(Font font, int codepoint);                                          // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found\nRLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint);                                     // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found\nRLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint);                                 // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found\n\n// Text codepoints management functions (unicode characters)\nRLAPI char *LoadUTF8(const int *codepoints, int length);                // Load UTF-8 text encoded from codepoints array\nRLAPI void UnloadUTF8(char *text);                                      // Unload UTF-8 text encoded from codepoints array\nRLAPI int *LoadCodepoints(const char *text, int *count);                // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter\nRLAPI void UnloadCodepoints(int *codepoints);                           // Unload codepoints data from memory\nRLAPI int GetCodepointCount(const char *text);                          // Get total number of codepoints in a UTF-8 encoded string\nRLAPI int GetCodepoint(const char *text, int *codepointSize);           // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure\nRLAPI int GetCodepointNext(const char *text, int *codepointSize);       // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure\nRLAPI int GetCodepointPrevious(const char *text, int *codepointSize);   // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure\nRLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size);        // Encode one codepoint into UTF-8 byte array (array length returned as parameter)\n\n// Text strings management functions (no UTF-8 strings, only byte chars)\n// NOTE: Some strings allocate memory internally for returned strings, just be careful!\nRLAPI int TextCopy(char *dst, const char *src);                                             // Copy one string to another, returns bytes copied\nRLAPI bool TextIsEqual(const char *text1, const char *text2);                               // Check if two text string are equal\nRLAPI unsigned int TextLength(const char *text);                                            // Get text length, checks for '\\0' ending\nRLAPI const char *TextFormat(const char *text, ...);                                        // Text formatting with variables (sprintf() style)\nRLAPI const char *TextSubtext(const char *text, int position, int length);                  // Get a piece of a text string\nRLAPI char *TextReplace(const char *text, const char *replace, const char *by);             // Replace text string (WARNING: memory must be freed!)\nRLAPI char *TextInsert(const char *text, const char *insert, int position);                 // Insert text in a position (WARNING: memory must be freed!)\nRLAPI const char *TextJoin(const char **textList, int count, const char *delimiter);        // Join text strings with delimiter\nRLAPI const char **TextSplit(const char *text, char delimiter, int *count);                 // Split text into multiple strings\nRLAPI void TextAppend(char *text, const char *append, int *position);                       // Append text at specific position and move cursor!\nRLAPI int TextFindIndex(const char *text, const char *find);                                // Find first text occurrence within a string\nRLAPI const char *TextToUpper(const char *text);                      // Get upper case version of provided string\nRLAPI const char *TextToLower(const char *text);                      // Get lower case version of provided string\nRLAPI const char *TextToPascal(const char *text);                     // Get Pascal case notation version of provided string\nRLAPI const char *TextToSnake(const char *text);                      // Get Snake case notation version of provided string\nRLAPI const char *TextToCamel(const char *text);                      // Get Camel case notation version of provided string\n\nRLAPI int TextToInteger(const char *text);                            // Get integer value from text (negative values not supported)\nRLAPI float TextToFloat(const char *text);                            // Get float value from text (negative values not supported)\n\n//------------------------------------------------------------------------------------\n// Basic 3d Shapes Drawing Functions (Module: models)\n//------------------------------------------------------------------------------------\n\n// Basic geometric 3D shapes drawing functions\nRLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color);                                    // Draw a line in 3D world space\nRLAPI void DrawPoint3D(Vector3 position, Color color);                                                   // Draw a point in 3D space, actually a small line\nRLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space\nRLAPI void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color);                              // Draw a color-filled triangle (vertex in counter-clockwise order!)\nRLAPI void DrawTriangleStrip3D(const Vector3 *points, int pointCount, Color color);                      // Draw a triangle strip defined by points\nRLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color);             // Draw cube\nRLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color);                                       // Draw cube (Vector version)\nRLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color);        // Draw cube wires\nRLAPI void DrawCubeWiresV(Vector3 position, Vector3 size, Color color);                                  // Draw cube wires (Vector version)\nRLAPI void DrawSphere(Vector3 centerPos, float radius, Color color);                                     // Draw sphere\nRLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color);            // Draw sphere with extended parameters\nRLAPI void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color);         // Draw sphere wires\nRLAPI void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone\nRLAPI void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder with base at startPos and top at endPos\nRLAPI void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires\nRLAPI void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder wires with base at startPos and top at endPos\nRLAPI void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw a capsule with the center of its sphere caps at startPos and endPos\nRLAPI void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw capsule wireframe with the center of its sphere caps at startPos and endPos\nRLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color);                                      // Draw a plane XZ\nRLAPI void DrawRay(Ray ray, Color color);                                                                // Draw a ray line\nRLAPI void DrawGrid(int slices, float spacing);                                                          // Draw a grid (centered at (0, 0, 0))\n\n//------------------------------------------------------------------------------------\n// Model 3d Loading and Drawing Functions (Module: models)\n//------------------------------------------------------------------------------------\n\n// Model management functions\nRLAPI Model LoadModel(const char *fileName);                                                // Load model from files (meshes and materials)\nRLAPI Model LoadModelFromMesh(Mesh mesh);                                                   // Load model from generated mesh (default material)\nRLAPI bool IsModelValid(Model model);                                                       // Check if a model is valid (loaded in GPU, VAO/VBOs)\nRLAPI void UnloadModel(Model model);                                                        // Unload model (including meshes) from memory (RAM and/or VRAM)\nRLAPI BoundingBox GetModelBoundingBox(Model model);                                         // Compute model bounding box limits (considers all meshes)\n\n// Model drawing functions\nRLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint);               // Draw a model (with texture if set)\nRLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters\nRLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint);          // Draw a model wires (with texture if set)\nRLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters\nRLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points\nRLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters\nRLAPI void DrawBoundingBox(BoundingBox box, Color color);                                   // Draw bounding box (wires)\nRLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint);   // Draw a billboard texture\nRLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source\nRLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation\n\n// Mesh management functions\nRLAPI void UploadMesh(Mesh *mesh, bool dynamic);                                            // Upload mesh vertex data in GPU and provide VAO/VBO ids\nRLAPI void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset); // Update mesh vertex data in GPU for a specific buffer index\nRLAPI void UnloadMesh(Mesh mesh);                                                           // Unload mesh data from CPU and GPU\nRLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform);                        // Draw a 3d mesh with material and transform\nRLAPI void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms\nRLAPI BoundingBox GetMeshBoundingBox(Mesh mesh);                                            // Compute mesh bounding box limits\nRLAPI void GenMeshTangents(Mesh *mesh);                                                     // Compute mesh tangents\nRLAPI bool ExportMesh(Mesh mesh, const char *fileName);                                     // Export mesh data to file, returns true on success\nRLAPI bool ExportMeshAsCode(Mesh mesh, const char *fileName);                               // Export mesh as code file (.h) defining multiple arrays of vertex attributes\n\n// Mesh generation functions\nRLAPI Mesh GenMeshPoly(int sides, float radius);                                            // Generate polygonal mesh\nRLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ);                     // Generate plane mesh (with subdivisions)\nRLAPI Mesh GenMeshCube(float width, float height, float length);                            // Generate cuboid mesh\nRLAPI Mesh GenMeshSphere(float radius, int rings, int slices);                              // Generate sphere mesh (standard sphere)\nRLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices);                          // Generate half-sphere mesh (no bottom cap)\nRLAPI Mesh GenMeshCylinder(float radius, float height, int slices);                         // Generate cylinder mesh\nRLAPI Mesh GenMeshCone(float radius, float height, int slices);                             // Generate cone/pyramid mesh\nRLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides);                   // Generate torus mesh\nRLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides);                    // Generate trefoil knot mesh\nRLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size);                                 // Generate heightmap mesh from image data\nRLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize);                               // Generate cubes-based map mesh from image data\n\n// Material loading/unloading functions\nRLAPI Material *LoadMaterials(const char *fileName, int *materialCount);                    // Load materials from model file\nRLAPI Material LoadMaterialDefault(void);                                                   // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)\nRLAPI bool IsMaterialValid(Material material);                                              // Check if a material is valid (shader assigned, map textures loaded in GPU)\nRLAPI void UnloadMaterial(Material material);                                               // Unload material from GPU memory (VRAM)\nRLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture);          // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)\nRLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId);                  // Set material for a mesh\n\n// Model animations loading/unloading functions\nRLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount);            // Load model animations from file\nRLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame);               // Update model animation pose (CPU)\nRLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame);          // Update model animation mesh bone matrices (GPU skinning)\nRLAPI void UnloadModelAnimation(ModelAnimation anim);                                       // Unload animation data\nRLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount);                // Unload animation array data\nRLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim);                         // Check model animation skeleton match\n\n// Collision detection functions\nRLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2);   // Check collision between two spheres\nRLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2);                                 // Check collision between two bounding boxes\nRLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius);                  // Check collision between box and sphere\nRLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius);                    // Get collision info between ray and sphere\nRLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box);                                    // Get collision info between ray and box\nRLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform);                       // Get collision info between ray and mesh\nRLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3);            // Get collision info between ray and triangle\nRLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4);    // Get collision info between ray and quad\n\n//------------------------------------------------------------------------------------\n// Audio Loading and Playing Functions (Module: audio)\n//------------------------------------------------------------------------------------\ntypedef void (*AudioCallback)(void *bufferData, unsigned int frames);\n\n// Audio device management functions\nRLAPI void InitAudioDevice(void);                                     // Initialize audio device and context\nRLAPI void CloseAudioDevice(void);                                    // Close the audio device and context\nRLAPI bool IsAudioDeviceReady(void);                                  // Check if audio device has been initialized successfully\nRLAPI void SetMasterVolume(float volume);                             // Set master volume (listener)\nRLAPI float GetMasterVolume(void);                                    // Get master volume (listener)\n\n// Wave/Sound loading/unloading functions\nRLAPI Wave LoadWave(const char *fileName);                            // Load wave data from file\nRLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav'\nRLAPI bool IsWaveValid(Wave wave);                                    // Checks if wave data is valid (data loaded and parameters)\nRLAPI Sound LoadSound(const char *fileName);                          // Load sound from file\nRLAPI Sound LoadSoundFromWave(Wave wave);                             // Load sound from wave data\nRLAPI Sound LoadSoundAlias(Sound source);                             // Create a new sound that shares the same sample data as the source sound, does not own the sound data\nRLAPI bool IsSoundValid(Sound sound);                                 // Checks if a sound is valid (data loaded and buffers initialized)\nRLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data\nRLAPI void UnloadWave(Wave wave);                                     // Unload wave data\nRLAPI void UnloadSound(Sound sound);                                  // Unload sound\nRLAPI void UnloadSoundAlias(Sound alias);                             // Unload a sound alias (does not deallocate sample data)\nRLAPI bool ExportWave(Wave wave, const char *fileName);               // Export wave data to file, returns true on success\nRLAPI bool ExportWaveAsCode(Wave wave, const char *fileName);         // Export wave sample data to code (.h), returns true on success\n\n// Wave/Sound management functions\nRLAPI void PlaySound(Sound sound);                                    // Play a sound\nRLAPI void StopSound(Sound sound);                                    // Stop playing a sound\nRLAPI void PauseSound(Sound sound);                                   // Pause a sound\nRLAPI void ResumeSound(Sound sound);                                  // Resume a paused sound\nRLAPI bool IsSoundPlaying(Sound sound);                               // Check if a sound is currently playing\nRLAPI void SetSoundVolume(Sound sound, float volume);                 // Set volume for a sound (1.0 is max level)\nRLAPI void SetSoundPitch(Sound sound, float pitch);                   // Set pitch for a sound (1.0 is base level)\nRLAPI void SetSoundPan(Sound sound, float pan);                       // Set pan for a sound (0.5 is center)\nRLAPI Wave WaveCopy(Wave wave);                                       // Copy a wave to a new wave\nRLAPI void WaveCrop(Wave *wave, int initFrame, int finalFrame);       // Crop a wave to defined frames range\nRLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format\nRLAPI float *LoadWaveSamples(Wave wave);                              // Load samples data from wave as a 32bit float data array\nRLAPI void UnloadWaveSamples(float *samples);                         // Unload samples data loaded with LoadWaveSamples()\n\n// Music management functions\nRLAPI Music LoadMusicStream(const char *fileName);                    // Load music stream from file\nRLAPI Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize); // Load music stream from data\nRLAPI bool IsMusicValid(Music music);                                 // Checks if a music stream is valid (context and buffers initialized)\nRLAPI void UnloadMusicStream(Music music);                            // Unload music stream\nRLAPI void PlayMusicStream(Music music);                              // Start music playing\nRLAPI bool IsMusicStreamPlaying(Music music);                         // Check if music is playing\nRLAPI void UpdateMusicStream(Music music);                            // Updates buffers for music streaming\nRLAPI void StopMusicStream(Music music);                              // Stop music playing\nRLAPI void PauseMusicStream(Music music);                             // Pause music playing\nRLAPI void ResumeMusicStream(Music music);                            // Resume playing paused music\nRLAPI void SeekMusicStream(Music music, float position);              // Seek music to a position (in seconds)\nRLAPI void SetMusicVolume(Music music, float volume);                 // Set volume for music (1.0 is max level)\nRLAPI void SetMusicPitch(Music music, float pitch);                   // Set pitch for a music (1.0 is base level)\nRLAPI void SetMusicPan(Music music, float pan);                       // Set pan for a music (0.5 is center)\nRLAPI float GetMusicTimeLength(Music music);                          // Get music time length (in seconds)\nRLAPI float GetMusicTimePlayed(Music music);                          // Get current music time played (in seconds)\n\n// AudioStream management functions\nRLAPI AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Load audio stream (to stream raw audio pcm data)\nRLAPI bool IsAudioStreamValid(AudioStream stream);                    // Checks if an audio stream is valid (buffers initialized)\nRLAPI void UnloadAudioStream(AudioStream stream);                     // Unload audio stream and free memory\nRLAPI void UpdateAudioStream(AudioStream stream, const void *data, int frameCount); // Update audio stream buffers with data\nRLAPI bool IsAudioStreamProcessed(AudioStream stream);                // Check if any audio stream buffers requires refill\nRLAPI void PlayAudioStream(AudioStream stream);                       // Play audio stream\nRLAPI void PauseAudioStream(AudioStream stream);                      // Pause audio stream\nRLAPI void ResumeAudioStream(AudioStream stream);                     // Resume audio stream\nRLAPI bool IsAudioStreamPlaying(AudioStream stream);                  // Check if audio stream is playing\nRLAPI void StopAudioStream(AudioStream stream);                       // Stop audio stream\nRLAPI void SetAudioStreamVolume(AudioStream stream, float volume);    // Set volume for audio stream (1.0 is max level)\nRLAPI void SetAudioStreamPitch(AudioStream stream, float pitch);      // Set pitch for audio stream (1.0 is base level)\nRLAPI void SetAudioStreamPan(AudioStream stream, float pan);          // Set pan for audio stream (0.5 is centered)\nRLAPI void SetAudioStreamBufferSizeDefault(int size);                 // Default size for new audio streams\nRLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data\n\nRLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as 'float'\nRLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream\n\nRLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as 'float'\nRLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline\n\n#if defined(__cplusplus)\n}\n#endif\n\n#endif // RAYLIB_H\n"
  },
  {
    "path": "renderers/raylib/raymath.h",
    "content": "/**********************************************************************************************\n*\n*   raymath v2.0 - Math functions to work with Vector2, Vector3, Matrix and Quaternions\n*\n*   CONVENTIONS:\n*     - Matrix structure is defined as row-major (memory layout) but parameters naming AND all\n*       math operations performed by the library consider the structure as it was column-major\n*       It is like transposed versions of the matrices are used for all the maths\n*       It benefits some functions making them cache-friendly and also avoids matrix\n*       transpositions sometimes required by OpenGL\n*       Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3]\n*     - Functions are always self-contained, no function use another raymath function inside,\n*       required code is directly re-implemented inside\n*     - Functions input parameters are always received by value (2 unavoidable exceptions)\n*     - Functions use always a \"result\" variable for return (except C++ operators)\n*     - Functions are always defined inline\n*     - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience)\n*     - No compound literals used to make sure libray is compatible with C++\n*\n*   CONFIGURATION:\n*       #define RAYMATH_IMPLEMENTATION\n*           Generates the implementation of the library into the included file.\n*           If not defined, the library is in header only mode and can be included in other headers\n*           or source files without problems. But only ONE file should hold the implementation.\n*\n*       #define RAYMATH_STATIC_INLINE\n*           Define static inline functions code, so #include header suffices for use.\n*           This may use up lots of memory.\n*\n*       #define RAYMATH_DISABLE_CPP_OPERATORS\n*           Disables C++ operator overloads for raymath types.\n*\n*   LICENSE: zlib/libpng\n*\n*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)\n*\n*   This software is provided \"as-is\", without any express or implied warranty. In no event\n*   will the authors be held liable for any damages arising from the use of this software.\n*\n*   Permission is granted to anyone to use this software for any purpose, including commercial\n*   applications, and to alter it and redistribute it freely, subject to the following restrictions:\n*\n*     1. The origin of this software must not be misrepresented; you must not claim that you\n*     wrote the original software. If you use this software in a product, an acknowledgment\n*     in the product documentation would be appreciated but is not required.\n*\n*     2. Altered source versions must be plainly marked as such, and must not be misrepresented\n*     as being the original software.\n*\n*     3. This notice may not be removed or altered from any source distribution.\n*\n**********************************************************************************************/\n\n#ifndef RAYMATH_H\n#define RAYMATH_H\n\n#if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_STATIC_INLINE)\n    #error \"Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_STATIC_INLINE is contradictory\"\n#endif\n\n// Function specifiers definition\n#if defined(RAYMATH_IMPLEMENTATION)\n    #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)\n        #define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll)\n    #elif defined(BUILD_LIBTYPE_SHARED)\n        #define RMAPI __attribute__((visibility(\"default\"))) // We are building raylib as a Unix shared library (.so/.dylib)\n    #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED)\n        #define RMAPI __declspec(dllimport)         // We are using raylib as a Win32 shared library (.dll)\n    #else\n        #define RMAPI extern inline // Provide external definition\n    #endif\n#elif defined(RAYMATH_STATIC_INLINE)\n    #define RMAPI static inline // Functions may be inlined, no external out-of-line definition\n#else\n    #if defined(__TINYC__)\n        #define RMAPI static inline // plain inline not supported by tinycc (See issue #435)\n    #else\n        #define RMAPI inline        // Functions may be inlined or external definition used\n    #endif\n#endif\n\n\n//----------------------------------------------------------------------------------\n// Defines and Macros\n//----------------------------------------------------------------------------------\n#ifndef PI\n    #define PI 3.14159265358979323846f\n#endif\n\n#ifndef EPSILON\n    #define EPSILON 0.000001f\n#endif\n\n#ifndef DEG2RAD\n    #define DEG2RAD (PI/180.0f)\n#endif\n\n#ifndef RAD2DEG\n    #define RAD2DEG (180.0f/PI)\n#endif\n\n// Get float vector for Matrix\n#ifndef MatrixToFloat\n    #define MatrixToFloat(mat) (MatrixToFloatV(mat).v)\n#endif\n\n// Get float vector for Vector3\n#ifndef Vector3ToFloat\n    #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v)\n#endif\n\n//----------------------------------------------------------------------------------\n// Types and Structures Definition\n//----------------------------------------------------------------------------------\n#if !defined(RL_VECTOR2_TYPE)\n// Vector2 type\ntypedef struct Vector2 {\n    float x;\n    float y;\n} Vector2;\n#define RL_VECTOR2_TYPE\n#endif\n\n#if !defined(RL_VECTOR3_TYPE)\n// Vector3 type\ntypedef struct Vector3 {\n    float x;\n    float y;\n    float z;\n} Vector3;\n#define RL_VECTOR3_TYPE\n#endif\n\n#if !defined(RL_VECTOR4_TYPE)\n// Vector4 type\ntypedef struct Vector4 {\n    float x;\n    float y;\n    float z;\n    float w;\n} Vector4;\n#define RL_VECTOR4_TYPE\n#endif\n\n#if !defined(RL_QUATERNION_TYPE)\n// Quaternion type\ntypedef Vector4 Quaternion;\n#define RL_QUATERNION_TYPE\n#endif\n\n#if !defined(RL_MATRIX_TYPE)\n// Matrix type (OpenGL style 4x4 - right handed, column major)\ntypedef struct Matrix {\n    float m0, m4, m8, m12;      // Matrix first row (4 components)\n    float m1, m5, m9, m13;      // Matrix second row (4 components)\n    float m2, m6, m10, m14;     // Matrix third row (4 components)\n    float m3, m7, m11, m15;     // Matrix fourth row (4 components)\n} Matrix;\n#define RL_MATRIX_TYPE\n#endif\n\n// NOTE: Helper types to be used instead of array return types for *ToFloat functions\ntypedef struct float3 {\n    float v[3];\n} float3;\n\ntypedef struct float16 {\n    float v[16];\n} float16;\n\n#include <math.h>       // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf()\n\n//----------------------------------------------------------------------------------\n// Module Functions Definition - Utils math\n//----------------------------------------------------------------------------------\n\n// Clamp float value\nRMAPI float Clamp(float value, float min, float max)\n{\n    float result = (value < min)? min : value;\n\n    if (result > max) result = max;\n\n    return result;\n}\n\n// Calculate linear interpolation between two floats\nRMAPI float Lerp(float start, float end, float amount)\n{\n    float result = start + amount*(end - start);\n\n    return result;\n}\n\n// Normalize input value within input range\nRMAPI float Normalize(float value, float start, float end)\n{\n    float result = (value - start)/(end - start);\n\n    return result;\n}\n\n// Remap input value within input range to output range\nRMAPI float Remap(float value, float inputStart, float inputEnd, float outputStart, float outputEnd)\n{\n    float result = (value - inputStart)/(inputEnd - inputStart)*(outputEnd - outputStart) + outputStart;\n\n    return result;\n}\n\n// Wrap input value from min to max\nRMAPI float Wrap(float value, float min, float max)\n{\n    float result = value - (max - min)*floorf((value - min)/(max - min));\n\n    return result;\n}\n\n// Check whether two given floats are almost equal\nRMAPI int FloatEquals(float x, float y)\n{\n#if !defined(EPSILON)\n    #define EPSILON 0.000001f\n#endif\n\n    int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y))));\n\n    return result;\n}\n\n//----------------------------------------------------------------------------------\n// Module Functions Definition - Vector2 math\n//----------------------------------------------------------------------------------\n\n// Vector with components value 0.0f\nRMAPI Vector2 Vector2Zero(void)\n{\n    Vector2 result = { 0.0f, 0.0f };\n\n    return result;\n}\n\n// Vector with components value 1.0f\nRMAPI Vector2 Vector2One(void)\n{\n    Vector2 result = { 1.0f, 1.0f };\n\n    return result;\n}\n\n// Add two vectors (v1 + v2)\nRMAPI Vector2 Vector2Add(Vector2 v1, Vector2 v2)\n{\n    Vector2 result = { v1.x + v2.x, v1.y + v2.y };\n\n    return result;\n}\n\n// Add vector and float value\nRMAPI Vector2 Vector2AddValue(Vector2 v, float add)\n{\n    Vector2 result = { v.x + add, v.y + add };\n\n    return result;\n}\n\n// Subtract two vectors (v1 - v2)\nRMAPI Vector2 Vector2Subtract(Vector2 v1, Vector2 v2)\n{\n    Vector2 result = { v1.x - v2.x, v1.y - v2.y };\n\n    return result;\n}\n\n// Subtract vector by float value\nRMAPI Vector2 Vector2SubtractValue(Vector2 v, float sub)\n{\n    Vector2 result = { v.x - sub, v.y - sub };\n\n    return result;\n}\n\n// Calculate vector length\nRMAPI float Vector2Length(Vector2 v)\n{\n    float result = sqrtf((v.x*v.x) + (v.y*v.y));\n\n    return result;\n}\n\n// Calculate vector square length\nRMAPI float Vector2LengthSqr(Vector2 v)\n{\n    float result = (v.x*v.x) + (v.y*v.y);\n\n    return result;\n}\n\n// Calculate two vectors dot product\nRMAPI float Vector2DotProduct(Vector2 v1, Vector2 v2)\n{\n    float result = (v1.x*v2.x + v1.y*v2.y);\n\n    return result;\n}\n\n// Calculate distance between two vectors\nRMAPI float Vector2Distance(Vector2 v1, Vector2 v2)\n{\n    float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y));\n\n    return result;\n}\n\n// Calculate square distance between two vectors\nRMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2)\n{\n    float result = ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y));\n\n    return result;\n}\n\n// Calculate angle between two vectors\n// NOTE: Angle is calculated from origin point (0, 0)\nRMAPI float Vector2Angle(Vector2 v1, Vector2 v2)\n{\n    float result = 0.0f;\n\n    float dot = v1.x*v2.x + v1.y*v2.y;\n    float det = v1.x*v2.y - v1.y*v2.x;\n\n    result = atan2f(det, dot);\n\n    return result;\n}\n\n// Calculate angle defined by a two vectors line\n// NOTE: Parameters need to be normalized\n// Current implementation should be aligned with glm::angle\nRMAPI float Vector2LineAngle(Vector2 start, Vector2 end)\n{\n    float result = 0.0f;\n\n    // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior\n    result = -atan2f(end.y - start.y, end.x - start.x);\n\n    return result;\n}\n\n// Scale vector (multiply by value)\nRMAPI Vector2 Vector2Scale(Vector2 v, float scale)\n{\n    Vector2 result = { v.x*scale, v.y*scale };\n\n    return result;\n}\n\n// Multiply vector by vector\nRMAPI Vector2 Vector2Multiply(Vector2 v1, Vector2 v2)\n{\n    Vector2 result = { v1.x*v2.x, v1.y*v2.y };\n\n    return result;\n}\n\n// Negate vector\nRMAPI Vector2 Vector2Negate(Vector2 v)\n{\n    Vector2 result = { -v.x, -v.y };\n\n    return result;\n}\n\n// Divide vector by vector\nRMAPI Vector2 Vector2Divide(Vector2 v1, Vector2 v2)\n{\n    Vector2 result = { v1.x/v2.x, v1.y/v2.y };\n\n    return result;\n}\n\n// Normalize provided vector\nRMAPI Vector2 Vector2Normalize(Vector2 v)\n{\n    Vector2 result = { 0 };\n    float length = sqrtf((v.x*v.x) + (v.y*v.y));\n\n    if (length > 0)\n    {\n        float ilength = 1.0f/length;\n        result.x = v.x*ilength;\n        result.y = v.y*ilength;\n    }\n\n    return result;\n}\n\n// Transforms a Vector2 by a given Matrix\nRMAPI Vector2 Vector2Transform(Vector2 v, Matrix mat)\n{\n    Vector2 result = { 0 };\n\n    float x = v.x;\n    float y = v.y;\n    float z = 0;\n\n    result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12;\n    result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13;\n\n    return result;\n}\n\n// Calculate linear interpolation between two vectors\nRMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount)\n{\n    Vector2 result = { 0 };\n\n    result.x = v1.x + amount*(v2.x - v1.x);\n    result.y = v1.y + amount*(v2.y - v1.y);\n\n    return result;\n}\n\n// Calculate reflected vector to normal\nRMAPI Vector2 Vector2Reflect(Vector2 v, Vector2 normal)\n{\n    Vector2 result = { 0 };\n\n    float dotProduct = (v.x*normal.x + v.y*normal.y); // Dot product\n\n    result.x = v.x - (2.0f*normal.x)*dotProduct;\n    result.y = v.y - (2.0f*normal.y)*dotProduct;\n\n    return result;\n}\n\n// Get min value for each pair of components\nRMAPI Vector2 Vector2Min(Vector2 v1, Vector2 v2)\n{\n    Vector2 result = { 0 };\n\n    result.x = fminf(v1.x, v2.x);\n    result.y = fminf(v1.y, v2.y);\n\n    return result;\n}\n\n// Get max value for each pair of components\nRMAPI Vector2 Vector2Max(Vector2 v1, Vector2 v2)\n{\n    Vector2 result = { 0 };\n\n    result.x = fmaxf(v1.x, v2.x);\n    result.y = fmaxf(v1.y, v2.y);\n\n    return result;\n}\n\n// Rotate vector by angle\nRMAPI Vector2 Vector2Rotate(Vector2 v, float angle)\n{\n    Vector2 result = { 0 };\n\n    float cosres = cosf(angle);\n    float sinres = sinf(angle);\n\n    result.x = v.x*cosres - v.y*sinres;\n    result.y = v.x*sinres + v.y*cosres;\n\n    return result;\n}\n\n// Move Vector towards target\nRMAPI Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance)\n{\n    Vector2 result = { 0 };\n\n    float dx = target.x - v.x;\n    float dy = target.y - v.y;\n    float value = (dx*dx) + (dy*dy);\n\n    if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target;\n\n    float dist = sqrtf(value);\n\n    result.x = v.x + dx/dist*maxDistance;\n    result.y = v.y + dy/dist*maxDistance;\n\n    return result;\n}\n\n// Invert the given vector\nRMAPI Vector2 Vector2Invert(Vector2 v)\n{\n    Vector2 result = { 1.0f/v.x, 1.0f/v.y };\n\n    return result;\n}\n\n// Clamp the components of the vector between\n// min and max values specified by the given vectors\nRMAPI Vector2 Vector2Clamp(Vector2 v, Vector2 min, Vector2 max)\n{\n    Vector2 result = { 0 };\n\n    result.x = fminf(max.x, fmaxf(min.x, v.x));\n    result.y = fminf(max.y, fmaxf(min.y, v.y));\n\n    return result;\n}\n\n// Clamp the magnitude of the vector between two min and max values\nRMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max)\n{\n    Vector2 result = v;\n\n    float length = (v.x*v.x) + (v.y*v.y);\n    if (length > 0.0f)\n    {\n        length = sqrtf(length);\n\n        float scale = 1;    // By default, 1 as the neutral element.\n        if (length < min)\n        {\n            scale = min/length;\n        }\n        else if (length > max)\n        {\n            scale = max/length;\n        }\n\n        result.x = v.x*scale;\n        result.y = v.y*scale;\n    }\n\n    return result;\n}\n\n// Check whether two given vectors are almost equal\nRMAPI int Vector2Equals(Vector2 p, Vector2 q)\n{\n#if !defined(EPSILON)\n    #define EPSILON 0.000001f\n#endif\n\n    int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&\n                  ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y)))));\n\n    return result;\n}\n\n// Compute the direction of a refracted ray\n// v: normalized direction of the incoming ray\n// n: normalized normal vector of the interface of two optical media\n// r: ratio of the refractive index of the medium from where the ray comes\n//    to the refractive index of the medium on the other side of the surface\nRMAPI Vector2 Vector2Refract(Vector2 v, Vector2 n, float r)\n{\n    Vector2 result = { 0 };\n\n    float dot = v.x*n.x + v.y*n.y;\n    float d = 1.0f - r*r*(1.0f - dot*dot);\n\n    if (d >= 0.0f)\n    {\n        d = sqrtf(d);\n        v.x = r*v.x - (r*dot + d)*n.x;\n        v.y = r*v.y - (r*dot + d)*n.y;\n\n        result = v;\n    }\n\n    return result;\n}\n\n\n//----------------------------------------------------------------------------------\n// Module Functions Definition - Vector3 math\n//----------------------------------------------------------------------------------\n\n// Vector with components value 0.0f\nRMAPI Vector3 Vector3Zero(void)\n{\n    Vector3 result = { 0.0f, 0.0f, 0.0f };\n\n    return result;\n}\n\n// Vector with components value 1.0f\nRMAPI Vector3 Vector3One(void)\n{\n    Vector3 result = { 1.0f, 1.0f, 1.0f };\n\n    return result;\n}\n\n// Add two vectors\nRMAPI Vector3 Vector3Add(Vector3 v1, Vector3 v2)\n{\n    Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };\n\n    return result;\n}\n\n// Add vector and float value\nRMAPI Vector3 Vector3AddValue(Vector3 v, float add)\n{\n    Vector3 result = { v.x + add, v.y + add, v.z + add };\n\n    return result;\n}\n\n// Subtract two vectors\nRMAPI Vector3 Vector3Subtract(Vector3 v1, Vector3 v2)\n{\n    Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };\n\n    return result;\n}\n\n// Subtract vector by float value\nRMAPI Vector3 Vector3SubtractValue(Vector3 v, float sub)\n{\n    Vector3 result = { v.x - sub, v.y - sub, v.z - sub };\n\n    return result;\n}\n\n// Multiply vector by scalar\nRMAPI Vector3 Vector3Scale(Vector3 v, float scalar)\n{\n    Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar };\n\n    return result;\n}\n\n// Multiply vector by vector\nRMAPI Vector3 Vector3Multiply(Vector3 v1, Vector3 v2)\n{\n    Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z };\n\n    return result;\n}\n\n// Calculate two vectors cross product\nRMAPI Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2)\n{\n    Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x };\n\n    return result;\n}\n\n// Calculate one vector perpendicular vector\nRMAPI Vector3 Vector3Perpendicular(Vector3 v)\n{\n    Vector3 result = { 0 };\n\n    float min = fabsf(v.x);\n    Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f};\n\n    if (fabsf(v.y) < min)\n    {\n        min = fabsf(v.y);\n        Vector3 tmp = {0.0f, 1.0f, 0.0f};\n        cardinalAxis = tmp;\n    }\n\n    if (fabsf(v.z) < min)\n    {\n        Vector3 tmp = {0.0f, 0.0f, 1.0f};\n        cardinalAxis = tmp;\n    }\n\n    // Cross product between vectors\n    result.x = v.y*cardinalAxis.z - v.z*cardinalAxis.y;\n    result.y = v.z*cardinalAxis.x - v.x*cardinalAxis.z;\n    result.z = v.x*cardinalAxis.y - v.y*cardinalAxis.x;\n\n    return result;\n}\n\n// Calculate vector length\nRMAPI float Vector3Length(const Vector3 v)\n{\n    float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);\n\n    return result;\n}\n\n// Calculate vector square length\nRMAPI float Vector3LengthSqr(const Vector3 v)\n{\n    float result = v.x*v.x + v.y*v.y + v.z*v.z;\n\n    return result;\n}\n\n// Calculate two vectors dot product\nRMAPI float Vector3DotProduct(Vector3 v1, Vector3 v2)\n{\n    float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);\n\n    return result;\n}\n\n// Calculate distance between two vectors\nRMAPI float Vector3Distance(Vector3 v1, Vector3 v2)\n{\n    float result = 0.0f;\n\n    float dx = v2.x - v1.x;\n    float dy = v2.y - v1.y;\n    float dz = v2.z - v1.z;\n    result = sqrtf(dx*dx + dy*dy + dz*dz);\n\n    return result;\n}\n\n// Calculate square distance between two vectors\nRMAPI float Vector3DistanceSqr(Vector3 v1, Vector3 v2)\n{\n    float result = 0.0f;\n\n    float dx = v2.x - v1.x;\n    float dy = v2.y - v1.y;\n    float dz = v2.z - v1.z;\n    result = dx*dx + dy*dy + dz*dz;\n\n    return result;\n}\n\n// Calculate angle between two vectors\nRMAPI float Vector3Angle(Vector3 v1, Vector3 v2)\n{\n    float result = 0.0f;\n\n    Vector3 cross = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x };\n    float len = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z);\n    float dot = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);\n    result = atan2f(len, dot);\n\n    return result;\n}\n\n// Negate provided vector (invert direction)\nRMAPI Vector3 Vector3Negate(Vector3 v)\n{\n    Vector3 result = { -v.x, -v.y, -v.z };\n\n    return result;\n}\n\n// Divide vector by vector\nRMAPI Vector3 Vector3Divide(Vector3 v1, Vector3 v2)\n{\n    Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z };\n\n    return result;\n}\n\n// Normalize provided vector\nRMAPI Vector3 Vector3Normalize(Vector3 v)\n{\n    Vector3 result = v;\n\n    float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);\n    if (length != 0.0f)\n    {\n        float ilength = 1.0f/length;\n\n        result.x *= ilength;\n        result.y *= ilength;\n        result.z *= ilength;\n    }\n\n    return result;\n}\n\n//Calculate the projection of the vector v1 on to v2\nRMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2)\n{\n    Vector3 result = { 0 };\n\n    float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);\n    float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z);\n\n    float mag = v1dv2/v2dv2;\n\n    result.x = v2.x*mag;\n    result.y = v2.y*mag;\n    result.z = v2.z*mag;\n\n    return result;\n}\n\n//Calculate the rejection of the vector v1 on to v2\nRMAPI Vector3 Vector3Reject(Vector3 v1, Vector3 v2)\n{\n    Vector3 result = { 0 };\n\n    float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);\n    float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z);\n\n    float mag = v1dv2/v2dv2;\n\n    result.x = v1.x - (v2.x*mag);\n    result.y = v1.y - (v2.y*mag);\n    result.z = v1.z - (v2.z*mag);\n\n    return result;\n}\n\n// Orthonormalize provided vectors\n// Makes vectors normalized and orthogonal to each other\n// Gram-Schmidt function implementation\nRMAPI void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2)\n{\n    float length = 0.0f;\n    float ilength = 0.0f;\n\n    // Vector3Normalize(*v1);\n    Vector3 v = *v1;\n    length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);\n    if (length == 0.0f) length = 1.0f;\n    ilength = 1.0f/length;\n    v1->x *= ilength;\n    v1->y *= ilength;\n    v1->z *= ilength;\n\n    // Vector3CrossProduct(*v1, *v2)\n    Vector3 vn1 = { v1->y*v2->z - v1->z*v2->y, v1->z*v2->x - v1->x*v2->z, v1->x*v2->y - v1->y*v2->x };\n\n    // Vector3Normalize(vn1);\n    v = vn1;\n    length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);\n    if (length == 0.0f) length = 1.0f;\n    ilength = 1.0f/length;\n    vn1.x *= ilength;\n    vn1.y *= ilength;\n    vn1.z *= ilength;\n\n    // Vector3CrossProduct(vn1, *v1)\n    Vector3 vn2 = { vn1.y*v1->z - vn1.z*v1->y, vn1.z*v1->x - vn1.x*v1->z, vn1.x*v1->y - vn1.y*v1->x };\n\n    *v2 = vn2;\n}\n\n// Transforms a Vector3 by a given Matrix\nRMAPI Vector3 Vector3Transform(Vector3 v, Matrix mat)\n{\n    Vector3 result = { 0 };\n\n    float x = v.x;\n    float y = v.y;\n    float z = v.z;\n\n    result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12;\n    result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13;\n    result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14;\n\n    return result;\n}\n\n// Transform a vector by quaternion rotation\nRMAPI Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q)\n{\n    Vector3 result = { 0 };\n\n    result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y);\n    result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z);\n    result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z);\n\n    return result;\n}\n\n// Rotates a vector around an axis\nRMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle)\n{\n    // Using Euler-Rodrigues Formula\n    // Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula\n\n    Vector3 result = v;\n\n    // Vector3Normalize(axis);\n    float length = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);\n    if (length == 0.0f) length = 1.0f;\n    float ilength = 1.0f/length;\n    axis.x *= ilength;\n    axis.y *= ilength;\n    axis.z *= ilength;\n\n    angle /= 2.0f;\n    float a = sinf(angle);\n    float b = axis.x*a;\n    float c = axis.y*a;\n    float d = axis.z*a;\n    a = cosf(angle);\n    Vector3 w = { b, c, d };\n\n    // Vector3CrossProduct(w, v)\n    Vector3 wv = { w.y*v.z - w.z*v.y, w.z*v.x - w.x*v.z, w.x*v.y - w.y*v.x };\n\n    // Vector3CrossProduct(w, wv)\n    Vector3 wwv = { w.y*wv.z - w.z*wv.y, w.z*wv.x - w.x*wv.z, w.x*wv.y - w.y*wv.x };\n\n    // Vector3Scale(wv, 2*a)\n    a *= 2;\n    wv.x *= a;\n    wv.y *= a;\n    wv.z *= a;\n\n    // Vector3Scale(wwv, 2)\n    wwv.x *= 2;\n    wwv.y *= 2;\n    wwv.z *= 2;\n\n    result.x += wv.x;\n    result.y += wv.y;\n    result.z += wv.z;\n\n    result.x += wwv.x;\n    result.y += wwv.y;\n    result.z += wwv.z;\n\n    return result;\n}\n\n// Move Vector towards target\nRMAPI Vector3 Vector3MoveTowards(Vector3 v, Vector3 target, float maxDistance)\n{\n    Vector3 result = { 0 };\n\n    float dx = target.x - v.x;\n    float dy = target.y - v.y;\n    float dz = target.z - v.z;\n    float value = (dx*dx) + (dy*dy) + (dz*dz);\n\n    if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target;\n\n    float dist = sqrtf(value);\n\n    result.x = v.x + dx/dist*maxDistance;\n    result.y = v.y + dy/dist*maxDistance;\n    result.z = v.z + dz/dist*maxDistance;\n\n    return result;\n}\n\n// Calculate linear interpolation between two vectors\nRMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount)\n{\n    Vector3 result = { 0 };\n\n    result.x = v1.x + amount*(v2.x - v1.x);\n    result.y = v1.y + amount*(v2.y - v1.y);\n    result.z = v1.z + amount*(v2.z - v1.z);\n\n    return result;\n}\n\n// Calculate cubic hermite interpolation between two vectors and their tangents\n// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic\nRMAPI Vector3 Vector3CubicHermite(Vector3 v1, Vector3 tangent1, Vector3 v2, Vector3 tangent2, float amount)\n{\n    Vector3 result = { 0 };\n\n    float amountPow2 = amount*amount;\n    float amountPow3 = amount*amount*amount;\n\n    result.x = (2*amountPow3 - 3*amountPow2 + 1)*v1.x + (amountPow3 - 2*amountPow2 + amount)*tangent1.x + (-2*amountPow3 + 3*amountPow2)*v2.x + (amountPow3 - amountPow2)*tangent2.x;\n    result.y = (2*amountPow3 - 3*amountPow2 + 1)*v1.y + (amountPow3 - 2*amountPow2 + amount)*tangent1.y + (-2*amountPow3 + 3*amountPow2)*v2.y + (amountPow3 - amountPow2)*tangent2.y;\n    result.z = (2*amountPow3 - 3*amountPow2 + 1)*v1.z + (amountPow3 - 2*amountPow2 + amount)*tangent1.z + (-2*amountPow3 + 3*amountPow2)*v2.z + (amountPow3 - amountPow2)*tangent2.z;\n\n    return result;\n}\n\n// Calculate reflected vector to normal\nRMAPI Vector3 Vector3Reflect(Vector3 v, Vector3 normal)\n{\n    Vector3 result = { 0 };\n\n    // I is the original vector\n    // N is the normal of the incident plane\n    // R = I - (2*N*(DotProduct[I, N]))\n\n    float dotProduct = (v.x*normal.x + v.y*normal.y + v.z*normal.z);\n\n    result.x = v.x - (2.0f*normal.x)*dotProduct;\n    result.y = v.y - (2.0f*normal.y)*dotProduct;\n    result.z = v.z - (2.0f*normal.z)*dotProduct;\n\n    return result;\n}\n\n// Get min value for each pair of components\nRMAPI Vector3 Vector3Min(Vector3 v1, Vector3 v2)\n{\n    Vector3 result = { 0 };\n\n    result.x = fminf(v1.x, v2.x);\n    result.y = fminf(v1.y, v2.y);\n    result.z = fminf(v1.z, v2.z);\n\n    return result;\n}\n\n// Get max value for each pair of components\nRMAPI Vector3 Vector3Max(Vector3 v1, Vector3 v2)\n{\n    Vector3 result = { 0 };\n\n    result.x = fmaxf(v1.x, v2.x);\n    result.y = fmaxf(v1.y, v2.y);\n    result.z = fmaxf(v1.z, v2.z);\n\n    return result;\n}\n\n// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c)\n// NOTE: Assumes P is on the plane of the triangle\nRMAPI Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c)\n{\n    Vector3 result = { 0 };\n\n    Vector3 v0 = { b.x - a.x, b.y - a.y, b.z - a.z };   // Vector3Subtract(b, a)\n    Vector3 v1 = { c.x - a.x, c.y - a.y, c.z - a.z };   // Vector3Subtract(c, a)\n    Vector3 v2 = { p.x - a.x, p.y - a.y, p.z - a.z };   // Vector3Subtract(p, a)\n    float d00 = (v0.x*v0.x + v0.y*v0.y + v0.z*v0.z);    // Vector3DotProduct(v0, v0)\n    float d01 = (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z);    // Vector3DotProduct(v0, v1)\n    float d11 = (v1.x*v1.x + v1.y*v1.y + v1.z*v1.z);    // Vector3DotProduct(v1, v1)\n    float d20 = (v2.x*v0.x + v2.y*v0.y + v2.z*v0.z);    // Vector3DotProduct(v2, v0)\n    float d21 = (v2.x*v1.x + v2.y*v1.y + v2.z*v1.z);    // Vector3DotProduct(v2, v1)\n\n    float denom = d00*d11 - d01*d01;\n\n    result.y = (d11*d20 - d01*d21)/denom;\n    result.z = (d00*d21 - d01*d20)/denom;\n    result.x = 1.0f - (result.z + result.y);\n\n    return result;\n}\n\n// Projects a Vector3 from screen space into object space\n// NOTE: We are avoiding calling other raymath functions despite available\nRMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view)\n{\n    Vector3 result = { 0 };\n\n    // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it\n    Matrix matViewProj = {      // MatrixMultiply(view, projection);\n        view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12,\n        view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13,\n        view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14,\n        view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15,\n        view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12,\n        view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13,\n        view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14,\n        view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15,\n        view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12,\n        view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13,\n        view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14,\n        view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15,\n        view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12,\n        view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13,\n        view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14,\n        view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 };\n\n    // Calculate inverted matrix -> MatrixInvert(matViewProj);\n    // Cache the matrix values (speed optimization)\n    float a00 = matViewProj.m0, a01 = matViewProj.m1, a02 = matViewProj.m2, a03 = matViewProj.m3;\n    float a10 = matViewProj.m4, a11 = matViewProj.m5, a12 = matViewProj.m6, a13 = matViewProj.m7;\n    float a20 = matViewProj.m8, a21 = matViewProj.m9, a22 = matViewProj.m10, a23 = matViewProj.m11;\n    float a30 = matViewProj.m12, a31 = matViewProj.m13, a32 = matViewProj.m14, a33 = matViewProj.m15;\n\n    float b00 = a00*a11 - a01*a10;\n    float b01 = a00*a12 - a02*a10;\n    float b02 = a00*a13 - a03*a10;\n    float b03 = a01*a12 - a02*a11;\n    float b04 = a01*a13 - a03*a11;\n    float b05 = a02*a13 - a03*a12;\n    float b06 = a20*a31 - a21*a30;\n    float b07 = a20*a32 - a22*a30;\n    float b08 = a20*a33 - a23*a30;\n    float b09 = a21*a32 - a22*a31;\n    float b10 = a21*a33 - a23*a31;\n    float b11 = a22*a33 - a23*a32;\n\n    // Calculate the invert determinant (inlined to avoid double-caching)\n    float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);\n\n    Matrix matViewProjInv = {\n        (a11*b11 - a12*b10 + a13*b09)*invDet,\n        (-a01*b11 + a02*b10 - a03*b09)*invDet,\n        (a31*b05 - a32*b04 + a33*b03)*invDet,\n        (-a21*b05 + a22*b04 - a23*b03)*invDet,\n        (-a10*b11 + a12*b08 - a13*b07)*invDet,\n        (a00*b11 - a02*b08 + a03*b07)*invDet,\n        (-a30*b05 + a32*b02 - a33*b01)*invDet,\n        (a20*b05 - a22*b02 + a23*b01)*invDet,\n        (a10*b10 - a11*b08 + a13*b06)*invDet,\n        (-a00*b10 + a01*b08 - a03*b06)*invDet,\n        (a30*b04 - a31*b02 + a33*b00)*invDet,\n        (-a20*b04 + a21*b02 - a23*b00)*invDet,\n        (-a10*b09 + a11*b07 - a12*b06)*invDet,\n        (a00*b09 - a01*b07 + a02*b06)*invDet,\n        (-a30*b03 + a31*b01 - a32*b00)*invDet,\n        (a20*b03 - a21*b01 + a22*b00)*invDet };\n\n    // Create quaternion from source point\n    Quaternion quat = { source.x, source.y, source.z, 1.0f };\n\n    // Multiply quat point by unprojecte matrix\n    Quaternion qtransformed = {     // QuaternionTransform(quat, matViewProjInv)\n        matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w,\n        matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w,\n        matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w,\n        matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w };\n\n    // Normalized world points in vectors\n    result.x = qtransformed.x/qtransformed.w;\n    result.y = qtransformed.y/qtransformed.w;\n    result.z = qtransformed.z/qtransformed.w;\n\n    return result;\n}\n\n// Get Vector3 as float array\nRMAPI float3 Vector3ToFloatV(Vector3 v)\n{\n    float3 buffer = { 0 };\n\n    buffer.v[0] = v.x;\n    buffer.v[1] = v.y;\n    buffer.v[2] = v.z;\n\n    return buffer;\n}\n\n// Invert the given vector\nRMAPI Vector3 Vector3Invert(Vector3 v)\n{\n    Vector3 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z };\n\n    return result;\n}\n\n// Clamp the components of the vector between\n// min and max values specified by the given vectors\nRMAPI Vector3 Vector3Clamp(Vector3 v, Vector3 min, Vector3 max)\n{\n    Vector3 result = { 0 };\n\n    result.x = fminf(max.x, fmaxf(min.x, v.x));\n    result.y = fminf(max.y, fmaxf(min.y, v.y));\n    result.z = fminf(max.z, fmaxf(min.z, v.z));\n\n    return result;\n}\n\n// Clamp the magnitude of the vector between two values\nRMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max)\n{\n    Vector3 result = v;\n\n    float length = (v.x*v.x) + (v.y*v.y) + (v.z*v.z);\n    if (length > 0.0f)\n    {\n        length = sqrtf(length);\n\n        float scale = 1;    // By default, 1 as the neutral element.\n        if (length < min)\n        {\n            scale = min/length;\n        }\n        else if (length > max)\n        {\n            scale = max/length;\n        }\n\n        result.x = v.x*scale;\n        result.y = v.y*scale;\n        result.z = v.z*scale;\n    }\n\n    return result;\n}\n\n// Check whether two given vectors are almost equal\nRMAPI int Vector3Equals(Vector3 p, Vector3 q)\n{\n#if !defined(EPSILON)\n    #define EPSILON 0.000001f\n#endif\n\n    int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&\n                 ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) &&\n                 ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z)))));\n\n    return result;\n}\n\n// Compute the direction of a refracted ray\n// v: normalized direction of the incoming ray\n// n: normalized normal vector of the interface of two optical media\n// r: ratio of the refractive index of the medium from where the ray comes\n//    to the refractive index of the medium on the other side of the surface\nRMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r)\n{\n    Vector3 result = { 0 };\n\n    float dot = v.x*n.x + v.y*n.y + v.z*n.z;\n    float d = 1.0f - r*r*(1.0f - dot*dot);\n\n    if (d >= 0.0f)\n    {\n        d = sqrtf(d);\n        v.x = r*v.x - (r*dot + d)*n.x;\n        v.y = r*v.y - (r*dot + d)*n.y;\n        v.z = r*v.z - (r*dot + d)*n.z;\n\n        result = v;\n    }\n\n    return result;\n}\n\n\n//----------------------------------------------------------------------------------\n// Module Functions Definition - Vector4 math\n//----------------------------------------------------------------------------------\n\nRMAPI Vector4 Vector4Zero(void)\n{\n    Vector4 result = { 0.0f, 0.0f, 0.0f, 0.0f };\n    return result;\n}\n\nRMAPI Vector4 Vector4One(void)\n{\n    Vector4 result = { 1.0f, 1.0f, 1.0f, 1.0f };\n    return result;\n}\n\nRMAPI Vector4 Vector4Add(Vector4 v1, Vector4 v2)\n{\n    Vector4 result = {\n        v1.x + v2.x,\n        v1.y + v2.y,\n        v1.z + v2.z,\n        v1.w + v2.w\n    };\n    return result;\n}\n\nRMAPI Vector4 Vector4AddValue(Vector4 v, float add)\n{\n    Vector4 result = {\n        v.x + add,\n        v.y + add,\n        v.z + add,\n        v.w + add\n    };\n    return result;\n}\n\nRMAPI Vector4 Vector4Subtract(Vector4 v1, Vector4 v2)\n{\n    Vector4 result = {\n        v1.x - v2.x,\n        v1.y - v2.y,\n        v1.z - v2.z,\n        v1.w - v2.w\n    };\n    return result;\n}\n\nRMAPI Vector4 Vector4SubtractValue(Vector4 v, float add)\n{\n    Vector4 result = {\n        v.x - add,\n        v.y - add,\n        v.z - add,\n        v.w - add\n    };\n    return result;\n}\n\nRMAPI float Vector4Length(Vector4 v)\n{\n    float result = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w));\n    return result;\n}\n\nRMAPI float Vector4LengthSqr(Vector4 v)\n{\n    float result = (v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w);\n    return result;\n}\n\nRMAPI float Vector4DotProduct(Vector4 v1, Vector4 v2)\n{\n    float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w);\n    return result;\n}\n\n// Calculate distance between two vectors\nRMAPI float Vector4Distance(Vector4 v1, Vector4 v2)\n{\n    float result = sqrtf(\n        (v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) +\n        (v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w));\n    return result;\n}\n\n// Calculate square distance between two vectors\nRMAPI float Vector4DistanceSqr(Vector4 v1, Vector4 v2)\n{\n    float result =\n        (v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) +\n        (v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w);\n\n    return result;\n}\n\nRMAPI Vector4 Vector4Scale(Vector4 v, float scale)\n{\n    Vector4 result = { v.x*scale, v.y*scale, v.z*scale, v.w*scale };\n    return result;\n}\n\n// Multiply vector by vector\nRMAPI Vector4 Vector4Multiply(Vector4 v1, Vector4 v2)\n{\n    Vector4 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z, v1.w*v2.w };\n    return result;\n}\n\n// Negate vector\nRMAPI Vector4 Vector4Negate(Vector4 v)\n{\n    Vector4 result = { -v.x, -v.y, -v.z, -v.w };\n    return result;\n}\n\n// Divide vector by vector\nRMAPI Vector4 Vector4Divide(Vector4 v1, Vector4 v2)\n{\n    Vector4 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z, v1.w/v2.w };\n    return result;\n}\n\n// Normalize provided vector\nRMAPI Vector4 Vector4Normalize(Vector4 v)\n{\n    Vector4 result = { 0 };\n    float length = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w));\n\n    if (length > 0)\n    {\n        float ilength = 1.0f/length;\n        result.x = v.x*ilength;\n        result.y = v.y*ilength;\n        result.z = v.z*ilength;\n        result.w = v.w*ilength;\n    }\n\n    return result;\n}\n\n// Get min value for each pair of components\nRMAPI Vector4 Vector4Min(Vector4 v1, Vector4 v2)\n{\n    Vector4 result = { 0 };\n\n    result.x = fminf(v1.x, v2.x);\n    result.y = fminf(v1.y, v2.y);\n    result.z = fminf(v1.z, v2.z);\n    result.w = fminf(v1.w, v2.w);\n\n    return result;\n}\n\n// Get max value for each pair of components\nRMAPI Vector4 Vector4Max(Vector4 v1, Vector4 v2)\n{\n    Vector4 result = { 0 };\n\n    result.x = fmaxf(v1.x, v2.x);\n    result.y = fmaxf(v1.y, v2.y);\n    result.z = fmaxf(v1.z, v2.z);\n    result.w = fmaxf(v1.w, v2.w);\n\n    return result;\n}\n\n// Calculate linear interpolation between two vectors\nRMAPI Vector4 Vector4Lerp(Vector4 v1, Vector4 v2, float amount)\n{\n    Vector4 result = { 0 };\n\n    result.x = v1.x + amount*(v2.x - v1.x);\n    result.y = v1.y + amount*(v2.y - v1.y);\n    result.z = v1.z + amount*(v2.z - v1.z);\n    result.w = v1.w + amount*(v2.w - v1.w);\n\n    return result;\n}\n\n// Move Vector towards target\nRMAPI Vector4 Vector4MoveTowards(Vector4 v, Vector4 target, float maxDistance)\n{\n    Vector4 result = { 0 };\n\n    float dx = target.x - v.x;\n    float dy = target.y - v.y;\n    float dz = target.z - v.z;\n    float dw = target.w - v.w;\n    float value = (dx*dx) + (dy*dy) + (dz*dz) + (dw*dw);\n\n    if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target;\n\n    float dist = sqrtf(value);\n\n    result.x = v.x + dx/dist*maxDistance;\n    result.y = v.y + dy/dist*maxDistance;\n    result.z = v.z + dz/dist*maxDistance;\n    result.w = v.w + dw/dist*maxDistance;\n\n    return result;\n}\n\n// Invert the given vector\nRMAPI Vector4 Vector4Invert(Vector4 v)\n{\n    Vector4 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z, 1.0f/v.w };\n    return result;\n}\n\n// Check whether two given vectors are almost equal\nRMAPI int Vector4Equals(Vector4 p, Vector4 q)\n{\n#if !defined(EPSILON)\n    #define EPSILON 0.000001f\n#endif\n\n    int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&\n                  ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) &&\n                  ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) &&\n                  ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))));\n    return result;\n}\n\n\n//----------------------------------------------------------------------------------\n// Module Functions Definition - Matrix math\n//----------------------------------------------------------------------------------\n\n// Compute matrix determinant\nRMAPI float MatrixDeterminant(Matrix mat)\n{\n    float result = 0.0f;\n\n    // Cache the matrix values (speed optimization)\n    float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3;\n    float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7;\n    float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11;\n    float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15;\n\n    result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 +\n             a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 +\n             a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 +\n             a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 +\n             a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 +\n             a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33;\n\n    return result;\n}\n\n// Get the trace of the matrix (sum of the values along the diagonal)\nRMAPI float MatrixTrace(Matrix mat)\n{\n    float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15);\n\n    return result;\n}\n\n// Transposes provided matrix\nRMAPI Matrix MatrixTranspose(Matrix mat)\n{\n    Matrix result = { 0 };\n\n    result.m0 = mat.m0;\n    result.m1 = mat.m4;\n    result.m2 = mat.m8;\n    result.m3 = mat.m12;\n    result.m4 = mat.m1;\n    result.m5 = mat.m5;\n    result.m6 = mat.m9;\n    result.m7 = mat.m13;\n    result.m8 = mat.m2;\n    result.m9 = mat.m6;\n    result.m10 = mat.m10;\n    result.m11 = mat.m14;\n    result.m12 = mat.m3;\n    result.m13 = mat.m7;\n    result.m14 = mat.m11;\n    result.m15 = mat.m15;\n\n    return result;\n}\n\n// Invert provided matrix\nRMAPI Matrix MatrixInvert(Matrix mat)\n{\n    Matrix result = { 0 };\n\n    // Cache the matrix values (speed optimization)\n    float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3;\n    float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7;\n    float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11;\n    float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15;\n\n    float b00 = a00*a11 - a01*a10;\n    float b01 = a00*a12 - a02*a10;\n    float b02 = a00*a13 - a03*a10;\n    float b03 = a01*a12 - a02*a11;\n    float b04 = a01*a13 - a03*a11;\n    float b05 = a02*a13 - a03*a12;\n    float b06 = a20*a31 - a21*a30;\n    float b07 = a20*a32 - a22*a30;\n    float b08 = a20*a33 - a23*a30;\n    float b09 = a21*a32 - a22*a31;\n    float b10 = a21*a33 - a23*a31;\n    float b11 = a22*a33 - a23*a32;\n\n    // Calculate the invert determinant (inlined to avoid double-caching)\n    float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);\n\n    result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet;\n    result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet;\n    result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet;\n    result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet;\n    result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet;\n    result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet;\n    result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet;\n    result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet;\n    result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet;\n    result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet;\n    result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet;\n    result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet;\n    result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet;\n    result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet;\n    result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet;\n    result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet;\n\n    return result;\n}\n\n// Get identity matrix\nRMAPI Matrix MatrixIdentity(void)\n{\n    Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,\n                      0.0f, 1.0f, 0.0f, 0.0f,\n                      0.0f, 0.0f, 1.0f, 0.0f,\n                      0.0f, 0.0f, 0.0f, 1.0f };\n\n    return result;\n}\n\n// Add two matrices\nRMAPI Matrix MatrixAdd(Matrix left, Matrix right)\n{\n    Matrix result = { 0 };\n\n    result.m0 = left.m0 + right.m0;\n    result.m1 = left.m1 + right.m1;\n    result.m2 = left.m2 + right.m2;\n    result.m3 = left.m3 + right.m3;\n    result.m4 = left.m4 + right.m4;\n    result.m5 = left.m5 + right.m5;\n    result.m6 = left.m6 + right.m6;\n    result.m7 = left.m7 + right.m7;\n    result.m8 = left.m8 + right.m8;\n    result.m9 = left.m9 + right.m9;\n    result.m10 = left.m10 + right.m10;\n    result.m11 = left.m11 + right.m11;\n    result.m12 = left.m12 + right.m12;\n    result.m13 = left.m13 + right.m13;\n    result.m14 = left.m14 + right.m14;\n    result.m15 = left.m15 + right.m15;\n\n    return result;\n}\n\n// Subtract two matrices (left - right)\nRMAPI Matrix MatrixSubtract(Matrix left, Matrix right)\n{\n    Matrix result = { 0 };\n\n    result.m0 = left.m0 - right.m0;\n    result.m1 = left.m1 - right.m1;\n    result.m2 = left.m2 - right.m2;\n    result.m3 = left.m3 - right.m3;\n    result.m4 = left.m4 - right.m4;\n    result.m5 = left.m5 - right.m5;\n    result.m6 = left.m6 - right.m6;\n    result.m7 = left.m7 - right.m7;\n    result.m8 = left.m8 - right.m8;\n    result.m9 = left.m9 - right.m9;\n    result.m10 = left.m10 - right.m10;\n    result.m11 = left.m11 - right.m11;\n    result.m12 = left.m12 - right.m12;\n    result.m13 = left.m13 - right.m13;\n    result.m14 = left.m14 - right.m14;\n    result.m15 = left.m15 - right.m15;\n\n    return result;\n}\n\n// Get two matrix multiplication\n// NOTE: When multiplying matrices... the order matters!\nRMAPI Matrix MatrixMultiply(Matrix left, Matrix right)\n{\n    Matrix result = { 0 };\n\n    result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12;\n    result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13;\n    result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14;\n    result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15;\n    result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12;\n    result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13;\n    result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14;\n    result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15;\n    result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12;\n    result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13;\n    result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14;\n    result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15;\n    result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12;\n    result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13;\n    result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14;\n    result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15;\n\n    return result;\n}\n\n// Get translation matrix\nRMAPI Matrix MatrixTranslate(float x, float y, float z)\n{\n    Matrix result = { 1.0f, 0.0f, 0.0f, x,\n                      0.0f, 1.0f, 0.0f, y,\n                      0.0f, 0.0f, 1.0f, z,\n                      0.0f, 0.0f, 0.0f, 1.0f };\n\n    return result;\n}\n\n// Create rotation matrix from axis and angle\n// NOTE: Angle should be provided in radians\nRMAPI Matrix MatrixRotate(Vector3 axis, float angle)\n{\n    Matrix result = { 0 };\n\n    float x = axis.x, y = axis.y, z = axis.z;\n\n    float lengthSquared = x*x + y*y + z*z;\n\n    if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f))\n    {\n        float ilength = 1.0f/sqrtf(lengthSquared);\n        x *= ilength;\n        y *= ilength;\n        z *= ilength;\n    }\n\n    float sinres = sinf(angle);\n    float cosres = cosf(angle);\n    float t = 1.0f - cosres;\n\n    result.m0 = x*x*t + cosres;\n    result.m1 = y*x*t + z*sinres;\n    result.m2 = z*x*t - y*sinres;\n    result.m3 = 0.0f;\n\n    result.m4 = x*y*t - z*sinres;\n    result.m5 = y*y*t + cosres;\n    result.m6 = z*y*t + x*sinres;\n    result.m7 = 0.0f;\n\n    result.m8 = x*z*t + y*sinres;\n    result.m9 = y*z*t - x*sinres;\n    result.m10 = z*z*t + cosres;\n    result.m11 = 0.0f;\n\n    result.m12 = 0.0f;\n    result.m13 = 0.0f;\n    result.m14 = 0.0f;\n    result.m15 = 1.0f;\n\n    return result;\n}\n\n// Get x-rotation matrix\n// NOTE: Angle must be provided in radians\nRMAPI Matrix MatrixRotateX(float angle)\n{\n    Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,\n                      0.0f, 1.0f, 0.0f, 0.0f,\n                      0.0f, 0.0f, 1.0f, 0.0f,\n                      0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity()\n\n    float cosres = cosf(angle);\n    float sinres = sinf(angle);\n\n    result.m5 = cosres;\n    result.m6 = sinres;\n    result.m9 = -sinres;\n    result.m10 = cosres;\n\n    return result;\n}\n\n// Get y-rotation matrix\n// NOTE: Angle must be provided in radians\nRMAPI Matrix MatrixRotateY(float angle)\n{\n    Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,\n                      0.0f, 1.0f, 0.0f, 0.0f,\n                      0.0f, 0.0f, 1.0f, 0.0f,\n                      0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity()\n\n    float cosres = cosf(angle);\n    float sinres = sinf(angle);\n\n    result.m0 = cosres;\n    result.m2 = -sinres;\n    result.m8 = sinres;\n    result.m10 = cosres;\n\n    return result;\n}\n\n// Get z-rotation matrix\n// NOTE: Angle must be provided in radians\nRMAPI Matrix MatrixRotateZ(float angle)\n{\n    Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,\n                      0.0f, 1.0f, 0.0f, 0.0f,\n                      0.0f, 0.0f, 1.0f, 0.0f,\n                      0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity()\n\n    float cosres = cosf(angle);\n    float sinres = sinf(angle);\n\n    result.m0 = cosres;\n    result.m1 = sinres;\n    result.m4 = -sinres;\n    result.m5 = cosres;\n\n    return result;\n}\n\n\n// Get xyz-rotation matrix\n// NOTE: Angle must be provided in radians\nRMAPI Matrix MatrixRotateXYZ(Vector3 angle)\n{\n    Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,\n                      0.0f, 1.0f, 0.0f, 0.0f,\n                      0.0f, 0.0f, 1.0f, 0.0f,\n                      0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity()\n\n    float cosz = cosf(-angle.z);\n    float sinz = sinf(-angle.z);\n    float cosy = cosf(-angle.y);\n    float siny = sinf(-angle.y);\n    float cosx = cosf(-angle.x);\n    float sinx = sinf(-angle.x);\n\n    result.m0 = cosz*cosy;\n    result.m1 = (cosz*siny*sinx) - (sinz*cosx);\n    result.m2 = (cosz*siny*cosx) + (sinz*sinx);\n\n    result.m4 = sinz*cosy;\n    result.m5 = (sinz*siny*sinx) + (cosz*cosx);\n    result.m6 = (sinz*siny*cosx) - (cosz*sinx);\n\n    result.m8 = -siny;\n    result.m9 = cosy*sinx;\n    result.m10= cosy*cosx;\n\n    return result;\n}\n\n// Get zyx-rotation matrix\n// NOTE: Angle must be provided in radians\nRMAPI Matrix MatrixRotateZYX(Vector3 angle)\n{\n    Matrix result = { 0 };\n\n    float cz = cosf(angle.z);\n    float sz = sinf(angle.z);\n    float cy = cosf(angle.y);\n    float sy = sinf(angle.y);\n    float cx = cosf(angle.x);\n    float sx = sinf(angle.x);\n\n    result.m0 = cz*cy;\n    result.m4 = cz*sy*sx - cx*sz;\n    result.m8 = sz*sx + cz*cx*sy;\n    result.m12 = 0;\n\n    result.m1 = cy*sz;\n    result.m5 = cz*cx + sz*sy*sx;\n    result.m9 = cx*sz*sy - cz*sx;\n    result.m13 = 0;\n\n    result.m2 = -sy;\n    result.m6 = cy*sx;\n    result.m10 = cy*cx;\n    result.m14 = 0;\n\n    result.m3 = 0;\n    result.m7 = 0;\n    result.m11 = 0;\n    result.m15 = 1;\n\n    return result;\n}\n\n// Get scaling matrix\nRMAPI Matrix MatrixScale(float x, float y, float z)\n{\n    Matrix result = { x, 0.0f, 0.0f, 0.0f,\n                      0.0f, y, 0.0f, 0.0f,\n                      0.0f, 0.0f, z, 0.0f,\n                      0.0f, 0.0f, 0.0f, 1.0f };\n\n    return result;\n}\n\n// Get perspective projection matrix\nRMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, double nearPlane, double farPlane)\n{\n    Matrix result = { 0 };\n\n    float rl = (float)(right - left);\n    float tb = (float)(top - bottom);\n    float fn = (float)(farPlane - nearPlane);\n\n    result.m0 = ((float)nearPlane*2.0f)/rl;\n    result.m1 = 0.0f;\n    result.m2 = 0.0f;\n    result.m3 = 0.0f;\n\n    result.m4 = 0.0f;\n    result.m5 = ((float)nearPlane*2.0f)/tb;\n    result.m6 = 0.0f;\n    result.m7 = 0.0f;\n\n    result.m8 = ((float)right + (float)left)/rl;\n    result.m9 = ((float)top + (float)bottom)/tb;\n    result.m10 = -((float)farPlane + (float)nearPlane)/fn;\n    result.m11 = -1.0f;\n\n    result.m12 = 0.0f;\n    result.m13 = 0.0f;\n    result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn;\n    result.m15 = 0.0f;\n\n    return result;\n}\n\n// Get perspective projection matrix\n// NOTE: Fovy angle must be provided in radians\nRMAPI Matrix MatrixPerspective(double fovY, double aspect, double nearPlane, double farPlane)\n{\n    Matrix result = { 0 };\n\n    double top = nearPlane*tan(fovY*0.5);\n    double bottom = -top;\n    double right = top*aspect;\n    double left = -right;\n\n    // MatrixFrustum(-right, right, -top, top, near, far);\n    float rl = (float)(right - left);\n    float tb = (float)(top - bottom);\n    float fn = (float)(farPlane - nearPlane);\n\n    result.m0 = ((float)nearPlane*2.0f)/rl;\n    result.m5 = ((float)nearPlane*2.0f)/tb;\n    result.m8 = ((float)right + (float)left)/rl;\n    result.m9 = ((float)top + (float)bottom)/tb;\n    result.m10 = -((float)farPlane + (float)nearPlane)/fn;\n    result.m11 = -1.0f;\n    result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn;\n\n    return result;\n}\n\n// Get orthographic projection matrix\nRMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane)\n{\n    Matrix result = { 0 };\n\n    float rl = (float)(right - left);\n    float tb = (float)(top - bottom);\n    float fn = (float)(farPlane - nearPlane);\n\n    result.m0 = 2.0f/rl;\n    result.m1 = 0.0f;\n    result.m2 = 0.0f;\n    result.m3 = 0.0f;\n    result.m4 = 0.0f;\n    result.m5 = 2.0f/tb;\n    result.m6 = 0.0f;\n    result.m7 = 0.0f;\n    result.m8 = 0.0f;\n    result.m9 = 0.0f;\n    result.m10 = -2.0f/fn;\n    result.m11 = 0.0f;\n    result.m12 = -((float)left + (float)right)/rl;\n    result.m13 = -((float)top + (float)bottom)/tb;\n    result.m14 = -((float)farPlane + (float)nearPlane)/fn;\n    result.m15 = 1.0f;\n\n    return result;\n}\n\n// Get camera look-at matrix (view matrix)\nRMAPI Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up)\n{\n    Matrix result = { 0 };\n\n    float length = 0.0f;\n    float ilength = 0.0f;\n\n    // Vector3Subtract(eye, target)\n    Vector3 vz = { eye.x - target.x, eye.y - target.y, eye.z - target.z };\n\n    // Vector3Normalize(vz)\n    Vector3 v = vz;\n    length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);\n    if (length == 0.0f) length = 1.0f;\n    ilength = 1.0f/length;\n    vz.x *= ilength;\n    vz.y *= ilength;\n    vz.z *= ilength;\n\n    // Vector3CrossProduct(up, vz)\n    Vector3 vx = { up.y*vz.z - up.z*vz.y, up.z*vz.x - up.x*vz.z, up.x*vz.y - up.y*vz.x };\n\n    // Vector3Normalize(x)\n    v = vx;\n    length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);\n    if (length == 0.0f) length = 1.0f;\n    ilength = 1.0f/length;\n    vx.x *= ilength;\n    vx.y *= ilength;\n    vx.z *= ilength;\n\n    // Vector3CrossProduct(vz, vx)\n    Vector3 vy = { vz.y*vx.z - vz.z*vx.y, vz.z*vx.x - vz.x*vx.z, vz.x*vx.y - vz.y*vx.x };\n\n    result.m0 = vx.x;\n    result.m1 = vy.x;\n    result.m2 = vz.x;\n    result.m3 = 0.0f;\n    result.m4 = vx.y;\n    result.m5 = vy.y;\n    result.m6 = vz.y;\n    result.m7 = 0.0f;\n    result.m8 = vx.z;\n    result.m9 = vy.z;\n    result.m10 = vz.z;\n    result.m11 = 0.0f;\n    result.m12 = -(vx.x*eye.x + vx.y*eye.y + vx.z*eye.z);   // Vector3DotProduct(vx, eye)\n    result.m13 = -(vy.x*eye.x + vy.y*eye.y + vy.z*eye.z);   // Vector3DotProduct(vy, eye)\n    result.m14 = -(vz.x*eye.x + vz.y*eye.y + vz.z*eye.z);   // Vector3DotProduct(vz, eye)\n    result.m15 = 1.0f;\n\n    return result;\n}\n\n// Get float array of matrix data\nRMAPI float16 MatrixToFloatV(Matrix mat)\n{\n    float16 result = { 0 };\n\n    result.v[0] = mat.m0;\n    result.v[1] = mat.m1;\n    result.v[2] = mat.m2;\n    result.v[3] = mat.m3;\n    result.v[4] = mat.m4;\n    result.v[5] = mat.m5;\n    result.v[6] = mat.m6;\n    result.v[7] = mat.m7;\n    result.v[8] = mat.m8;\n    result.v[9] = mat.m9;\n    result.v[10] = mat.m10;\n    result.v[11] = mat.m11;\n    result.v[12] = mat.m12;\n    result.v[13] = mat.m13;\n    result.v[14] = mat.m14;\n    result.v[15] = mat.m15;\n\n    return result;\n}\n\n//----------------------------------------------------------------------------------\n// Module Functions Definition - Quaternion math\n//----------------------------------------------------------------------------------\n\n// Add two quaternions\nRMAPI Quaternion QuaternionAdd(Quaternion q1, Quaternion q2)\n{\n    Quaternion result = {q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w};\n\n    return result;\n}\n\n// Add quaternion and float value\nRMAPI Quaternion QuaternionAddValue(Quaternion q, float add)\n{\n    Quaternion result = {q.x + add, q.y + add, q.z + add, q.w + add};\n\n    return result;\n}\n\n// Subtract two quaternions\nRMAPI Quaternion QuaternionSubtract(Quaternion q1, Quaternion q2)\n{\n    Quaternion result = {q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w};\n\n    return result;\n}\n\n// Subtract quaternion and float value\nRMAPI Quaternion QuaternionSubtractValue(Quaternion q, float sub)\n{\n    Quaternion result = {q.x - sub, q.y - sub, q.z - sub, q.w - sub};\n\n    return result;\n}\n\n// Get identity quaternion\nRMAPI Quaternion QuaternionIdentity(void)\n{\n    Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f };\n\n    return result;\n}\n\n// Computes the length of a quaternion\nRMAPI float QuaternionLength(Quaternion q)\n{\n    float result = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);\n\n    return result;\n}\n\n// Normalize provided quaternion\nRMAPI Quaternion QuaternionNormalize(Quaternion q)\n{\n    Quaternion result = { 0 };\n\n    float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);\n    if (length == 0.0f) length = 1.0f;\n    float ilength = 1.0f/length;\n\n    result.x = q.x*ilength;\n    result.y = q.y*ilength;\n    result.z = q.z*ilength;\n    result.w = q.w*ilength;\n\n    return result;\n}\n\n// Invert provided quaternion\nRMAPI Quaternion QuaternionInvert(Quaternion q)\n{\n    Quaternion result = q;\n\n    float lengthSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;\n\n    if (lengthSq != 0.0f)\n    {\n        float invLength = 1.0f/lengthSq;\n\n        result.x *= -invLength;\n        result.y *= -invLength;\n        result.z *= -invLength;\n        result.w *= invLength;\n    }\n\n    return result;\n}\n\n// Calculate two quaternion multiplication\nRMAPI Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2)\n{\n    Quaternion result = { 0 };\n\n    float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w;\n    float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w;\n\n    result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby;\n    result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz;\n    result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx;\n    result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz;\n\n    return result;\n}\n\n// Scale quaternion by float value\nRMAPI Quaternion QuaternionScale(Quaternion q, float mul)\n{\n    Quaternion result = { 0 };\n\n    result.x = q.x*mul;\n    result.y = q.y*mul;\n    result.z = q.z*mul;\n    result.w = q.w*mul;\n\n    return result;\n}\n\n// Divide two quaternions\nRMAPI Quaternion QuaternionDivide(Quaternion q1, Quaternion q2)\n{\n    Quaternion result = { q1.x/q2.x, q1.y/q2.y, q1.z/q2.z, q1.w/q2.w };\n\n    return result;\n}\n\n// Calculate linear interpolation between two quaternions\nRMAPI Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount)\n{\n    Quaternion result = { 0 };\n\n    result.x = q1.x + amount*(q2.x - q1.x);\n    result.y = q1.y + amount*(q2.y - q1.y);\n    result.z = q1.z + amount*(q2.z - q1.z);\n    result.w = q1.w + amount*(q2.w - q1.w);\n\n    return result;\n}\n\n// Calculate slerp-optimized interpolation between two quaternions\nRMAPI Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount)\n{\n    Quaternion result = { 0 };\n\n    // QuaternionLerp(q1, q2, amount)\n    result.x = q1.x + amount*(q2.x - q1.x);\n    result.y = q1.y + amount*(q2.y - q1.y);\n    result.z = q1.z + amount*(q2.z - q1.z);\n    result.w = q1.w + amount*(q2.w - q1.w);\n\n    // QuaternionNormalize(q);\n    Quaternion q = result;\n    float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);\n    if (length == 0.0f) length = 1.0f;\n    float ilength = 1.0f/length;\n\n    result.x = q.x*ilength;\n    result.y = q.y*ilength;\n    result.z = q.z*ilength;\n    result.w = q.w*ilength;\n\n    return result;\n}\n\n// Calculates spherical linear interpolation between two quaternions\nRMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount)\n{\n    Quaternion result = { 0 };\n\n#if !defined(EPSILON)\n    #define EPSILON 0.000001f\n#endif\n\n    float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w;\n\n    if (cosHalfTheta < 0)\n    {\n        q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w;\n        cosHalfTheta = -cosHalfTheta;\n    }\n\n    if (fabsf(cosHalfTheta) >= 1.0f) result = q1;\n    else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount);\n    else\n    {\n        float halfTheta = acosf(cosHalfTheta);\n        float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta);\n\n        if (fabsf(sinHalfTheta) < EPSILON)\n        {\n            result.x = (q1.x*0.5f + q2.x*0.5f);\n            result.y = (q1.y*0.5f + q2.y*0.5f);\n            result.z = (q1.z*0.5f + q2.z*0.5f);\n            result.w = (q1.w*0.5f + q2.w*0.5f);\n        }\n        else\n        {\n            float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta;\n            float ratioB = sinf(amount*halfTheta)/sinHalfTheta;\n\n            result.x = (q1.x*ratioA + q2.x*ratioB);\n            result.y = (q1.y*ratioA + q2.y*ratioB);\n            result.z = (q1.z*ratioA + q2.z*ratioB);\n            result.w = (q1.w*ratioA + q2.w*ratioB);\n        }\n    }\n\n    return result;\n}\n\n// Calculate quaternion cubic spline interpolation using Cubic Hermite Spline algorithm\n// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic\nRMAPI Quaternion QuaternionCubicHermiteSpline(Quaternion q1, Quaternion outTangent1, Quaternion q2, Quaternion inTangent2, float t)\n{\n    float t2 = t*t;\n    float t3 = t2*t;\n    float h00 = 2*t3 - 3*t2 + 1;\n    float h10 = t3 - 2*t2 + t;\n    float h01 = -2*t3 + 3*t2;\n    float h11 = t3 - t2;\n\n    Quaternion p0 = QuaternionScale(q1, h00);\n    Quaternion m0 = QuaternionScale(outTangent1, h10);\n    Quaternion p1 = QuaternionScale(q2, h01);\n    Quaternion m1 = QuaternionScale(inTangent2, h11);\n\n    Quaternion result = { 0 };\n\n    result = QuaternionAdd(p0, m0);\n    result = QuaternionAdd(result, p1);\n    result = QuaternionAdd(result, m1);\n    result = QuaternionNormalize(result);\n\n    return result;\n}\n\n// Calculate quaternion based on the rotation from one vector to another\nRMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to)\n{\n    Quaternion result = { 0 };\n\n    float cos2Theta = (from.x*to.x + from.y*to.y + from.z*to.z);    // Vector3DotProduct(from, to)\n    Vector3 cross = { from.y*to.z - from.z*to.y, from.z*to.x - from.x*to.z, from.x*to.y - from.y*to.x }; // Vector3CrossProduct(from, to)\n\n    result.x = cross.x;\n    result.y = cross.y;\n    result.z = cross.z;\n    result.w = 1.0f + cos2Theta;\n\n    // QuaternionNormalize(q);\n    // NOTE: Normalize to essentially nlerp the original and identity to 0.5\n    Quaternion q = result;\n    float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);\n    if (length == 0.0f) length = 1.0f;\n    float ilength = 1.0f/length;\n\n    result.x = q.x*ilength;\n    result.y = q.y*ilength;\n    result.z = q.z*ilength;\n    result.w = q.w*ilength;\n\n    return result;\n}\n\n// Get a quaternion for a given rotation matrix\nRMAPI Quaternion QuaternionFromMatrix(Matrix mat)\n{\n    Quaternion result = { 0 };\n\n    float fourWSquaredMinus1 = mat.m0  + mat.m5 + mat.m10;\n    float fourXSquaredMinus1 = mat.m0  - mat.m5 - mat.m10;\n    float fourYSquaredMinus1 = mat.m5  - mat.m0 - mat.m10;\n    float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5;\n\n    int biggestIndex = 0;\n    float fourBiggestSquaredMinus1 = fourWSquaredMinus1;\n    if (fourXSquaredMinus1 > fourBiggestSquaredMinus1)\n    {\n        fourBiggestSquaredMinus1 = fourXSquaredMinus1;\n        biggestIndex = 1;\n    }\n\n    if (fourYSquaredMinus1 > fourBiggestSquaredMinus1)\n    {\n        fourBiggestSquaredMinus1 = fourYSquaredMinus1;\n        biggestIndex = 2;\n    }\n\n    if (fourZSquaredMinus1 > fourBiggestSquaredMinus1)\n    {\n        fourBiggestSquaredMinus1 = fourZSquaredMinus1;\n        biggestIndex = 3;\n    }\n\n    float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f)*0.5f;\n    float mult = 0.25f/biggestVal;\n\n    switch (biggestIndex)\n    {\n        case 0:\n            result.w = biggestVal;\n            result.x = (mat.m6 - mat.m9)*mult;\n            result.y = (mat.m8 - mat.m2)*mult;\n            result.z = (mat.m1 - mat.m4)*mult;\n            break;\n        case 1:\n            result.x = biggestVal;\n            result.w = (mat.m6 - mat.m9)*mult;\n            result.y = (mat.m1 + mat.m4)*mult;\n            result.z = (mat.m8 + mat.m2)*mult;\n            break;\n        case 2:\n            result.y = biggestVal;\n            result.w = (mat.m8 - mat.m2)*mult;\n            result.x = (mat.m1 + mat.m4)*mult;\n            result.z = (mat.m6 + mat.m9)*mult;\n            break;\n        case 3:\n            result.z = biggestVal;\n            result.w = (mat.m1 - mat.m4)*mult;\n            result.x = (mat.m8 + mat.m2)*mult;\n            result.y = (mat.m6 + mat.m9)*mult;\n            break;\n    }\n\n    return result;\n}\n\n// Get a matrix for a given quaternion\nRMAPI Matrix QuaternionToMatrix(Quaternion q)\n{\n    Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,\n                      0.0f, 1.0f, 0.0f, 0.0f,\n                      0.0f, 0.0f, 1.0f, 0.0f,\n                      0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity()\n\n    float a2 = q.x*q.x;\n    float b2 = q.y*q.y;\n    float c2 = q.z*q.z;\n    float ac = q.x*q.z;\n    float ab = q.x*q.y;\n    float bc = q.y*q.z;\n    float ad = q.w*q.x;\n    float bd = q.w*q.y;\n    float cd = q.w*q.z;\n\n    result.m0 = 1 - 2*(b2 + c2);\n    result.m1 = 2*(ab + cd);\n    result.m2 = 2*(ac - bd);\n\n    result.m4 = 2*(ab - cd);\n    result.m5 = 1 - 2*(a2 + c2);\n    result.m6 = 2*(bc + ad);\n\n    result.m8 = 2*(ac + bd);\n    result.m9 = 2*(bc - ad);\n    result.m10 = 1 - 2*(a2 + b2);\n\n    return result;\n}\n\n// Get rotation quaternion for an angle and axis\n// NOTE: Angle must be provided in radians\nRMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle)\n{\n    Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f };\n\n    float axisLength = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);\n\n    if (axisLength != 0.0f)\n    {\n        angle *= 0.5f;\n\n        float length = 0.0f;\n        float ilength = 0.0f;\n\n        // Vector3Normalize(axis)\n        length = axisLength;\n        if (length == 0.0f) length = 1.0f;\n        ilength = 1.0f/length;\n        axis.x *= ilength;\n        axis.y *= ilength;\n        axis.z *= ilength;\n\n        float sinres = sinf(angle);\n        float cosres = cosf(angle);\n\n        result.x = axis.x*sinres;\n        result.y = axis.y*sinres;\n        result.z = axis.z*sinres;\n        result.w = cosres;\n\n        // QuaternionNormalize(q);\n        Quaternion q = result;\n        length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);\n        if (length == 0.0f) length = 1.0f;\n        ilength = 1.0f/length;\n        result.x = q.x*ilength;\n        result.y = q.y*ilength;\n        result.z = q.z*ilength;\n        result.w = q.w*ilength;\n    }\n\n    return result;\n}\n\n// Get the rotation angle and axis for a given quaternion\nRMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle)\n{\n    if (fabsf(q.w) > 1.0f)\n    {\n        // QuaternionNormalize(q);\n        float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);\n        if (length == 0.0f) length = 1.0f;\n        float ilength = 1.0f/length;\n\n        q.x = q.x*ilength;\n        q.y = q.y*ilength;\n        q.z = q.z*ilength;\n        q.w = q.w*ilength;\n    }\n\n    Vector3 resAxis = { 0.0f, 0.0f, 0.0f };\n    float resAngle = 2.0f*acosf(q.w);\n    float den = sqrtf(1.0f - q.w*q.w);\n\n    if (den > EPSILON)\n    {\n        resAxis.x = q.x/den;\n        resAxis.y = q.y/den;\n        resAxis.z = q.z/den;\n    }\n    else\n    {\n        // This occurs when the angle is zero.\n        // Not a problem: just set an arbitrary normalized axis.\n        resAxis.x = 1.0f;\n    }\n\n    *outAxis = resAxis;\n    *outAngle = resAngle;\n}\n\n// Get the quaternion equivalent to Euler angles\n// NOTE: Rotation order is ZYX\nRMAPI Quaternion QuaternionFromEuler(float pitch, float yaw, float roll)\n{\n    Quaternion result = { 0 };\n\n    float x0 = cosf(pitch*0.5f);\n    float x1 = sinf(pitch*0.5f);\n    float y0 = cosf(yaw*0.5f);\n    float y1 = sinf(yaw*0.5f);\n    float z0 = cosf(roll*0.5f);\n    float z1 = sinf(roll*0.5f);\n\n    result.x = x1*y0*z0 - x0*y1*z1;\n    result.y = x0*y1*z0 + x1*y0*z1;\n    result.z = x0*y0*z1 - x1*y1*z0;\n    result.w = x0*y0*z0 + x1*y1*z1;\n\n    return result;\n}\n\n// Get the Euler angles equivalent to quaternion (roll, pitch, yaw)\n// NOTE: Angles are returned in a Vector3 struct in radians\nRMAPI Vector3 QuaternionToEuler(Quaternion q)\n{\n    Vector3 result = { 0 };\n\n    // Roll (x-axis rotation)\n    float x0 = 2.0f*(q.w*q.x + q.y*q.z);\n    float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y);\n    result.x = atan2f(x0, x1);\n\n    // Pitch (y-axis rotation)\n    float y0 = 2.0f*(q.w*q.y - q.z*q.x);\n    y0 = y0 > 1.0f ? 1.0f : y0;\n    y0 = y0 < -1.0f ? -1.0f : y0;\n    result.y = asinf(y0);\n\n    // Yaw (z-axis rotation)\n    float z0 = 2.0f*(q.w*q.z + q.x*q.y);\n    float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z);\n    result.z = atan2f(z0, z1);\n\n    return result;\n}\n\n// Transform a quaternion given a transformation matrix\nRMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat)\n{\n    Quaternion result = { 0 };\n\n    result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w;\n    result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w;\n    result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w;\n    result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w;\n\n    return result;\n}\n\n// Check whether two given quaternions are almost equal\nRMAPI int QuaternionEquals(Quaternion p, Quaternion q)\n{\n#if !defined(EPSILON)\n    #define EPSILON 0.000001f\n#endif\n\n    int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&\n                  ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) &&\n                  ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) &&\n                  ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) ||\n                 (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&\n                  ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) &&\n                  ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) &&\n                  ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w))))));\n\n    return result;\n}\n\n// Decompose a transformation matrix into its rotational, translational and scaling components\nRMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotation, Vector3 *scale)\n{\n    // Extract translation.\n    translation->x = mat.m12;\n    translation->y = mat.m13;\n    translation->z = mat.m14;\n\n    // Extract upper-left for determinant computation\n    const float a = mat.m0;\n    const float b = mat.m4;\n    const float c = mat.m8;\n    const float d = mat.m1;\n    const float e = mat.m5;\n    const float f = mat.m9;\n    const float g = mat.m2;\n    const float h = mat.m6;\n    const float i = mat.m10;\n    const float A = e*i - f*h;\n    const float B = f*g - d*i;\n    const float C = d*h - e*g;\n\n    // Extract scale\n    const float det = a*A + b*B + c*C;\n    Vector3 abc = { a, b, c };\n    Vector3 def = { d, e, f };\n    Vector3 ghi = { g, h, i };\n\n    float scalex = Vector3Length(abc);\n    float scaley = Vector3Length(def);\n    float scalez = Vector3Length(ghi);\n    Vector3 s = { scalex, scaley, scalez };\n\n    if (det < 0) s = Vector3Negate(s);\n\n    *scale = s;\n\n    // Remove scale from the matrix if it is not close to zero\n    Matrix clone = mat;\n    if (!FloatEquals(det, 0))\n    {\n        clone.m0 /= s.x;\n        clone.m4 /= s.x;\n        clone.m8 /= s.x;\n        clone.m1 /= s.y;\n        clone.m5 /= s.y;\n        clone.m9 /= s.y;\n        clone.m2 /= s.z;\n        clone.m6 /= s.z;\n        clone.m10 /= s.z;\n\n        // Extract rotation\n        *rotation = QuaternionFromMatrix(clone);\n    }\n    else\n    {\n        // Set to identity if close to zero\n        *rotation = QuaternionIdentity();\n    }\n}\n\n#if defined(__cplusplus) && !defined(RAYMATH_DISABLE_CPP_OPERATORS)\n\n// Optional C++ math operators\n//-------------------------------------------------------------------------------\n\n// Vector2 operators\nstatic constexpr Vector2 Vector2Zeros = { 0, 0 };\nstatic constexpr Vector2 Vector2Ones = { 1, 1 };\nstatic constexpr Vector2 Vector2UnitX = { 1, 0 };\nstatic constexpr Vector2 Vector2UnitY = { 0, 1 };\n\ninline Vector2 operator + (const Vector2& lhs, const Vector2& rhs)\n{\n    return Vector2Add(lhs, rhs);\n}\n\ninline const Vector2& operator += (Vector2& lhs, const Vector2& rhs)\n{\n    lhs = Vector2Add(lhs, rhs);\n    return lhs;\n}\n\ninline Vector2 operator - (const Vector2& lhs, const Vector2& rhs)\n{\n    return Vector2Subtract(lhs, rhs);\n}\n\ninline const Vector2& operator -= (Vector2& lhs, const Vector2& rhs)\n{\n    lhs = Vector2Subtract(lhs, rhs);\n    return lhs;\n}\n\ninline Vector2 operator * (const Vector2& lhs, const float& rhs)\n{\n    return Vector2Scale(lhs, rhs);\n}\n\ninline const Vector2& operator *= (Vector2& lhs, const float& rhs)\n{\n    lhs = Vector2Scale(lhs, rhs);\n    return lhs;\n}\n\ninline Vector2 operator * (const Vector2& lhs, const Vector2& rhs)\n{\n    return Vector2Multiply(lhs, rhs);\n}\n\ninline const Vector2& operator *= (Vector2& lhs, const Vector2& rhs)\n{\n    lhs = Vector2Multiply(lhs, rhs);\n    return lhs;\n}\n\ninline Vector2 operator * (const Vector2& lhs, const Matrix& rhs)\n{\n    return Vector2Transform(lhs, rhs);\n}\n\ninline const Vector2& operator -= (Vector2& lhs, const Matrix& rhs)\n{\n    lhs = Vector2Transform(lhs, rhs);\n    return lhs;\n}\n\ninline Vector2 operator / (const Vector2& lhs, const float& rhs)\n{\n    return Vector2Scale(lhs, 1.0f / rhs);\n}\n\ninline const Vector2& operator /= (Vector2& lhs, const float& rhs)\n{\n    lhs = Vector2Scale(lhs, rhs);\n    return lhs;\n}\n\ninline Vector2 operator / (const Vector2& lhs, const Vector2& rhs)\n{\n    return Vector2Divide(lhs, rhs);\n}\n\ninline const Vector2& operator /= (Vector2& lhs, const Vector2& rhs)\n{\n    lhs = Vector2Divide(lhs, rhs);\n    return lhs;\n}\n\ninline bool operator == (const Vector2& lhs, const Vector2& rhs)\n{\n    return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y);\n}\n\ninline bool operator != (const Vector2& lhs, const Vector2& rhs)\n{\n    return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y);\n}\n\n// Vector3 operators\nstatic constexpr Vector3 Vector3Zeros = { 0, 0, 0 };\nstatic constexpr Vector3 Vector3Ones = { 1, 1, 1 };\nstatic constexpr Vector3 Vector3UnitX = { 1, 0, 0 };\nstatic constexpr Vector3 Vector3UnitY = { 0, 1, 0 };\nstatic constexpr Vector3 Vector3UnitZ = { 0, 0, 1 };\n\ninline Vector3 operator + (const Vector3& lhs, const Vector3& rhs)\n{\n    return Vector3Add(lhs, rhs);\n}\n\ninline const Vector3& operator += (Vector3& lhs, const Vector3& rhs)\n{\n    lhs = Vector3Add(lhs, rhs);\n    return lhs;\n}\n\ninline Vector3 operator - (const Vector3& lhs, const Vector3& rhs)\n{\n    return Vector3Subtract(lhs, rhs);\n}\n\ninline const Vector3& operator -= (Vector3& lhs, const Vector3& rhs)\n{\n    lhs = Vector3Subtract(lhs, rhs);\n    return lhs;\n}\n\ninline Vector3 operator * (const Vector3& lhs, const float& rhs)\n{\n    return Vector3Scale(lhs, rhs);\n}\n\ninline const Vector3& operator *= (Vector3& lhs, const float& rhs)\n{\n    lhs = Vector3Scale(lhs, rhs);\n    return lhs;\n}\n\ninline Vector3 operator * (const Vector3& lhs, const Vector3& rhs)\n{\n    return Vector3Multiply(lhs, rhs);\n}\n\ninline const Vector3& operator *= (Vector3& lhs, const Vector3& rhs)\n{\n    lhs = Vector3Multiply(lhs, rhs);\n    return lhs;\n}\n\ninline Vector3 operator * (const Vector3& lhs, const Matrix& rhs)\n{\n    return Vector3Transform(lhs, rhs);\n}\n\ninline const Vector3& operator -= (Vector3& lhs, const Matrix& rhs)\n{\n    lhs = Vector3Transform(lhs, rhs);\n    return lhs;\n}\n\ninline Vector3 operator / (const Vector3& lhs, const float& rhs)\n{\n    return Vector3Scale(lhs, 1.0f / rhs);\n}\n\ninline const Vector3& operator /= (Vector3& lhs, const float& rhs)\n{\n    lhs = Vector3Scale(lhs, rhs);\n    return lhs;\n}\n\ninline Vector3 operator / (const Vector3& lhs, const Vector3& rhs)\n{\n    return Vector3Divide(lhs, rhs);\n}\n\ninline const Vector3& operator /= (Vector3& lhs, const Vector3& rhs)\n{\n    lhs = Vector3Divide(lhs, rhs);\n    return lhs;\n}\n\ninline bool operator == (const Vector3& lhs, const Vector3& rhs)\n{\n    return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y) && FloatEquals(lhs.z, rhs.z);\n}\n\ninline bool operator != (const Vector3& lhs, const Vector3& rhs)\n{\n    return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y) || !FloatEquals(lhs.z, rhs.z);\n}\n\n// Vector4 operators\nstatic constexpr Vector4 Vector4Zeros = { 0, 0, 0, 0 };\nstatic constexpr Vector4 Vector4Ones = { 1, 1, 1, 1 };\nstatic constexpr Vector4 Vector4UnitX = { 1, 0, 0, 0 };\nstatic constexpr Vector4 Vector4UnitY = { 0, 1, 0, 0 };\nstatic constexpr Vector4 Vector4UnitZ = { 0, 0, 1, 0 };\nstatic constexpr Vector4 Vector4UnitW = { 0, 0, 0, 1 };\n\ninline Vector4 operator + (const Vector4& lhs, const Vector4& rhs)\n{\n    return Vector4Add(lhs, rhs);\n}\n\ninline const Vector4& operator += (Vector4& lhs, const Vector4& rhs)\n{\n    lhs = Vector4Add(lhs, rhs);\n    return lhs;\n}\n\ninline Vector4 operator - (const Vector4& lhs, const Vector4& rhs)\n{\n    return Vector4Subtract(lhs, rhs);\n}\n\ninline const Vector4& operator -= (Vector4& lhs, const Vector4& rhs)\n{\n    lhs = Vector4Subtract(lhs, rhs);\n    return lhs;\n}\n\ninline Vector4 operator * (const Vector4& lhs, const float& rhs)\n{\n    return Vector4Scale(lhs, rhs);\n}\n\ninline const Vector4& operator *= (Vector4& lhs, const float& rhs)\n{\n    lhs = Vector4Scale(lhs, rhs);\n    return lhs;\n}\n\ninline Vector4 operator * (const Vector4& lhs, const Vector4& rhs)\n{\n    return Vector4Multiply(lhs, rhs);\n}\n\ninline const Vector4& operator *= (Vector4& lhs, const Vector4& rhs)\n{\n    lhs = Vector4Multiply(lhs, rhs);\n    return lhs;\n}\n\ninline Vector4 operator / (const Vector4& lhs, const float& rhs)\n{\n    return Vector4Scale(lhs, 1.0f / rhs);\n}\n\ninline const Vector4& operator /= (Vector4& lhs, const float& rhs)\n{\n    lhs = Vector4Scale(lhs, rhs);\n    return lhs;\n}\n\ninline Vector4 operator / (const Vector4& lhs, const Vector4& rhs)\n{\n    return Vector4Divide(lhs, rhs);\n}\n\ninline const Vector4& operator /= (Vector4& lhs, const Vector4& rhs)\n{\n    lhs = Vector4Divide(lhs, rhs);\n    return lhs;\n}\n\ninline bool operator == (const Vector4& lhs, const Vector4& rhs)\n{\n    return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y) && FloatEquals(lhs.z, rhs.z) && FloatEquals(lhs.w, rhs.w);\n}\n\ninline bool operator != (const Vector4& lhs, const Vector4& rhs)\n{\n    return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y) || !FloatEquals(lhs.z, rhs.z) || !FloatEquals(lhs.w, rhs.w);\n}\n\n// Quaternion operators\nstatic constexpr Quaternion QuaternionZeros = { 0, 0, 0, 0 };\nstatic constexpr Quaternion QuaternionOnes = { 1, 1, 1, 1 };\nstatic constexpr Quaternion QuaternionUnitX = { 0, 0, 0, 1 };\n\ninline Quaternion operator + (const Quaternion& lhs, const float& rhs)\n{\n    return QuaternionAddValue(lhs, rhs);\n}\n\ninline const Quaternion& operator += (Quaternion& lhs, const float& rhs)\n{\n    lhs = QuaternionAddValue(lhs, rhs);\n    return lhs;\n}\n\ninline Quaternion operator - (const Quaternion& lhs, const float& rhs)\n{\n    return QuaternionSubtractValue(lhs, rhs);\n}\n\ninline const Quaternion& operator -= (Quaternion& lhs, const float& rhs)\n{\n    lhs = QuaternionSubtractValue(lhs, rhs);\n    return lhs;\n}\n\ninline Quaternion operator * (const Quaternion& lhs, const Matrix& rhs)\n{\n    return QuaternionTransform(lhs, rhs);\n}\n\ninline const Quaternion& operator *= (Quaternion& lhs, const Matrix& rhs)\n{\n    lhs = QuaternionTransform(lhs, rhs);\n    return lhs;\n}\n\n// Matrix operators\ninline Matrix operator + (const Matrix& lhs, const Matrix& rhs)\n{\n    return MatrixAdd(lhs, rhs);\n}\n\ninline const Matrix& operator += (Matrix& lhs, const Matrix& rhs)\n{\n    lhs = MatrixAdd(lhs, rhs);\n    return lhs;\n}\n\ninline Matrix operator - (const Matrix& lhs, const Matrix& rhs)\n{\n    return MatrixSubtract(lhs, rhs);\n}\n\ninline const Matrix& operator -= (Matrix& lhs, const Matrix& rhs)\n{\n    lhs = MatrixSubtract(lhs, rhs);\n    return lhs;\n}\n\ninline Matrix operator * (const Matrix& lhs, const Matrix& rhs)\n{\n    return MatrixMultiply(lhs, rhs);\n}\n\ninline const Matrix& operator *= (Matrix& lhs, const Matrix& rhs)\n{\n    lhs = MatrixMultiply(lhs, rhs);\n    return lhs;\n}\n//-------------------------------------------------------------------------------\n#endif  // C++ operators\n\n#endif  // RAYMATH_H\n"
  },
  {
    "path": "renderers/sokol/sokol_clay.h",
    "content": "#ifndef SOKOL_CLAY_INCLUDED\n#define SOKOL_CLAY_INCLUDED (1)\n/*\n    sokol_clay.h -- drop-in Clay renderer for sokol_gfx.h\n\n    Do this:\n        #define SOKOL_CLAY_IMPL\n\n    before you include this file in *one* C file to create the\n    implementation.\n\n    Optionally provide the following configuration define both before including the\n    the declaration and implementation:\n\n    SOKOL_CLAY_NO_SOKOL_APP    - don't depend on sokol_app.h (see below for details)\n\n    Include the following headers before sokol_clay.h (both before including\n    the declaration and implementation):\n\n        sokol_gl.h\n        sokol_fontstash.h\n        sokol_app.h         (except SOKOL_CLAY_NO_SOKOL_APP)\n        clay.h\n\n    FEATURE OVERVIEW:\n    =================\n    sokol_clay.h implements the rendering and event-handling code for Clay\n    (https://github.com/nicbarker/clay) on top of sokol_gl.h and (optionally)\n    sokol_app.h.\n\n    Since sokol_fontstash.h already depends on sokol_gl.h, the rendering is\n    implemented using sokol_gl calls. (TODO: make fontstash optional?)\n\n    The sokol_app.h dependency is optional and used for input event handling.\n    If you only use sokol_gfx.h but not sokol_app.h in your application,\n    define SOKOL_CLAY_NO_SOKOL_APP before including the implementation\n    of sokol_clay.h, this will remove any dependency to sokol_app.h, but\n    you must call sclay_set_layout_dimensions and handle input yourself.\n\n    sokol_clay.h is not thread-safe, all calls must be made from the\n    same thread where sokol_gfx.h is running.\n\n    HOWTO:\n    ======\n\n    --- To initialize sokol-clay, call sclay_setup(). This can be done\n        before or after Clay_Initialize.\n\n    --- Create an array of sclay_font_t and fill it by calling one of:\n\n            sclay_font_t sclay_add_font(const char *filename);\n            sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen);\n\n        The fontId value in Clay corresponds to indices in this array. After calling\n        Clay_Initialize but before calling any layout code, do this:\n\n            Clay_SetMeasureTextFunction(sclay_measure_text, &fonts);\n\n        where `fonts` is the abovementioned array.\n\n    --- At the start of a frame, call sclay_new_frame() if you're using sokol_app.h.\n        If you're not using sokol_app.h, call:\n \n            void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale);\n\n        at the start of the frame (or just when the window is resized.)\n\n        Either way, do some layout, then at the end of the frame call sclay_render:\n\n            sg_begin_pass(...)\n            // other rendering...\n            sclay_render(renderCommands, &fonts);\n            // other rendering...\n            sgl_draw();\n            sg_end_pass();\n            sg_commit();\n\n        One caveat: sclay_render assumes the default gl view matrix, and handles scaling\n        automatically. If you've adjusted the view matrix, remember to first call:\n\n            sgl_matrix_mode_modelview();\n            sgl_load_identity();\n\n        before calling sclay_render.\n\n    --- if you're using sokol_app.h, from inside the sokol_app.h event callback,\n        call:\n\n            void sclay_handle_event(const sapp_event* ev);\n\n        Unfortunately Clay does not currently provide feedback on whether a mouse\n        click was handled or not.\n\n    --- if you want to use images with clay, you should pass a pointer to a\n        sclay_image to the CLAY macro, like this:\n                CLAY({\n                   ...\n                   .image = { .imageData = &(sclay_image){ .view = view, .sampler = 0 } },\n                })\n        Using 0 as a sampler uses the sokol default sampler with linear interpolation.\n        The image should be created using sg_make_image from sokol_gfx.\n\n    --- finally, on application shutdown, call\n\n            sclay_shutdown()\n */\n#if !defined(SOKOL_CLAY_NO_SOKOL_APP) && !defined(SOKOL_APP_INCLUDED)\n#error \"Please include sokol_app.h before sokol_clay.h (or define SOKOL_CLAY_NO_SOKOL_APP)\"\n#endif\n\ntypedef int sclay_font_t;\n\ntypedef struct sclay_image {\n    sg_view view;\n    sg_sampler sampler;\n    struct {\n        float u0, v0, u1, v1;\n    } uv;\n} sclay_image;\n\n\nvoid sclay_setup();\nvoid sclay_shutdown();\n\nsclay_font_t sclay_add_font(const char *filename);\nsclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen);\nClay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);\n\n#ifndef SOKOL_CLAY_NO_SOKOL_APP\nvoid sclay_new_frame();\nvoid sclay_handle_event(const sapp_event *ev);\n#endif  /* SOKOL_CLAY_NO_SOKOL_APP */\n\n/* Use this if you don't call sclay_new_frame. `size` is the \"virtual\" size which\n * your layout is relative to (ie. the actual framebuffer size divided by dpi_scale.)\n * Set dpi_scale to 1 if you're not using high-dpi support. */\nvoid sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale);\n\nvoid sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts);\n\n#endif /* SOKOL_CLAY_INCLUDED */\n\n#ifdef SOKOL_CLAY_IMPL\n#define SOKOL_CLAY_IMPL_INCLUDED (1)\n#ifndef SOKOL_GL_INCLUDED\n#error \"Please include sokol_gl.h before sokol_clay.h\"\n#endif\n#ifndef SOKOL_FONTSTASH_INCLUDED\n#error \"Please include sokol_fontstash.h before sokol_clay.h\"\n#endif\n#ifndef CLAY_HEADER\n#error \"Please include clay.h before sokol_clay.h\"\n#endif\n\ntypedef struct {\n    sgl_pipeline pip;\n#ifndef SOKOL_CLAY_NO_SOKOL_APP\n    Clay_Vector2 mouse_pos, scroll;\n    bool mouse_down;\n#endif\n    Clay_Dimensions size;\n    float dpi_scale;\n    FONScontext *fonts;\n} _sclay_state_t;\nstatic _sclay_state_t _sclay;\n\nvoid sclay_setup() {\n    _sclay.pip = sgl_make_pipeline(&(sg_pipeline_desc){\n        .colors[0] = {\n            .blend = {\n                .enabled = true,\n                .src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,\n                .dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,\n            },\n        }\n    });\n#ifndef SOKOL_CLAY_NO_SOKOL_APP\n    _sclay.mouse_pos = (Clay_Vector2){0, 0};\n    _sclay.scroll = (Clay_Vector2){0, 0};\n    _sclay.mouse_down = false;\n#endif\n    _sclay.size = (Clay_Dimensions){1, 1};\n    _sclay.dpi_scale = 1;\n    _sclay.fonts = sfons_create(&(sfons_desc_t){ 0 });\n    //TODO clay error handler?\n}\n\nvoid sclay_shutdown() {\n    sgl_destroy_pipeline(_sclay.pip);\n    sfons_destroy(_sclay.fonts);\n}\n\n#ifndef SOKOL_CLAY_NO_SOKOL_APP\nvoid sclay_handle_event(const sapp_event* ev) {\n    switch(ev->type){\n    case SAPP_EVENTTYPE_MOUSE_MOVE:\n        _sclay.mouse_pos.x = ev->mouse_x / _sclay.dpi_scale;\n        _sclay.mouse_pos.y = ev->mouse_y / _sclay.dpi_scale;\n        break;\n    case SAPP_EVENTTYPE_MOUSE_DOWN:\n        _sclay.mouse_down = true;\n        break;\n    case SAPP_EVENTTYPE_MOUSE_UP:\n        _sclay.mouse_down = false;\n        break;\n    case SAPP_EVENTTYPE_MOUSE_SCROLL:\n        _sclay.scroll.x += ev->scroll_x;\n        _sclay.scroll.y += ev->scroll_y;\n        break;\n    default: break;\n    }\n}\n\nvoid sclay_new_frame() {\n    sclay_set_layout_dimensions((Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() },\n                                sapp_dpi_scale());\n    Clay_SetPointerState(_sclay.mouse_pos, _sclay.mouse_down);\n    Clay_UpdateScrollContainers(true, _sclay.scroll, sapp_frame_duration());\n    _sclay.scroll = (Clay_Vector2){0, 0};\n}\n#endif  /* SOKOL_CLAY_NO_SOKOL_APP */\n\nvoid sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale) {\n    size.width /= dpi_scale;\n    size.height /= dpi_scale;\n    _sclay.size = size;\n    if(_sclay.dpi_scale != dpi_scale){\n        _sclay.dpi_scale = dpi_scale;\n        Clay_ResetMeasureTextCache();\n    }\n    Clay_SetLayoutDimensions(size);\n}\n\nsclay_font_t sclay_add_font(const char *filename) {\n    //TODO log something if we get FONS_INVALID\n    return fonsAddFont(_sclay.fonts, \"\", filename);\n}\n\nsclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen) {\n    //TODO log something if we get FONS_INVALID\n    return fonsAddFontMem(_sclay.fonts, \"\", data, dataLen, false);\n}\n\nClay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {\n    sclay_font_t *fonts = (sclay_font_t *)userData;\n    if(!fonts) return (Clay_Dimensions){ 0 };\n    fonsSetFont(_sclay.fonts, fonts[config->fontId]);\n    fonsSetSize(_sclay.fonts, config->fontSize * _sclay.dpi_scale);\n    fonsSetSpacing(_sclay.fonts, config->letterSpacing * _sclay.dpi_scale);\n    fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP);\n    float ascent, descent, lineh;\n    fonsVertMetrics(_sclay.fonts, &ascent, &descent, &lineh);\n    return (Clay_Dimensions) {\n        .width = fonsTextBounds(_sclay.fonts, 0, 0, text.chars, text.chars + text.length, NULL) / _sclay.dpi_scale,\n        .height = (ascent - descent) / _sclay.dpi_scale\n    };\n}\n\nstatic void _draw_rect(float x, float y, float w, float h){\n    sgl_v2f(x, y);\n    sgl_v2f(x, y);\n    sgl_v2f(x+w, y);\n    sgl_v2f(x, y+h);\n    sgl_v2f(x+w, y+h);\n    sgl_v2f(x+w, y+h);\n}\n\nstatic void _draw_rect_textured(float x, float y, float w, float h, float u0, float v0, float u1, float v1){\n    sgl_v2f_t2f(x, y, u0, v0);\n    sgl_v2f_t2f(x, y, u0, v0);\n    sgl_v2f_t2f(x+w, y, u1, v0);\n    sgl_v2f_t2f(x, y+h, u0, v1);\n    sgl_v2f_t2f(x+w, y+h, u1, v1);\n    sgl_v2f_t2f(x+w, y+h, u1, v1);\n}\n\nstatic float _SIN[16] = {\n    0.000000f, 0.104528f, 0.207912f, 0.309017f,\n    0.406737f, 0.500000f, 0.587785f, 0.669131f,\n    0.743145f, 0.809017f, 0.866025f, 0.913545f,\n    0.951057f, 0.978148f, 0.994522f, 1.000000f,\n};\n\n/* rx,ry = radius */\nstatic void _draw_corner(float x, float y, float rx, float ry){\n    x -= rx;\n    y -= ry;\n    sgl_v2f(x, y);\n    for(int i = 0; i < 16; ++i){\n        sgl_v2f(x, y);\n        sgl_v2f(x+(rx*_SIN[15-i]), y+(ry*_SIN[i]));\n    }\n    sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]));\n}\n\nstatic void _draw_corner_textured(float x, float y, float rx, float ry, float bx, float by, float bw, float bh, float u0, float v0, float u1, float v1) {\n    x -= rx;\n    y -= ry;\n#define MAP_U(x) (u0+(((x)-bx)/bw)*(u1-u0))\n#define MAP_V(y) (v0+(((y)-by)/bh)*(v1-v0))\n    sgl_v2f_t2f(x, y, MAP_U(x), MAP_V(y));\n    for(int i = 0; i < 16; ++i){\n        sgl_v2f_t2f(x, y, MAP_U(x), MAP_V(y));\n        float px = x+(rx*_SIN[15-i]);\n        float py = y+(ry*_SIN[i]);\n        sgl_v2f_t2f(px, py, MAP_U(px), MAP_V(py));\n    }\n    sgl_v2f_t2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]), MAP_U(x+(rx*_SIN[0])), MAP_V(y+(ry*_SIN[15])));\n#undef MAP_U\n#undef MAP_V\n}\n\n/* rx,ry = radius   ix,iy = inner radius */\nstatic void _draw_corner_border(float x, float y, float rx, float ry, float ix, float iy){\n    x -= rx;\n    y -= ry;\n    sgl_v2f(x+(ix*_SIN[15]), y+(iy*_SIN[0]));\n    for(int i = 0; i < 16; ++i){\n        sgl_v2f(x+(ix*_SIN[15-i]), y+(iy*_SIN[i]));\n        sgl_v2f(x+(rx*_SIN[15-i]), y+(ry*_SIN[i]));\n    }\n    sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]));\n}\n\nvoid sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts) {\n    sgl_matrix_mode_modelview();\n    sgl_translate(-1.0f, 1.0f, 0.0f);\n    sgl_scale(2.0f/_sclay.size.width, -2.0f/_sclay.size.height, 1.0f);\n    sgl_disable_texture();\n    sgl_push_pipeline();\n    sgl_load_pipeline(_sclay.pip);\n    for (uint32_t i = 0; i < renderCommands.length; i++) {\n        Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i);\n        Clay_BoundingBox bbox = renderCommand->boundingBox;\n        switch (renderCommand->commandType) {\n            case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;\n                sgl_c4f(config->backgroundColor.r / 255.0f,\n                        config->backgroundColor.g / 255.0f,\n                        config->backgroundColor.b / 255.0f,\n                        config->backgroundColor.a / 255.0f);\n                Clay_CornerRadius r = config->cornerRadius;\n                sgl_begin_triangle_strip();\n                if(r.topLeft > 0 || r.topRight > 0){\n                    _draw_corner(bbox.x, bbox.y, -r.topLeft, -r.topLeft);\n                    _draw_corner(bbox.x+bbox.width, bbox.y, r.topRight, -r.topRight);\n                    _draw_rect(bbox.x+r.topLeft, bbox.y,\n                               bbox.width-r.topLeft-r.topRight, CLAY__MAX(r.topLeft, r.topRight));\n                }\n                if(r.bottomLeft > 0 || r.bottomRight > 0){\n                    _draw_corner(bbox.x, bbox.y+bbox.height, -r.bottomLeft, r.bottomLeft);\n                    _draw_corner(bbox.x+bbox.width, bbox.y+bbox.height, r.bottomRight, r.bottomRight);\n                    _draw_rect(bbox.x+r.bottomLeft,\n                               bbox.y+bbox.height-CLAY__MAX(r.bottomLeft, r.bottomRight),\n                               bbox.width-r.bottomLeft-r.bottomRight, CLAY__MAX(r.bottomLeft, r.bottomRight));\n                }\n                if(r.topLeft < r.bottomLeft){\n                    if(r.topLeft < r.topRight){\n                        _draw_rect(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft);\n                        _draw_rect(bbox.x+r.topLeft, bbox.y+r.topRight,\n                                   r.bottomLeft-r.topLeft, bbox.height-r.topRight-r.bottomLeft);\n                    } else {\n                        _draw_rect(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft);\n                    }\n                } else {\n                    if(r.bottomLeft < r.bottomRight){\n                        _draw_rect(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft);\n                        _draw_rect(bbox.x+r.bottomLeft, bbox.y+r.topLeft,\n                                   r.topLeft-r.bottomLeft, bbox.height-r.topLeft-r.bottomRight);\n                    } else {\n                        _draw_rect(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft);\n                    }\n                }\n                if(r.topRight < r.bottomRight){\n                    if(r.topRight < r.topLeft){\n                        _draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topLeft,\n                                   r.bottomRight-r.topRight, bbox.height-r.topLeft-r.bottomRight);\n                        _draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,\n                                   r.topRight, bbox.height-r.topRight-r.bottomRight);\n                    } else {\n                        _draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,\n                                   r.bottomRight, bbox.height-r.topRight-r.bottomRight);\n                    }\n                } else {\n                    if(r.bottomRight < r.bottomLeft){\n                        _draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,\n                                   r.topRight-r.bottomRight, bbox.height-r.topRight-r.bottomLeft);\n                        _draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,\n                                   r.bottomRight, bbox.height-r.topRight-r.bottomRight);\n                    } else {\n                        _draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,\n                                   r.topRight, bbox.height-r.topRight-r.bottomRight);\n                    }\n                }\n                _draw_rect(bbox.x+CLAY__MAX(r.topLeft, r.bottomLeft),\n                           bbox.y+CLAY__MAX(r.topLeft, r.topRight),\n                           bbox.width-CLAY__MAX(r.topLeft, r.bottomLeft)-CLAY__MAX(r.topRight, r.bottomRight),\n                           bbox.height-CLAY__MAX(r.topLeft, r.topRight)-CLAY__MAX(r.bottomLeft, r.bottomRight));\n                sgl_end();\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_TEXT: {\n                if(!fonts) break;\n                Clay_TextRenderData *config = &renderCommand->renderData.text;\n                Clay_StringSlice text = config->stringContents;\n                fonsSetFont(_sclay.fonts, fonts[config->fontId]);\n                uint32_t color = sfons_rgba(\n                        config->textColor.r,\n                        config->textColor.g,\n                        config->textColor.b,\n                        config->textColor.a);\n                fonsSetColor(_sclay.fonts, color);\n                fonsSetSpacing(_sclay.fonts, config->letterSpacing * _sclay.dpi_scale);\n                fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP);\n                fonsSetSize(_sclay.fonts, config->fontSize * _sclay.dpi_scale);\n                sgl_matrix_mode_modelview();\n                sgl_push_matrix();\n                sgl_scale(1.0f/_sclay.dpi_scale, 1.0f/_sclay.dpi_scale, 1.0f);\n                fonsDrawText(_sclay.fonts, bbox.x*_sclay.dpi_scale, bbox.y*_sclay.dpi_scale,\n                             text.chars, text.chars + text.length);\n                sgl_pop_matrix();\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {\n                sgl_scissor_rectf(bbox.x*_sclay.dpi_scale, bbox.y*_sclay.dpi_scale,\n                                  bbox.width*_sclay.dpi_scale, bbox.height*_sclay.dpi_scale,\n                                  true);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {\n                sgl_scissor_rectf(0, 0,\n                                  _sclay.size.width*_sclay.dpi_scale, _sclay.size.height*_sclay.dpi_scale,\n                                  true);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_IMAGE: {\n                Clay_ImageRenderData *config = &renderCommand->renderData.image;\n                sclay_image* img = (sclay_image*)config->imageData;\n                // by default, u1 and v1 are 1. if we pass 0.\n                // note, we are modifying a copy !\n                float u0 = img->uv.u0;\n                float v0 = img->uv.v0;\n                float u1 = img->uv.u1;\n                float v1 = img->uv.v1;\n                if (u1 == 0.f) {\n                    u1 = 1.f;\n                }\n                if (v1 == 0.f) {\n                    v1 = 1.f;\n                }\n\n                int untinted = config->backgroundColor.r == 0 && config->backgroundColor.g == 0 && config->backgroundColor.b == 0 && config->backgroundColor.a == 0;\n                float cr = untinted ? 1.f : (config->backgroundColor.r / 255.0f);\n                float gr = untinted ? 1.f : (config->backgroundColor.g / 255.0f);\n                float br = untinted ? 1.f : (config->backgroundColor.b / 255.0f);\n                float ar = untinted ? 1.f : (config->backgroundColor.a / 255.0f);\n\n                sgl_c4f(cr, gr, br, ar);\n\n                Clay_CornerRadius r = config->cornerRadius;\n\n                sgl_enable_texture();\n                sgl_texture(img->view, img->sampler);\n\n                sgl_begin_triangle_strip();\n                if(r.topLeft > 0 || r.topRight > 0){\n                    _draw_corner_textured(bbox.x, bbox.y, -r.topLeft, -r.topLeft, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1);\n                    _draw_corner_textured(bbox.x+bbox.width, bbox.y, r.topRight, -r.topRight, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1);\n                    _draw_rect_textured(bbox.x+r.topLeft, bbox.y,\n                               bbox.width-r.topLeft-r.topRight, CLAY__MAX(r.topLeft, r.topRight),\n                               u0 + (r.topLeft/bbox.width)*(u1-u0), v0, u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (CLAY__MAX(r.topLeft, r.topRight)/bbox.height)*(v1-v0));\n                }\n                if(r.bottomLeft > 0 || r.bottomRight > 0){\n                    _draw_corner_textured(bbox.x, bbox.y+bbox.height, -r.bottomLeft, r.bottomLeft, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1);\n                    _draw_corner_textured(bbox.x+bbox.width, bbox.y+bbox.height, r.bottomRight, r.bottomRight, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1);\n                    _draw_rect_textured(bbox.x+r.bottomLeft,\n                               bbox.y+bbox.height-CLAY__MAX(r.bottomLeft, r.bottomRight),\n                               bbox.width-r.bottomLeft-r.bottomRight, CLAY__MAX(r.bottomLeft, r.bottomRight),\n                               u0 + (r.bottomLeft/bbox.width)*(u1-u0), v1 - (CLAY__MAX(r.bottomLeft, r.bottomRight)/bbox.height)*(v1-v0), u1 - (r.bottomRight/bbox.width)*(u1-u0), v1);\n                }\n                if(r.topLeft < r.bottomLeft){\n                    if(r.topLeft < r.topRight){\n                        _draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft,\n                                u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));\n                        _draw_rect_textured(bbox.x+r.topLeft, bbox.y+r.topRight,\n                               r.bottomLeft-r.topLeft, bbox.height-r.topRight-r.bottomLeft,\n                               u0 + (r.topLeft/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));\n                    } else {\n                        _draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft,\n                                u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.bottomLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));\n                    }\n                } else {\n                    if(r.bottomLeft < r.bottomRight){\n                        _draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft,\n                               u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.bottomLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));\n                        _draw_rect_textured(bbox.x+r.bottomLeft, bbox.y+r.topLeft,\n                                   r.topLeft-r.bottomLeft, bbox.height-r.topLeft-r.bottomRight,\n                                   u0 + (r.bottomLeft/bbox.width)*(u1-u0), v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomRight/bbox.height)*(v1-v0));\n                    } else {\n                        _draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft,\n                                u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));\n                    }\n                }\n                if(r.topRight < r.bottomRight){\n                    if(r.topRight < r.topLeft){\n                        _draw_rect_textured(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topLeft,\n                                   r.bottomRight-r.topRight, bbox.height-r.topLeft-r.bottomRight,\n                                   u1 - (r.bottomRight/bbox.width)*(u1-u0), v0 + (r.topLeft/bbox.height)*(v1-v0), u1 - (r.topRight/bbox.width)*(u1-u0), v1 - (r.bottomRight/bbox.height)*(v1-v0));\n                        _draw_rect_textured(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,\n                                   r.topRight, bbox.height-r.topRight-r.bottomRight,\n                                   u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0));\n                    } else {\n                        _draw_rect_textured(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,\n                                   r.bottomRight, bbox.height-r.topRight-r.bottomRight,\n                                   u1 - (r.bottomRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0));\n                    }\n                } else {\n                    if(r.bottomRight < r.bottomLeft){\n                        _draw_rect_textured(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,\n                                   r.topRight-r.bottomRight, bbox.height-r.topRight-r.bottomLeft,\n                                   u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1 - (r.bottomRight/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));\n                        _draw_rect_textured(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,\n                                   r.bottomRight, bbox.height-r.topRight-r.bottomRight,\n                                   u1 - (r.bottomRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0));\n                    } else {\n                        _draw_rect_textured(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,\n                                   r.topRight, bbox.height-r.topRight-r.bottomRight,\n                                   u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0));\n                    }\n                }\n                _draw_rect_textured(bbox.x+CLAY__MAX(r.topLeft, r.bottomLeft),\n                           bbox.y+CLAY__MAX(r.topLeft, r.topRight),\n                           bbox.width-CLAY__MAX(r.topLeft, r.bottomLeft)-CLAY__MAX(r.topRight, r.bottomRight),\n                           bbox.height-CLAY__MAX(r.topLeft, r.topRight)-CLAY__MAX(r.bottomLeft, r.bottomRight),\n                           u0+CLAY__MAX(r.topLeft,r.bottomLeft)/bbox.width*(u1-u0), v0+CLAY__MAX(r.topLeft,r.topRight)/bbox.height*(v1-v0),\n                           u1-CLAY__MAX(r.topRight,r.bottomRight)/bbox.width*(u1-u0), v1-CLAY__MAX(r.bottomLeft,r.bottomRight)/bbox.height*(v1-v0));\n                sgl_end();\n                sgl_disable_texture();\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_BORDER: {\n                Clay_BorderRenderData *config = &renderCommand->renderData.border;\n                sgl_c4f(config->color.r / 255.0f,\n                        config->color.g / 255.0f,\n                        config->color.b / 255.0f,\n                        config->color.a / 255.0f);\n                Clay_BorderWidth w = config->width;\n                Clay_CornerRadius r = config->cornerRadius;\n                sgl_begin_triangle_strip();\n                if(w.left > 0){\n                    _draw_rect(bbox.x, bbox.y + r.topLeft,\n                               w.left, bbox.height - r.topLeft - r.bottomLeft);\n                }\n                if(w.right > 0){\n                    _draw_rect(bbox.x + bbox.width - w.right, bbox.y + r.topRight,\n                               w.right, bbox.height - r.topRight - r.bottomRight);\n                }\n                if(w.top > 0){\n                    _draw_rect(bbox.x + r.topLeft, bbox.y,\n                               bbox.width - r.topLeft - r.topRight, w.top);\n                }\n                if(w.bottom > 0){\n                    _draw_rect(bbox.x + r.bottomLeft, bbox.y + bbox.height - w.bottom,\n                               bbox.width - r.bottomLeft - r.bottomRight, w.bottom);\n                }\n                if(r.topLeft > 0 && (w.top > 0 || w.left > 0)){\n                    _draw_corner_border(bbox.x, bbox.y,\n                                        -r.topLeft, -r.topLeft,\n                                        -r.topLeft+w.left, -r.topLeft+w.top);\n                }\n                if(r.topRight > 0 && (w.top > 0 || w.right > 0)){\n                    _draw_corner_border(bbox.x+bbox.width, bbox.y,\n                                        r.topRight, -r.topRight,\n                                        r.topRight-w.right, -r.topRight+w.top);\n                }\n                if(r.bottomLeft > 0 && (w.bottom > 0 || w.left > 0)){\n                    _draw_corner_border(bbox.x, bbox.y+bbox.height,\n                                        -r.bottomLeft, r.bottomLeft,\n                                        -r.bottomLeft+w.left, r.bottomLeft-w.bottom);\n                }\n                if(r.bottomRight > 0 && (w.bottom > 0 || w.right > 0)){\n                    _draw_corner_border(bbox.x+bbox.width, bbox.y+bbox.height,\n                                        r.bottomRight, r.bottomRight,\n                                        r.bottomRight-w.right, r.bottomRight-w.bottom);\n                }\n                sgl_end();\n                break;\n            }\n            default:\n                break;\n        }\n    }\n    sgl_pop_pipeline();\n    sfons_flush(_sclay.fonts);\n}\n#endif /* SOKOL_CLAY_IMPL */\n"
  },
  {
    "path": "renderers/termbox2/clay_renderer_termbox2.c",
    "content": "/*\n    zlib/libpng license\n\n    Copyright (c) 2025 Mivirl\n\n    altered by Godje (Sep 2025)\n\n    This software is provided 'as-is', without any express or implied warranty.\n    In no event will the authors be held liable for any damages arising from the\n    use of this software.\n\n    Permission is granted to anyone to use this software for any purpose,\n    including commercial applications, and to alter it and redistribute it\n    freely, subject to the following restrictions:\n\n        1. The origin of this software must not be misrepresented; you must not\n        claim that you wrote the original software. If you use this software in a\n        product, an acknowledgment in the product documentation would be\n        appreciated but is not required.\n\n        2. Altered source versions must be plainly marked as such, and must not\n        be misrepresented as being the original software.\n\n        3. This notice may not be removed or altered from any source\n        distribution.\n*/\n\n#include \"../../clay.h\"\n\n#include \"image_character_masks.h\"\n\n#define TB_OPT_ATTR_W 32 // Required for truecolor support\n#include \"termbox2.h\"\n\n#include \"stb_image.h\"\n#include \"stb_image_resize2.h\"\n\n// -------------------------------------------------------------------------------------------------\n// -- Data structures\n\ntypedef struct {\n    int width, height;\n} clay_tb_dimensions;\n\ntypedef struct {\n    float width, height;\n} clay_tb_pixel_dimensions;\n\ntypedef struct {\n    int x, y;\n    int width, height;\n} clay_tb_cell_bounding_box;\n\ntypedef struct {\n    Clay_Color clay;\n    uintattr_t termbox;\n} clay_tb_color_pair;\n\nenum border_mode {\n    CLAY_TB_BORDER_MODE_DEFAULT,\n    CLAY_TB_BORDER_MODE_ROUND,\n    CLAY_TB_BORDER_MODE_MINIMUM,\n};\n\nenum border_chars {\n    CLAY_TB_BORDER_CHARS_DEFAULT,\n    CLAY_TB_BORDER_CHARS_ASCII,\n    CLAY_TB_BORDER_CHARS_UNICODE,\n    CLAY_TB_BORDER_CHARS_BLANK,\n    CLAY_TB_BORDER_CHARS_NONE,\n};\n\nenum image_mode {\n    CLAY_TB_IMAGE_MODE_DEFAULT,\n    CLAY_TB_IMAGE_MODE_PLACEHOLDER,\n    CLAY_TB_IMAGE_MODE_BG,\n    CLAY_TB_IMAGE_MODE_ASCII_FG,\n    CLAY_TB_IMAGE_MODE_ASCII_FG_FAST,\n    CLAY_TB_IMAGE_MODE_ASCII,\n    CLAY_TB_IMAGE_MODE_ASCII_FAST,\n    CLAY_TB_IMAGE_MODE_UNICODE,\n    CLAY_TB_IMAGE_MODE_UNICODE_FAST,\n};\n\ntypedef struct {\n    // Stores information about image loaded from stb\n    int pixel_width, pixel_height;\n    unsigned char *pixel_data;\n\n    // Internal cached data from previous renders\n    struct {\n        enum image_mode last_image_mode;\n        int width, height;\n        size_t size_max;\n        uint32_t *characters;\n        Clay_Color *foreground;\n        Clay_Color *background;\n\n        // Data storing progress of partially complete image conversions that take multiple renders\n        struct clay_tb_partial_render {\n            bool in_progress;\n            unsigned char *resized_pixel_data;\n            int cursor_x, cursor_y;\n            int cursor_mask;\n            int min_difference_squared_sum;\n            int best_mask;\n            Clay_Color best_foreground, best_background;\n        } partial_render;\n    } internal;\n} clay_tb_image;\n\n// Truecolor is only enabled if TB_OPT_ATTR_W is set to 32 or 64. The default is 16, so it must be\n// defined to reference the constant\n#ifndef TB_OUTPUT_TRUECOLOR\n#define TB_OUTPUT_TRUECOLOR (TB_OUTPUT_GRAYSCALE + 1)\n#endif\n\n// Constant that doesn't collide with termbox2's existing output modes\n#define CLAY_TB_OUTPUT_NOCOLOR 0\n\n#if !(defined NDEBUG || defined CLAY_TB_NDEBUG)\n#define clay_tb_assert(condition, ...)                                                    \\\n    if (!(condition)) {                                                                   \\\n        Clay_Termbox_Close();                                                             \\\n        fprintf(stderr, \"%s %d (%s): Assertion failure: \", __FILE__, __LINE__, __func__); \\\n        fprintf(stderr, __VA_ARGS__);                                                     \\\n        fprintf(stderr, \"\\n\");                                                            \\\n        exit(1);                                                                          \\\n    }\n#else\n#define clay_tb_assert(condition, ...)\n#endif // NDEBUG || CLAY_TB_NDEBUG\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Public API\n\n/**\n  Set the equivalent size for a terminal cell in pixels.\n\n  This size is used to convert Clay's pixel measurements to terminal cells, and\n  affects scaling.\n\n  Default dimensions were measured on Debian 12: (9, 21)\n\n  \\param width Width of a terminal cell in pixels\n  \\param height Height of a terminal cell in pixels\n*/\nvoid Clay_Termbox_Set_Cell_Pixel_Size(float width, float height);\n\n/**\n  Sets the color rendering mode for the terminal\n\n  \\param color_mode Termbox output mode as defined in termbox2.h, excluding truecolor\n                     - TB_OUTPUT_NORMAL - Use default ANSI colors\n                     - TB_OUTPUT_256 - Use 256 terminal colors\n                     - TB_OUTPUT_216 - Use 216 terminal colors from 256 color mode\n                     - TB_OUTPUT_GRAYSCALE - Use 24 gray colors from 256 color mode\n                     - CLAY_TB_OUTPUT_NOCOLOR - Don't use ANSI colors at all\n */\nvoid Clay_Termbox_Set_Color_Mode(int color_mode);\n\n/**\n  Sets the method for converting the width of borders to terminal cells\n\n  \\param border_mode Method for adjusting border sizes to fit terminal cells\n                      - CLAY_TB_BORDER_MODE_DEFAULT       - same as CLAY_TB_BORDER_MODE_MINIMUM\n                      - CLAY_TB_BORDER_MODE_ROUND         - borders will be rounded to nearest cell\n                                                            size\n                      - CLAY_TB_BORDER_MODE_MINIMUM       - borders will have a minimum width of one\n                                                            cell\n */\nvoid Clay_Termbox_Set_Border_Mode(enum border_mode border_mode);\n\n/**\n  Sets the character style to use for rendering borders\n\n  \\param border_chars Characters used for rendering borders\n                       - CLAY_TB_BORDER_CHARS_DEFAULT - same as BORDER_UNICODE\n                       - CLAY_TB_BORDER_CHARS_ASCII   - Uses ascii characters: '+', '|', '-'\n                       - CLAY_TB_BORDER_CHARS_UNICODE - Uses unicode box drawing characters\n                       - CLAY_TB_BORDER_CHARS_BLANK   - Draws background colors only\n                       - CLAY_TB_BORDER_CHARS_NONE    - Don't draw borders\n */\nvoid Clay_Termbox_Set_Border_Chars(enum border_chars border_chars);\n\n/**\n  Sets the method for drawing images\n\n  \\param image_mode Method for adjusting border sizes to fit terminal cells\n                     - CLAY_TB_IMAGE_MODE_DEFAULT       - same as CLAY_TB_IMAGE_MODE_UNICODE\n                     - CLAY_TB_IMAGE_MODE_PLACEHOLDER   - Draw a placeholder pattern in place of\n                                                          images\n                     - CLAY_TB_IMAGE_MODE_BG            - Draw image by setting the background color\n                                                          for space characters\n                     - CLAY_TB_IMAGE_MODE_ASCII_FG      - Draw image by setting the foreground color\n                                                          for ascii characters\n                     - CLAY_TB_IMAGE_MODE_ASCII         - Draw image by setting the foreground and\n                                                          background colors for ascii characters\n                     - CLAY_TB_IMAGE_MODE_UNICODE       - Draw image by setting the foreground and\n                                                          background colors for unicode characters\n                     - CLAY_TB_IMAGE_MODE_ASCII_FG_FAST - Draw image by setting the foreground color\n                                                          for ascii characters. Checks fewer\n                                                          characters to draw faster\n                     - CLAY_TB_IMAGE_MODE_ASCII_FAST    - Draw image by setting the foreground and\n                                                          background colors for ascii characters.\n                                                          Checks fewer characters to draw faster\n                     - CLAY_TB_IMAGE_MODE_UNICODE_FAST  - Draw image by setting the foreground and\n                                                          background colors for unicode characters.\n                                                          Checks fewer characters to draw faster\n */\nvoid Clay_Termbox_Set_Image_Mode(enum image_mode image_mode);\n\n/**\n  Fuel corresponds to the amount of time spent per render on drawing images. Increasing this has\n  the image render faster, but the program will be less responsive until it finishes\n\n  Cost to draw one cell (lengths of arrays in image_character_masks.h):\n  -  1 : CLAY_TB_IMAGE_MODE_BG\n  - 15 : CLAY_TB_IMAGE_MODE_UNICODE_FAST, CLAY_TB_IMAGE_MODE_ASCII_FAST,\n         CLAY_TB_IMAGE_MODE_ASCII_FG_FAST\n  - 52 : CLAY_TB_IMAGE_MODE_UNICODE\n  - 95 : CLAY_TB_IMAGE_MODE_ASCII, CLAY_TB_IMAGE_MODE_ASCII_FG\n\n  \\param fuel_max       Maximum amount of fuel used per render (shared between all images)\n  \\param fuel_per_image Maximum amount of fuel used per render per image\n */\nvoid Clay_Termbox_Set_Image_Fuel(int fuel_max, int fuel_per_image);\n\n/**\n  Enables or disables emulated transparency\n\n  If the color mode is TB_OUTPUT_NORMAL or CLAY_TB_OUTPUT_NOCOLOR, transparency will not be enabled\n\n  \\param transparency Transparency value to set\n */\nvoid Clay_Termbox_Set_Transparency(bool transparency);\n\n/**\n  Current width of the terminal in pixels\n*/\nfloat Clay_Termbox_Width(void);\n\n/**\n  Current height of the terminal in pixels\n*/\nfloat Clay_Termbox_Height(void);\n\n/**\n  Current width of a terminal cell in pixels\n*/\nfloat Clay_Termbox_Cell_Width(void);\n\n/**\n  Current height of a terminal cell in pixels\n*/\nfloat Clay_Termbox_Cell_Height(void);\n\n/**\n  Callback function used to measure the dimensions in pixels of a text string\n\n  \\param text     Text to measure\n  \\param config   Ignored\n  \\param userData Ignored\n */\nstatic inline Clay_Dimensions Clay_Termbox_MeasureText(\n    Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);\n\n/**\n  Load an image from a file into a format usable with this renderer\n\n  Supports image formats from stb_image (JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC)\n\n  Note that rendered characters are cached in the returned `clay_tb_image`. If the same image is\n  used in multiple places, load it a separate time for each use to reduce unecessary reprocessing\n  every render.\n\n  \\param filename File to load image from\n */\nclay_tb_image Clay_Termbox_Image_Load_File(const char *filename);\n\n/**\n  Load an image from memory into a format usable with this renderer\n\n  Supports image formats from stb_image (JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC)\n\n  Note that rendered characters are cached in the returned `clay_tb_image`. If the same image is\n  used in multiple places, load it a separate time for each use to reduce unecessary reprocessing\n  every render.\n\n  \\param image Image to load. Should be the whole file copied into memory\n  \\param size  Size of the image in bytes\n */\nclay_tb_image Clay_Termbox_Image_Load_Memory(const void *image, int size);\n\n/**\n  Free an image\n\n  \\param image Image to free\n */\nvoid Clay_Termbox_Image_Free(clay_tb_image *image);\n\n/**\n  Set up configuration, start termbox2, and allocate internal structures.\n\n  Configuration can be overriden by environment variables:\n  - CLAY_TB_COLOR_MODE\n    - NORMAL\n    - 256\n    - 216\n    - GRAYSCALE\n    - TRUECOLOR\n    - NOCOLOR\n  - CLAY_TB_BORDER_CHARS\n    - DEFAULT\n    - ASCII\n    - UNICODE\n    - BLANK\n    - NONE\n  - CLAY_TB_IMAGE_MODE\n    - DEFAULT\n    - PLACEHOLDER\n    - BG\n    - ASCII_FG\n    - ASCII\n    - UNICODE\n    - ASCII_FG_FAST\n    - ASCII_FAST\n    - UNICODE_FAST\n  - CLAY_TB_TRANSPARENCY\n    - 1\n    - 0\n  - CLAY_TB_CELL_PIXELS\n    - 10x20\n\n  Must be run before using this renderer.\n\n  \\param color_mode   Termbox output mode as defined in termbox2.h, excluding truecolor\n  \\param border_mode  Method for adjusting border sizes to fit terminal cells\n  \\param border_chars Characters used for rendering borders\n  \\param image_mode   Method for drawing images\n  \\param transparency Emulate transparency using background colors\n */\nvoid Clay_Termbox_Initialize(int color_mode, enum border_mode border_mode,\n    enum border_chars border_chars, enum image_mode image_mode, bool transparency);\n\n/**\n Stop termbox2 and release internal structures\n */\nvoid Clay_Termbox_Close(void);\n\n/**\n  Render a set of commands to the terminal\n\n  \\param commands Array of render commands from Clay's CreateLayout() function\n */\nvoid Clay_Termbox_Render(Clay_RenderCommandArray commands);\n\n/**\n  Convenience function to block until an event is received from termbox. If an image is only\n  partially rendered, this returns immediately.\n */\nvoid Clay_Termbox_Waitfor_Event(void);\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Internal state\n\n// Settings/options\nstatic bool clay_tb_initialized = false;\nstatic int clay_tb_color_mode = TB_OUTPUT_NORMAL;\nstatic bool clay_tb_transparency = false;\nstatic enum border_mode clay_tb_border_mode = CLAY_TB_BORDER_MODE_DEFAULT;\nstatic enum border_chars clay_tb_border_chars = CLAY_TB_BORDER_CHARS_DEFAULT;\nstatic enum image_mode clay_tb_image_mode = CLAY_TB_IMAGE_MODE_DEFAULT;\n\n// Dimensions of a cell are specified in pixels\n// Default dimensions were measured from the default terminal on Debian 12:\n//  Terminal:  gnome-terminal\n//  Font:      \"Monospace Regular\"\n//  Font size: 11\nstatic clay_tb_pixel_dimensions clay_tb_cell_size = { .width = 9, .height = 21 };\n\n// Scissor mode prevents drawing outside of the specified bounding box\nstatic bool clay_tb_scissor_enabled = false;\nclay_tb_cell_bounding_box clay_tb_scissor_box;\n\n// Images may be drawn across multiple renders to improve responsiveness. The initial draw will be\n// approximate, then further partial draws will replace characters with more accurate ones\nstatic bool clay_tb_partial_image_drawn = false;\n\n\n// Maximum fuel used per render across all images\nstatic int clay_tb_image_fuel_max = 200 * 1024;\n// Maximum fuel used per render per image\nstatic int clay_tb_image_fuel_per_image = 100 * 1024;\n// Fuel used this render\nstatic int clay_tb_image_fuel_used = 0;\n// -----------------------------------------------\n// -- Color buffer\n\n// Buffer storing background colors from previously drawn items. Used to emulate transparency and\n// set the background color for text.\nstatic Clay_Color *clay_tb_color_buffer_clay = NULL;\n// Dimensions are specified in cells\nstatic clay_tb_dimensions clay_tb_color_buffer_dimensions = { 0, 0 };\nstatic clay_tb_dimensions clay_tb_color_buffer_max_dimensions = { 0, 0 };\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Internal utility functions\n\nstatic inline bool clay_tb_valid_color(Clay_Color color)\n{\n    return (\n            0x00 <= color.r && color.r <= 0xff &&\n            0x00 <= color.g && color.g <= 0xff &&\n            0x00 <= color.b && color.b <= 0xff &&\n            0x00 <= color.a && color.a <= 0xff\n    );\n}\n\n/**\n In 256-color mode, there are 216 colors (excluding default terminal colors and gray colors),\n with 6 different magnitudes for each of r, g, b.\n\n This function clamps to the nearest intensity (represented by 0-5) that can be output in this mode\n\n Possible intensities per component: 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff\n\n Examples:\n - 0x20  -> 0\n - 0x2f  -> 1\n - 0x85  -> 2\n - 0xff  -> 5\n\n \\param color 8-bit intensity of one RGB component\n*/\nstatic int clay_tb_rgb_intensity_to_index(int color)\n{\n    clay_tb_assert(0x00 <= color && color <= 0xff, \"Invalid intensity (allowed range 0x00-0xff)\");\n    return (color < 0x2f) ? 0\n        : (color < 0x73)  ? 1\n            : 2 + ((color - 0x73) / 0x28);\n}\n\n/**\n  Convert an RGB color from Clay's representation to the nearest representable color in the current\n  termbox2 output mode\n\n  \\param color Color to convert\n */\nstatic uintattr_t clay_tb_color_convert(Clay_Color color)\n{\n    clay_tb_assert(clay_tb_valid_color(color), \"Invalid Clay color: (%f, %f, %f, %f)\", color.r,\n        color.g, color.b, color.a);\n\n    uintattr_t tb_color = TB_DEFAULT;\n\n    switch (clay_tb_color_mode) {\n        default: {\n            clay_tb_assert(false, \"Invalid or unimplemented Termbox color output mode (%d)\",\n                clay_tb_color_mode);\n            break;\n        }\n        case TB_OUTPUT_NORMAL: {\n            const int color_lut_count = 16;\n            const uintattr_t color_lut[][4] = {\n                { TB_BLACK,               0x00, 0x00, 0x00 },\n                { TB_RED,                 0xaa, 0x00, 0x00 },\n                { TB_GREEN,               0x00, 0xaa, 0x00 },\n                { TB_YELLOW,              0xaa, 0x55, 0x00 },\n                { TB_BLUE,                0x00, 0x00, 0xaa },\n                { TB_MAGENTA,             0xaa, 0x00, 0xaa },\n                { TB_CYAN,                0x00, 0xaa, 0xaa },\n                { TB_WHITE,               0xaa, 0xaa, 0xaa },\n                { TB_BLACK   | TB_BRIGHT, 0x55, 0x55, 0x55 },\n                { TB_RED     | TB_BRIGHT, 0xff, 0x55, 0x55 },\n                { TB_GREEN   | TB_BRIGHT, 0x55, 0xff, 0x55 },\n                { TB_YELLOW  | TB_BRIGHT, 0xff, 0xff, 0x55 },\n                { TB_BLUE    | TB_BRIGHT, 0x55, 0x55, 0xff },\n                { TB_MAGENTA | TB_BRIGHT, 0xff, 0x55, 0xff },\n                { TB_CYAN    | TB_BRIGHT, 0x55, 0xff, 0xff },\n                { TB_WHITE   | TB_BRIGHT, 0xff, 0xff, 0xff }\n            };\n\n            // Find nearest color on the lookup table\n            int color_index = 0;\n            float min_distance_squared = 0xff * 0xff * 3;\n            for (int i = 0; i < color_lut_count; ++i) {\n                float r_distance = color.r - (float)color_lut[i][1];\n                float g_distance = color.g - (float)color_lut[i][2];\n                float b_distance = color.b - (float)color_lut[i][3];\n\n                float distance_squared =\n                    (r_distance * r_distance) +\n                    (g_distance * g_distance) +\n                    (b_distance * b_distance);\n\n                // Penalize pure black and white to display faded colors more often\n                if (TB_BLACK == color_lut[i][0] || TB_WHITE == color_lut[i][0]\n                    || (TB_BLACK | TB_BRIGHT) == color_lut[i][0]\n                    || (TB_WHITE | TB_BRIGHT) == color_lut[i][0]) {\n                    distance_squared *= 2;\n                }\n\n                if (distance_squared < min_distance_squared) {\n                    min_distance_squared = distance_squared;\n                    color_index = i;\n                }\n            }\n            tb_color = color_lut[color_index][0];\n            break;\n        }\n        case TB_OUTPUT_216: {\n            int r_index = clay_tb_rgb_intensity_to_index((int)color.r);\n            int g_index = clay_tb_rgb_intensity_to_index((int)color.g);\n            int b_index = clay_tb_rgb_intensity_to_index((int)color.b);\n\n            tb_color = 0x01 + (36 * r_index) + (6 * g_index) + (b_index);\n            break;\n        }\n        case TB_OUTPUT_256: {\n            const int index_lut_count = 6;\n            const uintattr_t index_lut[] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff };\n\n            int r_index = clay_tb_rgb_intensity_to_index((int)color.r);\n            int g_index = clay_tb_rgb_intensity_to_index((int)color.g);\n            int b_index = clay_tb_rgb_intensity_to_index((int)color.b);\n\n            int rgb_color = 0x10 + (36 * r_index) + (6 * g_index) + (b_index);\n\n            float rgb_r_distance = color.r - (float)index_lut[r_index];\n            float rgb_g_distance = color.g - (float)index_lut[g_index];\n            float rgb_b_distance = color.b - (float)index_lut[b_index];\n\n            float rgb_distance_squared =\n                (rgb_r_distance * rgb_r_distance) +\n                (rgb_g_distance * rgb_g_distance) +\n                (rgb_b_distance * rgb_b_distance);\n\n            int avg_color = (int)((color.r + color.g + color.b) / 3);\n            int gray_avg_color = (avg_color * 24 / 0x100);\n            int gray_color = 0xe8 + gray_avg_color;\n\n            float gray_r_distance = color.r - (float)gray_avg_color;\n            float gray_g_distance = color.g - (float)gray_avg_color;\n            float gray_b_distance = color.b - (float)gray_avg_color;\n\n            float gray_distance_squared =\n                (gray_r_distance * gray_r_distance) +\n                (gray_g_distance * gray_g_distance) +\n                (gray_b_distance * gray_b_distance);\n\n            tb_color = (rgb_distance_squared < gray_distance_squared) ? rgb_color : gray_color;\n\n            break;\n        }\n        case TB_OUTPUT_GRAYSCALE: {\n            // 24 shades of gray\n            float avg_color = ((color.r + color.g + color.b) / 3);\n            tb_color = 0x01 + (int)(avg_color * 24 / 0x100);\n            break;\n        }\n        case TB_OUTPUT_TRUECOLOR: {\n            clay_tb_assert(32 <= TB_OPT_ATTR_W, \"Truecolor requires TB_OPT_ATTR_W to be 32 or 64\");\n            tb_color = ((uintattr_t)color.r << 4 * 4) + ((uintattr_t)color.g << 2 * 4)\n                + ((uintattr_t)color.b);\n            if (0x000000 == tb_color) {\n                tb_color = TB_HI_BLACK;\n            }\n            break;\n        }\n        case CLAY_TB_OUTPUT_NOCOLOR: {\n            // Uses default terminal colors\n            tb_color = TB_DEFAULT;\n            break;\n        }\n    }\n    return tb_color;\n}\n\n/**\n  Round float to nearest integer value\n\n  Used instead of roundf() so math.h doesn't need to be linked\n\n  \\param f Float to round\n */\nstatic inline int clay_tb_roundf(float f)\n{\n    int i = f;\n    return (f - i > 0.5f) ? i + 1 : i;\n}\n\n/**\n  Snap pixel values from Clay to nearest cell values\n\n  Width/height accounts for offset from x/y, so a box at x=(1.2 * cell_width) and\n  width=(1.4 * cell_width) is snapped to x=1 and width=2.\n\n  \\param box Bounding box with pixel measurements to convert\n */\nstatic inline clay_tb_cell_bounding_box cell_snap_bounding_box(Clay_BoundingBox box)\n{\n    return (clay_tb_cell_bounding_box) {\n        .x = clay_tb_roundf(box.x / clay_tb_cell_size.width),\n        .y = clay_tb_roundf(box.y / clay_tb_cell_size.height),\n        .width = clay_tb_roundf((box.x + box.width) / clay_tb_cell_size.width)\n            - clay_tb_roundf(box.x / clay_tb_cell_size.width),\n        .height = clay_tb_roundf((box.y + box.height) / clay_tb_cell_size.height)\n            - clay_tb_roundf(box.y / clay_tb_cell_size.height),\n    };\n}\n\n/**\n  Snap pixel values from Clay to nearest cell values without considering x and y position when\n  calculating width/height.\n\n  Width/height ignores offset from x/y, so a box at x=(1.2 * cell_width) and\n  width=(1.4 * cell_width) is snapped to x=1 and width=1.\n\n  \\param box Bounding box with pixel measurements to convert\n */\nstatic inline clay_tb_cell_bounding_box cell_snap_pos_ind_bounding_box(Clay_BoundingBox box)\n{\n    return (clay_tb_cell_bounding_box) {\n        .x = clay_tb_roundf(box.x / clay_tb_cell_size.width),\n        .y = clay_tb_roundf(box.y / clay_tb_cell_size.height),\n        .width = clay_tb_roundf(box.width / clay_tb_cell_size.width),\n        .height = clay_tb_roundf(box.height / clay_tb_cell_size.height),\n    };\n}\n\n/**\n  Get stored clay color for a position from the internal color buffer\n\n  \\param x X position of cell\n  \\param y Y position of cell\n */\nstatic inline Clay_Color clay_tb_color_buffer_clay_get(int x, int y)\n{\n    clay_tb_assert(0 <= x && x < clay_tb_color_buffer_dimensions.width,\n        \"Cell buffer x position (%d) offscreen (range 0-%d)\", x,\n        clay_tb_color_buffer_dimensions.width);\n    clay_tb_assert(0 <= y && y < clay_tb_color_buffer_dimensions.height,\n        \"Cell buffer y position (%d) offscreen (range 0-%d)\", y,\n        clay_tb_color_buffer_dimensions.height);\n    return clay_tb_color_buffer_clay[x + (y * clay_tb_color_buffer_dimensions.width)];\n}\n\n/**\n  Set stored clay color for a position in the internal color buffer\n\n  \\param x     X position of cell\n  \\param y     Y position of cell\n  \\param color Color to store\n */\nstatic inline void clay_tb_color_buffer_clay_set(int x, int y, Clay_Color color)\n{\n    clay_tb_assert(0 <= x && x < clay_tb_color_buffer_dimensions.width,\n        \"Cell buffer x position (%d) offscreen (range 0-%d)\", x,\n        clay_tb_color_buffer_dimensions.width);\n    clay_tb_assert(0 <= y && y < clay_tb_color_buffer_dimensions.height,\n        \"Cell buffer y position (%d) offscreen (range 0-%d)\", y,\n        clay_tb_color_buffer_dimensions.height);\n    clay_tb_color_buffer_clay[x + (y * clay_tb_color_buffer_dimensions.width)] = color;\n}\n\n/**\n  Resize internal color buffer to the current terminal size\n */\nstatic void clay_tb_resize_buffer(void)\n{\n    int current_width = tb_width();\n    int current_height = tb_height();\n\n    // Reallocate if the new size is larger than the maximum size of the buffer\n    size_t max_size = (size_t)clay_tb_color_buffer_max_dimensions.width\n        * clay_tb_color_buffer_max_dimensions.height;\n    size_t new_size = (size_t)current_width * current_height;\n    if (max_size < new_size) {\n        Clay_Color *tmp_clay = tb_realloc(clay_tb_color_buffer_clay, sizeof(Clay_Color) * new_size);\n        if (NULL == tmp_clay) {\n            clay_tb_assert(false, \"Reallocation failure for internal clay color buffer\");\n        }\n        clay_tb_color_buffer_clay = tmp_clay;\n        for (size_t i = max_size; i < new_size; ++i) {\n            clay_tb_color_buffer_clay[i] = (Clay_Color) { 0 };\n        }\n\n        clay_tb_color_buffer_max_dimensions.width = current_width;\n        clay_tb_color_buffer_max_dimensions.height = current_height;\n    }\n    clay_tb_color_buffer_dimensions.width = current_width;\n    clay_tb_color_buffer_dimensions.height = current_height;\n}\n\n/**\n  Calculate color at a given position after emulating transparency.\n\n  This isn't true transparency, just the background colors changing to emulate it\n\n  \\param color Pair of termbox and clay color representations to overlay with the background color\n  \\param x     X position of cell\n  \\param y     Y position of cell\n */\nstatic inline clay_tb_color_pair clay_tb_get_transparency_color(\n    int x, int y, clay_tb_color_pair color)\n{\n    if (!clay_tb_transparency) {\n        return color;\n    }\n\n    Clay_Color color_bg = clay_tb_color_buffer_clay_get(x, y);\n    Clay_Color new_color = {\n        .r = color_bg.r + (color.clay.a / 255) * (color.clay.r - color_bg.r),\n        .g = color_bg.g + (color.clay.a / 255) * (color.clay.g - color_bg.g),\n        .b = color_bg.b + (color.clay.a / 255) * (color.clay.b - color_bg.b),\n        .a = 255\n    };\n\n    return (clay_tb_color_pair) {\n        .clay = new_color,\n        .termbox = clay_tb_color_convert(new_color)\n    };\n}\n\n/**\n  Draw a character cell at a position on screen.\n\n  Accounts for scissor mode and stores the cell to the internal color buffer for transparency and\n  text backgrounds.\n\n  \\param x     X position of cell\n  \\param y     Y position of cell\n  \\param ch    Utf32 representation of character to draw\n  \\param tb_fg Foreground color in termbox representation\n  \\param tb_bg Background color in termbox representation\n  \\param fg    Foreground color in clay representation\n  \\param bg    Background color in clay representation\n */\nstatic int clay_tb_set_cell(\n    int x, int y, uint32_t ch, uintattr_t tb_fg, uintattr_t tb_bg, Clay_Color bg)\n{\n    clay_tb_assert(0 <= x && x < tb_width(), \"Cell buffer x position (%d) offscreen (range 0-%d)\",\n        x, tb_width());\n    clay_tb_assert(0 <= y && y < tb_height(), \"Cell buffer y position (%d) offscreen (range 0-%d)\",\n        y, tb_height());\n\n    if (!clay_tb_scissor_enabled\n        || (clay_tb_scissor_enabled\n            && (clay_tb_scissor_box.x <= x\n                && x < clay_tb_scissor_box.x + clay_tb_scissor_box.width)\n            && (clay_tb_scissor_box.y <= y\n                && y < clay_tb_scissor_box.y + clay_tb_scissor_box.height))) {\n        int codepoint_width = tb_wcwidth(ch);\n        if (-1 == codepoint_width) {\n            // Nonprintable character, use REPLACEMENT CHARACTER (U+FFFD)\n            ch = U'\\ufffd';\n            codepoint_width = tb_wcwidth(ch);\n        }\n\n        int err;\n        int max_x = CLAY__MIN(x + codepoint_width, tb_width());\n        for (int i = x; i < max_x; ++i) {\n            clay_tb_color_buffer_clay_set(i, y, bg);\n            err = tb_set_cell(i, y, ch, tb_fg, tb_bg);\n            if (TB_OK != err) {\n                break;\n            }\n        }\n\n        return err;\n    }\n    return -1;\n}\n\n/**\n  Convert a pixel-based image to a cell-based image of the specified width and height. Stores the\n  converted/resized result in the cache of the input image.\n\n  If the image has not changed size or image mode since the last convert it is returned unchanged\n\n  \\param image  Image to convert/resize\n  \\param width  Target width in cells for the converted image\n  \\param height Target height in cells for the converted image\n */\nbool clay_tb_image_convert(clay_tb_image *image, int width, int height)\n{\n    clay_tb_assert(NULL != image->pixel_data, \"Image must be loaded\");\n\n    bool image_unchanged = (width == image->internal.width && height == image->internal.height\n        && (clay_tb_image_mode == image->internal.last_image_mode));\n\n    if (image_unchanged && !image->internal.partial_render.in_progress) {\n        return true;\n    }\n    if (!image_unchanged) {\n        free(image->internal.partial_render.resized_pixel_data);\n        image->internal.partial_render = (struct clay_tb_partial_render) {\n            .in_progress = false,\n            .resized_pixel_data = NULL,\n            .cursor_x = 0,\n            .cursor_y = 0,\n            .cursor_mask = 0,\n            .min_difference_squared_sum = INT_MAX,\n            .best_mask = 0,\n            .best_foreground = { 0, 0, 0, 0 },\n            .best_background = { 0, 0, 0, 0 }\n        };\n    }\n\n    const size_t size = (size_t)width * height;\n\n    // Allocate/resize internal cache data\n    if (size > image->internal.size_max) {\n        uint32_t *tmp_characters = realloc(image->internal.characters, size * sizeof(uint32_t));\n        Clay_Color *tmp_foreground = realloc(image->internal.foreground, size * sizeof(Clay_Color));\n        Clay_Color *tmp_background = realloc(image->internal.background, size * sizeof(Clay_Color));\n\n        if (NULL == tmp_characters || NULL == tmp_foreground || NULL == tmp_background) {\n            image->internal.size_max = 0;\n            free(tmp_characters);\n            free(tmp_foreground);\n            free(tmp_background);\n            image->internal.characters = NULL;\n            image->internal.foreground = NULL;\n            image->internal.background = NULL;\n            return false;\n        }\n        image->internal.characters = tmp_characters;\n        image->internal.foreground = tmp_foreground;\n        image->internal.background = tmp_background;\n        image->internal.size_max = size;\n    }\n\n    image->internal.width = width;\n    image->internal.height = height;\n\n    // Resize image using the same width/height in cells, but with the pixel sizes of the character\n    // masks instead of the cell size. The pixel data for each character mask will be compared to\n    // the pixel data of a small section of the image under the mask. The closest mask to the image\n    // data is chosen as the character to draw.\n    const int character_mask_pixel_width = 6;\n    const int character_mask_pixel_height = 12;\n    const int pixel_width = width * character_mask_pixel_width;\n    const int pixel_height = height * character_mask_pixel_height;\n\n    unsigned char *resized_pixel_data;\n    if (image->internal.partial_render.in_progress) {\n        resized_pixel_data = image->internal.partial_render.resized_pixel_data;\n    } else {\n        resized_pixel_data = stbir_resize_uint8_linear(image->pixel_data, image->pixel_width,\n            image->pixel_height, 0, NULL, pixel_width, pixel_height, 0, STBIR_RGB);\n        image->internal.partial_render.resized_pixel_data = resized_pixel_data;\n    }\n\n    int num_character_masks = 1;\n    const clay_tb_character_mask *character_masks = NULL;\n    switch (clay_tb_image_mode) {\n        case CLAY_TB_IMAGE_MODE_BG: {\n            num_character_masks = 1;\n            character_masks = &clay_tb_image_shapes_ascii_fast[0];\n            break;\n        }\n        case CLAY_TB_IMAGE_MODE_ASCII:\n        case CLAY_TB_IMAGE_MODE_ASCII_FG: {\n            num_character_masks = CLAY_TB_IMAGE_SHAPES_ASCII_BEST_COUNT;\n            character_masks = &clay_tb_image_shapes_ascii_best[0];\n            break;\n        }\n        case CLAY_TB_IMAGE_MODE_UNICODE: {\n            num_character_masks = CLAY_TB_IMAGE_SHAPES_UNICODE_BEST_COUNT;\n            character_masks = &clay_tb_image_shapes_unicode_best[0];\n            break;\n        }\n        case CLAY_TB_IMAGE_MODE_ASCII_FAST:\n        case CLAY_TB_IMAGE_MODE_ASCII_FG_FAST: {\n            num_character_masks = CLAY_TB_IMAGE_SHAPES_ASCII_FAST_COUNT;\n            character_masks = &clay_tb_image_shapes_ascii_fast[0];\n            break;\n        }\n        case CLAY_TB_IMAGE_MODE_UNICODE_FAST: {\n            num_character_masks = CLAY_TB_IMAGE_SHAPES_UNICODE_FAST_COUNT;\n            character_masks = &clay_tb_image_shapes_unicode_fast[0];\n            break;\n        }\n    };\n\n    // The number of character masks to check before exiting the render for this step\n    // Used to improve responsiveness by splitting renders across multiple frames\n    const int fuel_amount_initial\n        = CLAY__MIN(clay_tb_image_fuel_per_image, clay_tb_image_fuel_max - clay_tb_image_fuel_used);\n    int fuel_remaining = fuel_amount_initial;\n    bool partial_character_render = false;\n\n    // Do a quick initial render to set the background\n    if (!image->internal.partial_render.in_progress) {\n        image->internal.last_image_mode = clay_tb_image_mode;\n        for (int y = image->internal.partial_render.cursor_y; y < height; ++y) {\n            for (int x = image->internal.partial_render.cursor_x; x < width; ++x) {\n                const int cell_top_left_pixel_x = x * character_mask_pixel_width;\n                const int cell_top_left_pixel_y = y * character_mask_pixel_height;\n                const int image_index = 3\n                    * (((cell_top_left_pixel_y + character_mask_pixel_height / 2) * pixel_width)\n                        + (cell_top_left_pixel_x + character_mask_pixel_width / 2));\n                Clay_Color pixel_color = {\n                    (float)resized_pixel_data[image_index],\n                    (float)resized_pixel_data[image_index + 1],\n                    (float)resized_pixel_data[image_index + 2],\n                };\n\n                const int cell_index = y * width + x;\n                image->internal.characters[cell_index] = '.';\n                image->internal.foreground[cell_index] = pixel_color;\n                image->internal.background[cell_index] = pixel_color;\n\n                fuel_remaining = CLAY__MAX(0, fuel_remaining - 1);\n            }\n        }\n    }\n\n    if (0 == fuel_remaining) {\n        image->internal.partial_render.in_progress = true;\n        clay_tb_partial_image_drawn = true;\n        goto done;\n    }\n\n    for (int y = image->internal.partial_render.cursor_y; y < height; ++y) {\n        for (int x = image->internal.partial_render.cursor_x; x < width; ++x) {\n            const int cell_top_left_pixel_x = x * character_mask_pixel_width;\n            const int cell_top_left_pixel_y = y * character_mask_pixel_height;\n\n            // For each possible cell character, use the mask to find the average color for the\n            // foreground ('1's) and background ('0's).\n            int min_difference_squared_sum\n                = image->internal.partial_render.min_difference_squared_sum;\n            int best_mask = image->internal.partial_render.best_mask;\n            Clay_Color best_foreground = image->internal.partial_render.best_foreground;\n            Clay_Color best_background = image->internal.partial_render.best_background;\n\n            for (int i = image->internal.partial_render.cursor_mask; i < num_character_masks; ++i) {\n                int color_avg_background_r = 0;\n                int color_avg_background_g = 0;\n                int color_avg_background_b = 0;\n                int color_avg_foreground_r = 0;\n                int color_avg_foreground_g = 0;\n                int color_avg_foreground_b = 0;\n                int foreground_count = 0;\n                int background_count = 0;\n\n                for (int cell_pixel_y = 0; cell_pixel_y < character_mask_pixel_height;\n                     ++cell_pixel_y) {\n                    for (int cell_pixel_x = 0; cell_pixel_x < character_mask_pixel_width;\n                         ++cell_pixel_x) {\n                        const int index = 3\n                            * (((cell_top_left_pixel_y + cell_pixel_y) * pixel_width)\n                                + (cell_top_left_pixel_x + cell_pixel_x));\n\n                        const int mask_index\n                            = (cell_pixel_y * character_mask_pixel_width) + cell_pixel_x;\n                        if (0 == character_masks[i].data[mask_index]) {\n                            if (CLAY_TB_IMAGE_MODE_ASCII_FG != clay_tb_image_mode\n                                && CLAY_TB_IMAGE_MODE_ASCII_FG_FAST != clay_tb_image_mode) {\n                                color_avg_background_r += resized_pixel_data[index];\n                                color_avg_background_g += resized_pixel_data[index + 1];\n                                color_avg_background_b += resized_pixel_data[index + 2];\n                                background_count += 1;\n                            }\n                        } else {\n                            color_avg_foreground_r += resized_pixel_data[index];\n                            color_avg_foreground_g += resized_pixel_data[index + 1];\n                            color_avg_foreground_b += resized_pixel_data[index + 2];\n                            foreground_count += 1;\n                        }\n                    }\n                }\n\n                if (CLAY_TB_IMAGE_MODE_ASCII_FG != clay_tb_image_mode\n                    && CLAY_TB_IMAGE_MODE_ASCII_FG_FAST != clay_tb_image_mode) {\n                    color_avg_background_r /= CLAY__MAX(1, background_count);\n                    color_avg_background_g /= CLAY__MAX(1, background_count);\n                    color_avg_background_b /= CLAY__MAX(1, background_count);\n                } else {\n                    color_avg_background_r = 0;\n                    color_avg_background_g = 0;\n                    color_avg_background_b = 0;\n                }\n\n                color_avg_foreground_r /= CLAY__MAX(1, foreground_count);\n                color_avg_foreground_g /= CLAY__MAX(1, foreground_count);\n                color_avg_foreground_b /= CLAY__MAX(1, foreground_count);\n\n                // Determine the difference between the mask with colors and the actual pixel data\n                int difference_squared_sum = 0;\n                for (int cell_pixel_y = 0; cell_pixel_y < character_mask_pixel_height;\n                     ++cell_pixel_y) {\n                    for (int cell_pixel_x = 0; cell_pixel_x < character_mask_pixel_width;\n                         ++cell_pixel_x) {\n                        const int index = 3\n                            * (((cell_top_left_pixel_y + cell_pixel_y) * pixel_width)\n                                + (cell_top_left_pixel_x + cell_pixel_x));\n                        int rdiff, gdiff, bdiff, adiff;\n\n                        const int mask_index\n                            = (cell_pixel_y * character_mask_pixel_width) + cell_pixel_x;\n                        if (0 == character_masks[i].data[mask_index]) {\n                            rdiff = (color_avg_background_r - resized_pixel_data[index]);\n                            gdiff = (color_avg_background_g - resized_pixel_data[index + 1]);\n                            bdiff = (color_avg_background_b - resized_pixel_data[index + 2]);\n                        } else {\n                            rdiff = (color_avg_foreground_r - resized_pixel_data[index]);\n                            gdiff = (color_avg_foreground_g - resized_pixel_data[index + 1]);\n                            bdiff = (color_avg_foreground_b - resized_pixel_data[index + 2]);\n                        }\n\n                        difference_squared_sum += (\n                            (rdiff * rdiff) +\n                            (gdiff * gdiff) +\n                            (bdiff * bdiff));\n                    }\n                }\n\n                // Choose the closest character mask to the image data\n                if (difference_squared_sum < min_difference_squared_sum) {\n                    min_difference_squared_sum = difference_squared_sum;\n                    best_mask = i;\n                    best_background = (Clay_Color) {\n                        .r = (float)color_avg_background_r,\n                        .g = (float)color_avg_background_g,\n                        .b = (float)color_avg_background_b,\n                        .a = 255\n                    };\n                    best_foreground = (Clay_Color) {\n                        .r = (float)color_avg_foreground_r,\n                        .g = (float)color_avg_foreground_g,\n                        .b = (float)color_avg_foreground_b,\n                        .a = 255\n                    };\n                }\n\n                fuel_remaining -= 1;\n                if (0 == fuel_remaining) {\n                    // Set progress for partial render\n                    image->internal.partial_render = (struct clay_tb_partial_render) {\n                        .in_progress = true,\n                        .resized_pixel_data = resized_pixel_data,\n                        .cursor_x = x,\n                        .cursor_y = y,\n                        .cursor_mask = i + 1,\n                        .min_difference_squared_sum = min_difference_squared_sum,\n                        .best_mask = best_mask,\n                        .best_foreground = best_foreground,\n                        .best_background = best_background\n                    };\n                    partial_character_render = true;\n                    clay_tb_partial_image_drawn = true;\n                    goto done;\n                }\n            }\n            image->internal.partial_render.cursor_mask = 0;\n\n            // Set data in cache for this character\n            const int index = y * width + x;\n            image->internal.characters[index] = character_masks[best_mask].character;\n            image->internal.foreground[index] = best_foreground;\n            image->internal.background[index] = best_background;\n\n            image->internal.partial_render = (struct clay_tb_partial_render) {\n                .in_progress = true,\n                .resized_pixel_data = resized_pixel_data,\n                .cursor_x = x + 1,\n                .cursor_y = y,\n                .cursor_mask = 0,\n                .min_difference_squared_sum = INT_MAX,\n                .best_mask = 0,\n                .best_foreground = { 0, 0, 0, 0 },\n                .best_background = { 0, 0, 0, 0 },\n            };\n            if (0 == fuel_remaining) {\n                clay_tb_partial_image_drawn = true;\n                goto done;\n            }\n        }\n        image->internal.partial_render.cursor_x = 0;\n    }\n    image->internal.partial_render.cursor_y = 0;\n    image->internal.partial_render.in_progress = false;\n    free(resized_pixel_data);\n    image->internal.partial_render.resized_pixel_data = NULL;\n\ndone:\n    clay_tb_image_fuel_used += fuel_amount_initial - fuel_remaining;\n    return true;\n}\n\n\n// -------------------------------------------------------------------------------------------------\n// -- Public API implementation\n\nvoid Clay_Termbox_Set_Cell_Pixel_Size(float width, float height)\n{\n    clay_tb_assert(0 <= width, \"Cell pixel width must be > 0\");\n    clay_tb_assert(0 <= height, \"Cell pixel height must be > 0\");\n    clay_tb_cell_size = (clay_tb_pixel_dimensions) { .width = width, .height = height };\n}\n\nvoid Clay_Termbox_Set_Color_Mode(int color_mode)\n{\n    clay_tb_assert(clay_tb_initialized, \"Clay_Termbox_Initialize must be run first\");\n    clay_tb_assert(CLAY_TB_OUTPUT_NOCOLOR <= color_mode && color_mode <= TB_OUTPUT_TRUECOLOR,\n        \"Color mode invalid (%d)\", color_mode);\n\n    if (CLAY_TB_OUTPUT_NOCOLOR == color_mode) {\n        tb_set_output_mode(TB_OUTPUT_NORMAL);\n    } else {\n        tb_set_output_mode(color_mode);\n    }\n\n    // Force complete re-render to ensure all colors are redrawn\n    tb_invalidate();\n\n    clay_tb_color_mode = color_mode;\n\n    // Re-set transparency value. It will be toggled off if the new output mode doesn't support it\n    Clay_Termbox_Set_Transparency(clay_tb_transparency);\n}\n\nvoid Clay_Termbox_Set_Border_Mode(enum border_mode border_mode)\n{\n    clay_tb_assert(CLAY_TB_BORDER_MODE_DEFAULT <= border_mode\n            && border_mode <= CLAY_TB_BORDER_MODE_MINIMUM,\n        \"Border mode invalid (%d)\", border_mode);\n    if (CLAY_TB_BORDER_MODE_DEFAULT == border_mode) {\n        clay_tb_border_mode = CLAY_TB_BORDER_MODE_MINIMUM;\n    } else {\n        clay_tb_border_mode = border_mode;\n    }\n}\n\nvoid Clay_Termbox_Set_Border_Chars(enum border_chars border_chars)\n{\n    clay_tb_assert(\n        CLAY_TB_BORDER_CHARS_DEFAULT <= border_chars && border_chars <= CLAY_TB_BORDER_CHARS_NONE,\n        \"Border mode invalid (%d)\", border_chars);\n    if (CLAY_TB_BORDER_CHARS_DEFAULT == border_chars) {\n        clay_tb_border_chars = CLAY_TB_BORDER_CHARS_UNICODE;\n    } else {\n        clay_tb_border_chars = border_chars;\n    }\n}\n\nvoid Clay_Termbox_Set_Image_Mode(enum image_mode image_mode)\n{\n    clay_tb_assert(CLAY_TB_IMAGE_MODE_DEFAULT <= image_mode\n            && image_mode <= CLAY_TB_IMAGE_MODE_UNICODE_FAST,\n        \"Image mode invalid (%d)\", image_mode);\n    if (CLAY_TB_IMAGE_MODE_DEFAULT == image_mode) {\n        clay_tb_image_mode = CLAY_TB_IMAGE_MODE_UNICODE;\n    } else {\n        clay_tb_image_mode = image_mode;\n    }\n}\n\nvoid Clay_Termbox_Set_Image_Fuel(int fuel_max, int fuel_per_image)\n{\n    clay_tb_assert(0 < fuel_max && 0 < fuel_per_image,\n            \"Fuel must be positive (%d, %d)\", fuel_max, fuel_per_image);\n    clay_tb_image_fuel_max = fuel_max;\n    clay_tb_image_fuel_per_image = fuel_per_image;\n}\n\nvoid Clay_Termbox_Set_Transparency(bool transparency)\n{\n    clay_tb_transparency = transparency;\n    if (TB_OUTPUT_NORMAL == clay_tb_color_mode || CLAY_TB_OUTPUT_NOCOLOR == clay_tb_color_mode) {\n        clay_tb_transparency = false;\n    }\n}\n\nfloat Clay_Termbox_Width(void)\n{\n    clay_tb_assert(clay_tb_initialized, \"Clay_Termbox_Initialize must be run first\");\n\n    return (float)tb_width() * clay_tb_cell_size.width;\n}\n\nfloat Clay_Termbox_Height(void)\n{\n    clay_tb_assert(clay_tb_initialized, \"Clay_Termbox_Initialize must be run first\");\n\n    return (float)tb_height() * clay_tb_cell_size.height;\n}\n\nfloat Clay_Termbox_Cell_Width(void)\n{\n    return clay_tb_cell_size.width;\n}\n\nfloat Clay_Termbox_Cell_Height(void)\n{\n    return clay_tb_cell_size.height;\n}\n\nstatic inline Clay_Dimensions Clay_Termbox_MeasureText(\n    Clay_StringSlice text, Clay_TextElementConfig *config, void *userData)\n{\n    clay_tb_assert(clay_tb_initialized, \"Clay_Termbox_Initialize must be run first\");\n\n    int width = 0;\n    int height = 1;\n\n    // Convert to utf32 so termbox2's internal wcwidth function can get the printed width of each\n    // codepoint\n    for (int32_t i = 0; i < text.length;) {\n        uint32_t ch;\n        int codepoint_bytes = tb_utf8_char_to_unicode(&ch, text.chars + i);\n        if (0 > codepoint_bytes) {\n            clay_tb_assert(false, \"Invalid utf8\");\n        }\n        i += codepoint_bytes;\n\n        int codepoint_width = tb_wcwidth(ch);\n        if (-1 == codepoint_width) {\n            // Nonprintable character, use width of REPLACEMENT CHARACTER (U+FFFD)\n            codepoint_width = tb_wcwidth(0xfffd);\n        }\n        width += codepoint_width;\n    }\n    return (Clay_Dimensions) {\n        (float)width * clay_tb_cell_size.width,\n        (float)height * clay_tb_cell_size.height\n    };\n}\n\nclay_tb_image Clay_Termbox_Image_Load_File(const char *filename)\n{\n    clay_tb_assert(NULL != filename, \"Filename cannot be null\");\n\n    clay_tb_image rv = { 0 };\n\n    FILE *image_file = NULL;\n\n    image_file = fopen(filename, \"r\");\n    if (NULL == image_file) {\n        fprintf(stderr, \"Failed to open image %s: %s\\n\", filename, strerror(errno));\n        return rv;\n    }\n\n    int channels_in_file;\n    const int desired_color_channels = 3;\n    rv.pixel_data = stbi_load_from_file(\n        image_file, &rv.pixel_width, &rv.pixel_height, &channels_in_file, desired_color_channels);\n\n    fclose(image_file);\n\n    return rv;\n}\n\nclay_tb_image Clay_Termbox_Image_Load_Memory(const void *image, int size)\n{\n    clay_tb_assert(NULL != image, \"Image cannot be null\");\n    clay_tb_assert(0 < size, \"Image size must be > 0\");\n\n    clay_tb_image rv = { 0 };\n\n    int channels_in_file;\n    const int desired_color_channels = 3;\n    rv.pixel_data = stbi_load_from_memory(\n        image, size, &rv.pixel_width, &rv.pixel_height, &channels_in_file, desired_color_channels);\n\n    return rv;\n}\n\nvoid Clay_Termbox_Image_Free(clay_tb_image *image)\n{\n    free(image->pixel_data);\n    free(image->internal.partial_render.resized_pixel_data);\n    free(image->internal.characters);\n    free(image->internal.foreground);\n    free(image->internal.background);\n    *image = (clay_tb_image) { 0 };\n}\n\nvoid Clay_Termbox_Initialize(int color_mode, enum border_mode border_mode,\n    enum border_chars border_chars, enum image_mode image_mode, bool transparency)\n{\n    int new_color_mode = color_mode;\n    int new_border_mode = border_mode;\n    int new_border_chars = border_chars;\n    int new_image_mode = image_mode;\n    int new_transparency = transparency;\n    clay_tb_pixel_dimensions new_pixel_size = clay_tb_cell_size;\n\n    // Check for environment variables that override settings\n\n    const char *env_color_mode = getenv(\"CLAY_TB_COLOR_MODE\");\n    if (NULL != env_color_mode) {\n        if (0 == strcmp(\"NORMAL\", env_color_mode)) {\n            new_color_mode = TB_OUTPUT_NORMAL;\n        } else if (0 == strcmp(\"256\", env_color_mode)) {\n            new_color_mode = TB_OUTPUT_256;\n        } else if (0 == strcmp(\"216\", env_color_mode)) {\n            new_color_mode = TB_OUTPUT_216;\n        } else if (0 == strcmp(\"GRAYSCALE\", env_color_mode)) {\n            new_color_mode = TB_OUTPUT_GRAYSCALE;\n        } else if (0 == strcmp(\"TRUECOLOR\", env_color_mode)) {\n            new_color_mode = TB_OUTPUT_TRUECOLOR;\n        } else if (0 == strcmp(\"NOCOLOR\", env_color_mode)) {\n            new_color_mode = CLAY_TB_OUTPUT_NOCOLOR;\n        }\n    }\n\n    const char *env_border_chars = getenv(\"CLAY_TB_BORDER_CHARS\");\n    if (NULL != env_border_chars) {\n        if (0 == strcmp(\"DEFAULT\", env_border_chars)) {\n            new_border_chars = CLAY_TB_BORDER_CHARS_DEFAULT;\n        } else if (0 == strcmp(\"ASCII\", env_border_chars)) {\n            new_border_chars = CLAY_TB_BORDER_CHARS_ASCII;\n        } else if (0 == strcmp(\"UNICODE\", env_border_chars)) {\n            new_border_chars = CLAY_TB_BORDER_CHARS_UNICODE;\n        } else if (0 == strcmp(\"BLANK\", env_border_chars)) {\n            new_border_chars = CLAY_TB_BORDER_CHARS_BLANK;\n        } else if (0 == strcmp(\"NONE\", env_border_chars)) {\n            new_border_chars = CLAY_TB_BORDER_CHARS_NONE;\n        }\n    }\n\n    const char *env_image_mode = getenv(\"CLAY_TB_IMAGE_MODE\");\n    if (NULL != env_image_mode) {\n        if (0 == strcmp(\"DEFAULT\", env_image_mode)) {\n            new_image_mode = CLAY_TB_IMAGE_MODE_DEFAULT;\n        } else if (0 == strcmp(\"PLACEHOLDER\", env_image_mode)) {\n            new_image_mode = CLAY_TB_IMAGE_MODE_PLACEHOLDER;\n        } else if (0 == strcmp(\"BG\", env_image_mode)) {\n            new_image_mode = CLAY_TB_IMAGE_MODE_BG;\n        } else if (0 == strcmp(\"ASCII_FG\", env_image_mode)) {\n            new_image_mode = CLAY_TB_IMAGE_MODE_ASCII_FG;\n        } else if (0 == strcmp(\"ASCII\", env_image_mode)) {\n            new_image_mode = CLAY_TB_IMAGE_MODE_ASCII;\n        } else if (0 == strcmp(\"UNICODE\", env_image_mode)) {\n            new_image_mode = CLAY_TB_IMAGE_MODE_UNICODE;\n        } else if (0 == strcmp(\"ASCII_FG_FAST\", env_image_mode)) {\n            new_image_mode = CLAY_TB_IMAGE_MODE_ASCII_FG_FAST;\n        } else if (0 == strcmp(\"ASCII_FAST\", env_image_mode)) {\n            new_image_mode = CLAY_TB_IMAGE_MODE_ASCII_FAST;\n        } else if (0 == strcmp(\"UNICODE_FAST\", env_image_mode)) {\n            new_image_mode = CLAY_TB_IMAGE_MODE_UNICODE_FAST;\n        }\n    }\n\n    const char *env_transparency = getenv(\"CLAY_TB_TRANSPARENCY\");\n    if (NULL != env_transparency) {\n        if (0 == strcmp(\"1\", env_transparency)) {\n            new_transparency = true;\n        } else if (0 == strcmp(\"0\", env_transparency)) {\n            new_transparency = false;\n        }\n    }\n\n    const char *env_cell_pixels = getenv(\"CLAY_TB_CELL_PIXELS\");\n    if (NULL != env_cell_pixels) {\n        const char *str_width = env_cell_pixels;\n        const char *str_height = strstr(env_cell_pixels, \"x\") + 1;\n        if (NULL + 1 != str_height) {\n            bool missing_value = false;\n\n            errno = 0;\n            float cell_width = strtof(str_width, NULL);\n            if (0 != errno || 0 > cell_width) {\n                missing_value = true;\n            }\n            float cell_height = strtof(str_height, NULL);\n            if (0 != errno || 0 >= cell_height) {\n                missing_value = true;\n            }\n\n            if (!missing_value) {\n                new_pixel_size = (clay_tb_pixel_dimensions) { cell_width, cell_height };\n            }\n        }\n    }\n\n    // NO_COLOR indicates that ANSI colors shouldn't be used: https://no-color.org/\n    const char *env_nocolor = getenv(\"NO_COLOR\");\n    if (NULL != env_nocolor && '\\0' != env_nocolor[0]) {\n        new_color_mode = CLAY_TB_OUTPUT_NOCOLOR;\n    }\n\n    tb_init();\n    tb_set_input_mode(TB_INPUT_MOUSE);\n\n    // Enable mouse hover support\n    // - see https://github.com/termbox/termbox2/issues/71#issuecomment-2179581609\n    // - 1003 \"Any-event tracking\" mode\n    // - 1006 SGR extended coordinates (already enabled with TB_INPUT_MOUSE)\n    tb_sendf(\"\\x1b[?%d;%dh\", 1003, 1006);\n\n    clay_tb_initialized = true;\n\n    Clay_Termbox_Set_Color_Mode(new_color_mode);\n    Clay_Termbox_Set_Border_Mode(new_border_mode);\n    Clay_Termbox_Set_Border_Chars(new_border_chars);\n    Clay_Termbox_Set_Image_Mode(new_image_mode);\n    Clay_Termbox_Set_Transparency(new_transparency);\n    Clay_Termbox_Set_Cell_Pixel_Size(new_pixel_size.width, new_pixel_size.height);\n\n    size_t size = (size_t)tb_width() * tb_height();\n    clay_tb_color_buffer_clay = tb_malloc(sizeof(Clay_Color) * size);\n    for (int i = 0; i < size; ++i) {\n        clay_tb_color_buffer_clay[i] = (Clay_Color) { 0, 0, 0, 0 };\n    }\n}\n\nvoid Clay_Termbox_Close(void)\n{\n    if (clay_tb_initialized) {\n        // Disable mouse hover support\n        tb_sendf(\"\\x1b[?%d;%dl\", 1003, 1006);\n\n        tb_free(clay_tb_color_buffer_clay);\n        tb_shutdown();\n        clay_tb_initialized = false;\n    }\n}\n\nvoid Clay_Termbox_Render(Clay_RenderCommandArray commands)\n{\n    clay_tb_assert(clay_tb_initialized, \"Clay_Termbox_Initialize must be run first\");\n\n    clay_tb_resize_buffer();\n    clay_tb_partial_image_drawn = false;\n    clay_tb_image_fuel_used = 0;\n\n    for (int32_t i = 0; i < commands.length; ++i) {\n        const Clay_RenderCommand *command = Clay_RenderCommandArray_Get(&commands, i);\n        const clay_tb_cell_bounding_box cell_box = cell_snap_bounding_box(command->boundingBox);\n\n        int box_begin_x = CLAY__MAX(cell_box.x, 0);\n        int box_end_x = CLAY__MIN(cell_box.x + cell_box.width, tb_width());\n        int box_begin_y = CLAY__MAX(cell_box.y, 0);\n        int box_end_y = CLAY__MIN(cell_box.y + cell_box.height, tb_height());\n\n        if (box_end_x < 0 || box_end_y < 0 || tb_width() < box_begin_x\n            || tb_height() < box_begin_y) {\n            continue;\n        }\n\n        switch (command->commandType) {\n            default: {\n                clay_tb_assert(false, \"Unhandled command: %d\\n\", command->commandType);\n            }\n            case CLAY_RENDER_COMMAND_TYPE_NONE: {\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                Clay_RectangleRenderData render_data = command->renderData.rectangle;\n                Clay_Color color_fg = { 0, 0, 0, 0 };\n                Clay_Color color_bg = render_data.backgroundColor;\n                uintattr_t color_tb_fg = TB_DEFAULT;\n                uintattr_t color_tb_bg = clay_tb_color_convert(color_bg);\n\n                for (int y = box_begin_y; y < box_end_y; ++y) {\n                    for (int x = box_begin_x; x < box_end_x; ++x) {\n                        clay_tb_color_pair color_bg_new = clay_tb_get_transparency_color(\n                            x, y, (clay_tb_color_pair) { color_bg, color_tb_bg });\n                        clay_tb_set_cell(\n                            x, y, ' ', color_tb_fg, color_bg_new.termbox, color_bg_new.clay);\n                    }\n                }\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_BORDER: {\n                if (CLAY_TB_BORDER_CHARS_NONE == clay_tb_border_chars) {\n                    break;\n                }\n                Clay_BorderRenderData render_data = command->renderData.border;\n                Clay_Color color_fg = { 0, 0, 0, 1 };\n                Clay_Color color_bg = render_data.color;\n                uintattr_t color_tb_fg = TB_DEFAULT;\n                uintattr_t color_tb_bg = clay_tb_color_convert(color_bg);\n\n                int border_skip_begin_x = box_begin_x;\n                int border_skip_end_x = box_end_x;\n                int border_skip_begin_y = box_begin_y;\n                int border_skip_end_y = box_end_y;\n\n                switch (clay_tb_border_mode) {\n                    default: {\n                        clay_tb_assert(false, \"Invalid or unimplemented border mode (%d)\",\n                            clay_tb_border_mode);\n                        break;\n                    }\n                    case CLAY_TB_BORDER_MODE_MINIMUM: {\n                        // Borders will be at least one cell wide if width is nonzero\n                        // and the bounding box is large enough to not be all borders\n                        if (0 < cell_box.width) {\n                            if (0 < render_data.width.left) {\n                                border_skip_begin_x = box_begin_x\n                                    + (int)CLAY__MAX(\n                                        1, (render_data.width.left / clay_tb_cell_size.width));\n                            }\n                            if (0 < render_data.width.right) {\n                                border_skip_end_x = box_end_x\n                                    - (int)CLAY__MAX(\n                                        1, (render_data.width.right / clay_tb_cell_size.width));\n                            }\n                        }\n                        if (0 < cell_box.height) {\n                            if (0 < render_data.width.top) {\n                                border_skip_begin_y = box_begin_y\n                                    + (int)CLAY__MAX(\n                                        1, (render_data.width.top / clay_tb_cell_size.width));\n                            }\n                            if (0 < render_data.width.bottom) {\n                                border_skip_end_y = box_end_y\n                                    - (int)CLAY__MAX(\n                                        1, (render_data.width.bottom / clay_tb_cell_size.width));\n                            }\n                        }\n                        break;\n                    }\n                    case CLAY_TB_BORDER_MODE_ROUND: {\n                        int halfwidth = clay_tb_roundf(clay_tb_cell_size.width / 2);\n                        int halfheight = clay_tb_roundf(clay_tb_cell_size.height / 2);\n\n                        if (halfwidth < render_data.width.left) {\n                            border_skip_begin_x = box_begin_x\n                                + (int)CLAY__MAX(\n                                    1, (render_data.width.left / clay_tb_cell_size.width));\n                        }\n                        if (halfwidth < render_data.width.right) {\n                            border_skip_end_x = box_end_x\n                                - (int)CLAY__MAX(\n                                    1, (render_data.width.right / clay_tb_cell_size.width));\n                        }\n                        if (halfheight < render_data.width.top) {\n                            border_skip_begin_y = box_begin_y\n                                + (int)CLAY__MAX(\n                                    1, (render_data.width.top / clay_tb_cell_size.width));\n                        }\n                        if (halfheight < render_data.width.bottom) {\n                            border_skip_end_y = box_end_y\n                                - (int)CLAY__MAX(\n                                    1, (render_data.width.bottom / clay_tb_cell_size.width));\n                        }\n                        break;\n                    }\n                }\n\n                // Draw border, skipping over the center of the bounding box\n                for (int y = box_begin_y; y < box_end_y; ++y) {\n                    for (int x = box_begin_x; x < box_end_x; ++x) {\n                        if ((border_skip_begin_x <= x && x < border_skip_end_x)\n                            && (border_skip_begin_y <= y && y < border_skip_end_y)) {\n                            x = border_skip_end_x - 1;\n                            continue;\n                        }\n\n                        uint32_t ch;\n                        switch (clay_tb_border_chars) {\n                            default: {\n                                clay_tb_assert(false,\n                                    \"Invalid or unimplemented border character mode (%d)\",\n                                    clay_tb_border_chars);\n                            }\n                            case CLAY_TB_BORDER_CHARS_UNICODE: {\n                                if ((x < border_skip_begin_x)\n                                    && (y < border_skip_begin_y)) { // Top left\n                                    ch = U'\\u250c';\n                                } else if ((x >= border_skip_end_x)\n                                    && (y < border_skip_begin_y)) { // Top right\n                                    ch = U'\\u2510';\n                                } else if ((x < border_skip_begin_x)\n                                    && (y >= border_skip_end_y)) { // Bottom left\n                                    ch = U'\\u2514';\n                                } else if ((x >= border_skip_end_x)\n                                    && (y >= border_skip_end_y)) { // Bottom right\n                                    ch = U'\\u2518';\n                                } else if (x < border_skip_begin_x || x >= border_skip_end_x) {\n                                    ch = U'\\u2502';\n                                } else if (y < border_skip_begin_y || y >= border_skip_end_y) {\n                                    ch = U'\\u2500';\n                                }\n                                break;\n                            }\n                            case CLAY_TB_BORDER_CHARS_DEFAULT:\n                            case CLAY_TB_BORDER_CHARS_ASCII: {\n                                if ((x < border_skip_begin_x || x >= border_skip_end_x)\n                                    && (y < border_skip_begin_y || y >= border_skip_end_y)) {\n                                    ch = '+';\n                                } else if (x < border_skip_begin_x || x >= border_skip_end_x) {\n                                    ch = '|';\n                                } else if (y < border_skip_begin_y || y >= border_skip_end_y) {\n                                    ch = '-';\n                                }\n                                break;\n                            }\n                            case CLAY_TB_BORDER_CHARS_BLANK: {\n                                ch = ' ';\n                                break;\n                            }\n                        }\n\n                        clay_tb_color_pair color_bg_new = clay_tb_get_transparency_color(\n                            x, y, (clay_tb_color_pair) { color_bg, color_tb_bg });\n                        clay_tb_set_cell(\n                            x, y, ch, color_tb_fg, color_bg_new.termbox, color_bg_new.clay);\n                    }\n                }\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_TEXT: {\n                Clay_TextRenderData render_data = command->renderData.text;\n                Clay_Color color_fg = render_data.textColor;\n                uintattr_t color_tb_fg = clay_tb_color_convert(color_fg);\n\n                Clay_StringSlice *text = &render_data.stringContents;\n                int32_t i = 0;\n\n                // culling text characters that are outside of the layout\n                int h_clip = 0 - cell_box.x;\n                while(h_clip > 0 && i < text->length){\n                    uint32_t ch = ' ';\n                    int codepoint_length = tb_utf8_char_to_unicode(&ch, text->chars + i);\n                    if (0 > codepoint_length) {\n                        clay_tb_assert(false, \"Invalid utf8\");\n                    }\n                    i += codepoint_length;\n                    h_clip -= 1;\n                }\n\n                // printing the rest of the characters\n                for (int y = box_begin_y; y < box_end_y; ++y) {\n                    for (int x = box_begin_x; x < box_end_x;) {\n                        uint32_t ch = ' ';\n                        if (i < text->length) {\n                            int codepoint_length = tb_utf8_char_to_unicode(&ch, text->chars + i);\n                            if (0 > codepoint_length) {\n                                clay_tb_assert(false, \"Invalid utf8\");\n                            }\n                            i += codepoint_length;\n\n                            uintattr_t color_tb_bg = (clay_tb_transparency)\n                                ? TB_DEFAULT\n                                : clay_tb_color_convert(clay_tb_color_buffer_clay_get(x, y));\n                            Clay_Color color_bg = { 0 };\n                            clay_tb_color_pair color_bg_new = clay_tb_get_transparency_color(\n                                x, y, (clay_tb_color_pair) { color_bg, color_tb_bg });\n                            clay_tb_set_cell(\n                                x, y, ch, color_tb_fg, color_bg_new.termbox, color_bg_new.clay);\n                        }\n\n                        int codepoint_width = tb_wcwidth(ch);\n                        if (-1 == codepoint_width) {\n                            // Nonprintable character, use REPLACEMENT CHARACTER (U+FFFD)\n                            ch = U'\\ufffd';\n                            codepoint_width = tb_wcwidth(ch);\n                        }\n\n                        x += codepoint_width;\n                    }\n                }\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_IMAGE: {\n                Clay_ImageRenderData render_data = command->renderData.image;\n                Clay_Color color_fg = { 0, 0, 0, 0 };\n                Clay_Color color_bg = render_data.backgroundColor;\n                uintattr_t color_tb_fg = clay_tb_color_convert(color_fg);\n                uintattr_t color_tb_bg;\n\n                // Only set background to the provided color if it's non-default\n                bool color_specified\n                    = !(color_bg.r == 0 && color_bg.g == 0 && color_bg.b == 0 && color_bg.a == 0);\n                if (color_specified) {\n                    color_tb_bg = clay_tb_color_convert(color_bg);\n                }\n\n                bool use_placeholder = true;\n\n                clay_tb_image *image = (clay_tb_image *)render_data.imageData;\n\n                if (!(CLAY_TB_IMAGE_MODE_PLACEHOLDER == clay_tb_image_mode\n                        || CLAY_TB_OUTPUT_NOCOLOR == clay_tb_color_mode)) {\n                    bool convert_success = (NULL != image)\n                        ? clay_tb_image_convert(image, cell_box.width, cell_box.height)\n                        : false;\n                    if (convert_success) {\n                        use_placeholder = false;\n                    }\n                }\n\n                if (!use_placeholder) {\n                    // Render image\n                    for (int y = box_begin_y; y < box_end_y; ++y) {\n                        int y_offset = y - cell_box.y;\n                        for (int x = box_begin_x; x < box_end_x; ++x) {\n                            int x_offset = x - cell_box.x;\n                            // Fetch cells from the image's cache\n                            if (!color_specified) {\n                                if (CLAY_TB_IMAGE_MODE_ASCII_FG == clay_tb_image_mode\n                                    || CLAY_TB_IMAGE_MODE_ASCII_FG_FAST == clay_tb_image_mode) {\n                                    color_bg = (Clay_Color) { 0, 0, 0, 0 };\n                                    color_tb_bg = TB_DEFAULT;\n                                } else {\n                                    color_bg\n                                        = image->internal\n                                              .background[y_offset * cell_box.width + x_offset];\n                                    color_tb_bg = clay_tb_color_convert(color_bg);\n                                }\n                            }\n                            color_tb_fg = clay_tb_color_convert(\n                                image->internal.foreground[y_offset * cell_box.width + x_offset]);\n                            uint32_t ch\n                                = image->internal.characters[y_offset * cell_box.width + x_offset];\n                            if (CLAY_TB_IMAGE_MODE_BG == clay_tb_image_mode) {\n                                ch = ' ';\n                            }\n\n                            clay_tb_set_cell(x, y, ch, color_tb_fg, color_tb_bg, color_bg);\n                        }\n                    }\n                } else {\n                    // Render a placeholder pattern\n                    const char *placeholder_text = \"[Image]\";\n\n                    int i = 0;\n                    unsigned long len = strlen(placeholder_text);\n                    for (int y = box_begin_y; y < box_end_y; ++y) {\n                        float percent_y = (float)(y - box_begin_y) / (float)cell_box.height;\n\n                        for (int x = box_begin_x; x < box_end_x; ++x) {\n                            char ch = ' ';\n                            if (i < len) {\n                                ch = placeholder_text[i++];\n                            }\n\n                            if (!color_specified) {\n                                // Use a placeholder pattern for the image\n                                float percent_x = (float)(cell_box.width - (x - box_begin_x))\n                                    / (float)cell_box.width;\n                                if (percent_x > percent_y) {\n                                    color_bg = (Clay_Color) { 0x94, 0xb4, 0xff, 0xff };\n                                    color_tb_bg = clay_tb_color_convert(color_bg);\n                                } else {\n                                    color_bg = (Clay_Color) { 0x3f, 0xcc, 0x45, 0xff };\n                                    color_tb_bg = clay_tb_color_convert(color_bg);\n                                }\n                            }\n                            clay_tb_set_cell(x, y, ch, color_tb_fg, color_tb_bg, color_bg);\n                        }\n                    }\n                }\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {\n                clay_tb_scissor_box = (clay_tb_cell_bounding_box) {\n                    .x = box_begin_x,\n                    .y = box_begin_y,\n                    .width = box_end_x - box_begin_x,\n                    .height = box_end_y - box_begin_y,\n                };\n                clay_tb_scissor_enabled = true;\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {\n                clay_tb_scissor_enabled = false;\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {\n                break;\n            }\n        }\n    }\n}\n\nvoid Clay_Termbox_Waitfor_Event(void)\n{\n    if (clay_tb_partial_image_drawn) {\n        return;\n    }\n    int termbox_ttyfd, termbox_resizefd;\n    tb_get_fds(&termbox_ttyfd, &termbox_resizefd);\n    int nfds = CLAY__MAX(termbox_ttyfd, termbox_resizefd) + 1;\n    fd_set monitor_set;\n    FD_ZERO(&monitor_set);\n    FD_SET(termbox_ttyfd, &monitor_set);\n    FD_SET(termbox_resizefd, &monitor_set);\n    select(nfds, &monitor_set, NULL, NULL, NULL);\n}\n"
  },
  {
    "path": "renderers/termbox2/image_character_masks.h",
    "content": "#ifndef CLAY_TB_IMAGE_H\n#define CLAY_TB_IMAGE_H\n#include <stdint.h>\n\ntypedef struct {\n    uint32_t character;\n    int data[6 * 12];\n} clay_tb_character_mask;\n\n// Ascii bitmap data from the Terminus 4.49 font (https://terminus-font.sourceforge.net/)\n// Licensed under the SIL Open Font License, Version 1.1 (https://scripts.sil.org/OFL)\n#define CLAY_TB_IMAGE_SHAPES_ASCII_BEST_COUNT 95\nconst clay_tb_character_mask clay_tb_image_shapes_ascii_best[CLAY_TB_IMAGE_SHAPES_ASCII_BEST_COUNT] = {\n    {\n        .character = ' ',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '!',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '\"',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '#',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '$',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '%',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 0, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 1, 0,\n          0, 1, 0, 1, 0, 1,\n          0, 1, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '&',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 1, 0, 1, 0,\n          1, 0, 0, 1, 0, 0,\n          1, 0, 0, 1, 0, 0,\n          0, 1, 1, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '\\'',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '(',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = ')',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '*',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '+',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = ',',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '-',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '.',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '/',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '0',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 1, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 1, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '1',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '2',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '3',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 1, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '4',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 1, 1, 0,\n          0, 0, 1, 0, 1, 0,\n          0, 1, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '5',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '6',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '7',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '8',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '9',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = ':',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = ';',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '<',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '=',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '>',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '?',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '@',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 1, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 0, 1, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'A',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'B',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'C',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'D',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 0, 0, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 1, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'E',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'F',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'G',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 1, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'H',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'I',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'J',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 1, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          1, 0, 0, 1, 0, 0,\n          1, 0, 0, 1, 0, 0,\n          0, 1, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'K',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 1, 0, 0,\n          1, 0, 1, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 0, 1, 0, 0, 0,\n          1, 0, 0, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'L',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'M',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 0, 1, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'N',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 0, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 0, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'O',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'P',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'Q',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'R',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 1, 0, 0, 0,\n          1, 0, 0, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'S',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'T',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'U',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'V',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'W',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 1, 0, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'X',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'Y',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'Z',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '[',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '\\\\',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = ']',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '^',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '_',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '`',\n        .data =\n        { 0, 1, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'a',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'b',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'c',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'd',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'e',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'f',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 1, 1, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'g',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0 },\n    },\n    {\n        .character = 'h',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'i',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'j',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 1, 0, 0, 1, 0,\n          0, 0, 1, 1, 0, 0 },\n    },\n    {\n        .character = 'k',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 1, 0, 0, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 1, 0, 0, 0,\n          0, 1, 1, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'l',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'm',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'n',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'o',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'p',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'q',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0 },\n    },\n    {\n        .character = 'r',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 1, 1, 1, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 's',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 't',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'u',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'v',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'w',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          0, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'x',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'y',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0 },\n    },\n    {\n        .character = 'z',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '{',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 1, 1, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 1, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '|',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '}',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 0, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 0, 0, 1, 0, 0,\n          0, 1, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '~',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 1, 0, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 0, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n};\n\n\n#define CLAY_TB_IMAGE_SHAPES_ASCII_FAST_COUNT 15\nconst clay_tb_character_mask clay_tb_image_shapes_ascii_fast[CLAY_TB_IMAGE_SHAPES_ASCII_FAST_COUNT] = {\n    {\n        .character = ' ',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '\"',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '#',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 1, 0, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '-',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '.',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '@',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 1, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 0, 1, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'B',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'F',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'L',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '_',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = '`',\n        .data =\n        { 0, 1, 0, 0, 0, 0,\n          0, 0, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'g',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 1, 1, 1, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0 },\n    },\n    {\n        .character = 'r',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 1, 1, 1, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = 'y',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          1, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 1, 0,\n          0, 0, 0, 0, 1, 0,\n          0, 1, 1, 1, 0, 0 },\n    },\n    {\n        .character = '~',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 1, 0, 0, 1, 0,\n          1, 0, 1, 0, 1, 0,\n          1, 0, 0, 1, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n};\n\n#define CLAY_TB_IMAGE_SHAPES_UNICODE_BEST_COUNT 52\nconst clay_tb_character_mask clay_tb_image_shapes_unicode_best[CLAY_TB_IMAGE_SHAPES_UNICODE_BEST_COUNT] = {\n    {\n        .character = ' ',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2580',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2581',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2582',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2583',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2584',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2585',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2586',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2587',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2588',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2589',\n        .data =\n        { 1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0 },\n    },\n    {\n        .character = U'\\u258a',\n        .data =\n        { 1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0 },\n    },\n    {\n        .character = U'\\u258c',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u258e',\n        .data =\n        { 1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u258f',\n        .data =\n        { 1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2590',\n        .data =\n        { 0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2594',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2595',\n        .data =\n        { 0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1 },\n    },\n    {\n        .character = U'\\u2596',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2597',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2598',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2599',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u259a',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u259b',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u259c',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u259d',\n        .data =\n        { 0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u259e',\n        .data =\n        { 0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u259f',\n        .data =\n        { 0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    // TODO: END HERE\n    {\n        .character = U'\\u25e2',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 1, 1, 1, 1,\n          0, 0, 1, 1, 1, 1,\n          0, 1, 1, 1, 1, 1,\n          0, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u25e3',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u25e4',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u25e5',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          0, 1, 1, 1, 1, 1,\n          0, 1, 1, 1, 1, 1,\n          0, 0, 1, 1, 1, 1,\n          0, 0, 1, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 0 },\n    },\n\n    {\n        .character = U'\\U0001fb3c',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb3d',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\U0001fb3e',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb3f',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\U0001fb40',\n        .data =\n        { 1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb47',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 1, 1, 1 },\n    },\n    {\n        .character = U'\\U0001fb48',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\U0001fb49',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1 },\n    },\n    {\n        .character = U'\\U0001fb4a',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 1, 1, 1, 1,\n          0, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\U0001fb4b',\n        .data =\n        { 0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1 },\n    },\n    {\n        .character = U'\\U0001fb57',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb58',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb59',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb5a',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb59',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 1, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb62',\n        .data =\n        { 0, 0, 0, 1, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb63',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          0, 0, 1, 1, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb64',\n        .data =\n        { 0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb65',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          0, 1, 1, 1, 1, 1,\n          0, 0, 1, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\U0001fb66',\n        .data =\n        { 0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 1, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1,\n          0, 0, 0, 0, 0, 1 },\n    },\n};\n\n#define CLAY_TB_IMAGE_SHAPES_UNICODE_FAST_COUNT 15\nconst clay_tb_character_mask clay_tb_image_shapes_unicode_fast[CLAY_TB_IMAGE_SHAPES_UNICODE_FAST_COUNT] = {\n    {\n        .character = ' ',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2580',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2581',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2586',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2587',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2589',\n        .data =\n        { 1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0,\n          1, 1, 1, 1, 1, 0 },\n    },\n    {\n        .character = U'\\u258c',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u258f',\n        .data =\n        { 1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0,\n          1, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2594',\n        .data =\n        { 1, 1, 1, 1, 1, 1,\n          1, 1, 1, 1, 1, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2596',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u2597',\n        .data =\n        { 0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u2598',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u259a',\n        .data =\n        { 1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1 },\n    },\n    {\n        .character = U'\\u259d',\n        .data =\n        { 0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0,\n          0, 0, 0, 0, 0, 0 },\n    },\n    {\n        .character = U'\\u259e',\n        .data =\n        { 0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          0, 0, 0, 1, 1, 1,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0,\n          1, 1, 1, 0, 0, 0 },\n    },\n};\n\n#endif // CLAY_TB_IMAGE_H\n"
  },
  {
    "path": "renderers/terminal/clay_renderer_terminal_ansi.c",
    "content": "#include \"stdint.h\"\n#include \"string.h\"\n#include \"stdio.h\"\n#include \"stdlib.h\"\n\n#ifdef CLAY_OVERFLOW_TRAP\n#include \"signal.h\"\n#endif\n\nstatic inline void Console_MoveCursor(int x, int y) {\n    printf(\"\\033[%d;%dH\", y + 1, x + 1);\n}\n\nbool Clay_PointIsInsideRect(Clay_Vector2 point, Clay_BoundingBox rect) {\n    // TODO this function is a copy of Clay__PointIsInsideRect but that one is internal, I don't know if we want\n    // TODO to expose Clay__PointIsInsideRect\n    return point.x >= rect.x && point.x < rect.x + rect.width && point.y >= rect.y && point.y < rect.y + rect.height;\n}\n\nstatic inline void Console_DrawRectangle(int x0, int y0, int width, int height, Clay_Color color,\n                                         Clay_BoundingBox scissorBox) {\n    float average = (color.r + color.g + color.b + color.a) / 4 / 255;\n\n    for (int y = y0; y < height + y0; y++) {\n        for (int x = x0; x < width + x0; x++) {\n            if (!Clay_PointIsInsideRect((Clay_Vector2) {.x = x, .y = y}, scissorBox)) {\n                continue;\n            }\n\n            Console_MoveCursor(x, y);\n            // TODO this should be replaced by a better logarithmic scale if we're doing black and white\n            if (average > 0.75) {\n                printf(\"█\");\n            } else if (average > 0.5) {\n                printf(\"▓\");\n            } else if (average > 0.25) {\n                printf(\"▒\");\n            } else {\n                printf(\"░\");\n            }\n        }\n    }\n}\n\nstatic inline Clay_Dimensions\nConsole_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {\n    Clay_Dimensions textSize = {0};\n    int columnWidth = *(int *) userData;\n\n    // TODO this function is very wrong, it measures in characters, I have no idea what is the size in pixels\n\n    float maxTextWidth = 0.0f;\n    float lineTextWidth = 0;\n\n    float textHeight = 1;\n\n    for (int i = 0; i < text.length; ++i) {\n        if (text.chars[i] == '\\n') {\n            maxTextWidth = maxTextWidth > lineTextWidth ? maxTextWidth : lineTextWidth;\n            lineTextWidth = 0;\n            textHeight++;\n            continue;\n        }\n        lineTextWidth++;\n    }\n\n    maxTextWidth = maxTextWidth > lineTextWidth ? maxTextWidth : lineTextWidth;\n\n    textSize.width = maxTextWidth * columnWidth;\n    textSize.height = textHeight * columnWidth;\n\n    return textSize;\n}\n\nvoid Clay_Terminal_Render(Clay_RenderCommandArray renderCommands, int width, int height, int columnWidth) {\n    printf(\"\\033[H\\033[J\"); // Clear\n\n    const Clay_BoundingBox fullWindow = {\n            .x = 0,\n            .y = 0,\n            .width = (float) width,\n            .height = (float) height,\n    };\n\n    Clay_BoundingBox scissorBox = fullWindow;\n\n    for (int j = 0; j < renderCommands.length; j++) {\n        Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, j);\n        Clay_BoundingBox boundingBox = (Clay_BoundingBox) {\n                .x = (int)((renderCommand->boundingBox.x / columnWidth) + 0.5),\n                .y = (int)((renderCommand->boundingBox.y / columnWidth) + 0.5),\n                .width = (int)((renderCommand->boundingBox.width / columnWidth) + 0.5),\n                .height = (int)((renderCommand->boundingBox.height / columnWidth) + 0.5),\n        };\n        switch (renderCommand->commandType) {\n            case CLAY_RENDER_COMMAND_TYPE_TEXT: {\n                Clay_TextRenderData data = renderCommand->renderData.text;\n                Clay_StringSlice text = data.stringContents;\n                int y = 0;\n                for (int x = 0; x < text.length; x++) {\n                    if (text.chars[x] == '\\n') {\n                        y++;\n                        continue;\n                    }\n\n                    int cursorX = (int) boundingBox.x + x;\n                    int cursorY = (int) boundingBox.y + y;\n                    if (cursorY > scissorBox.y + scissorBox.height) {\n                        break;\n                    }\n                    if (!Clay_PointIsInsideRect((Clay_Vector2) {.x = cursorX, .y = cursorY}, scissorBox)) {\n                        continue;\n                    }\n\n                    Console_MoveCursor(cursorX, cursorY);\n                    printf(\"%c\", text.chars[x]);\n                }\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {\n                scissorBox = boundingBox;\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {\n                scissorBox = fullWindow;\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                Clay_RectangleRenderData data = renderCommand->renderData.rectangle;\n                Console_DrawRectangle(\n                        (int) boundingBox.x,\n                        (int) boundingBox.y,\n                        (int) boundingBox.width,\n                        (int) boundingBox.height,\n                        data.backgroundColor,\n                        scissorBox);\n                break;\n            }\n            case CLAY_RENDER_COMMAND_TYPE_BORDER: {\n                Clay_BorderRenderData data = renderCommand->renderData.border;\n                // Left border\n                if (data.width.left > 0) {\n                    Console_DrawRectangle(\n                            (int) (boundingBox.x),\n                            (int) (boundingBox.y + data.cornerRadius.topLeft),\n                            (int) data.width.left,\n                            (int) (boundingBox.height - data.cornerRadius.topLeft - data.cornerRadius.bottomLeft),\n                            data.color,\n                            scissorBox);\n                }\n                // Right border\n                if (data.width.right > 0) {\n                    Console_DrawRectangle(\n                            (int) (boundingBox.x + boundingBox.width - data.width.right),\n                            (int) (boundingBox.y + data.cornerRadius.topRight),\n                            (int) data.width.right,\n                            (int) (boundingBox.height - data.cornerRadius.topRight - data.cornerRadius.bottomRight),\n                            data.color,\n                            scissorBox);\n                }\n                // Top border\n                if (data.width.top > 0) {\n                    Console_DrawRectangle(\n                            (int) (boundingBox.x + data.cornerRadius.topLeft),\n                            (int) (boundingBox.y),\n                            (int) (boundingBox.width - data.cornerRadius.topLeft - data.cornerRadius.topRight),\n                            (int) data.width.top,\n                            data.color,\n                            scissorBox);\n                }\n                // Bottom border\n                if (data.width.bottom > 0) {\n                    Console_DrawRectangle(\n                            (int) (boundingBox.x + data.cornerRadius.bottomLeft),\n                            (int) (boundingBox.y + boundingBox.height - data.width.bottom),\n                            (int) (boundingBox.width - data.cornerRadius.bottomLeft - data.cornerRadius.bottomRight),\n                            (int) data.width.bottom,\n                            data.color,\n                            scissorBox);\n                }\n                break;\n            }\n            default: {\n                printf(\"Error: unhandled render command.\");\n#ifdef CLAY_OVERFLOW_TRAP\n                raise(SIGTRAP);\n#endif\n                exit(1);\n            }\n        }\n    }\n\n    Console_MoveCursor(-1, -1);  // TODO make the user not be able to write\n}\n"
  },
  {
    "path": "renderers/web/build-wasm.sh",
    "content": "cp ../../clay.h clay.c && \t\t\\\nclang \t\t\t\t\t\t\t\\\n-Os \t\t\t\t\t\t\t\\\n-DCLAY_WASM \t\t\t\t\t\\\n-mbulk-memory \t\t\t\t\t\\\n--target=wasm32 \t\t\t\t\\\n-nostdlib \t\t\t\t\t\t\\\n-Wl,--strip-all \t\t\t\t\\\n-Wl,--export-dynamic \t\t\t\\\n-Wl,--no-entry \t\t\t\t\t\\\n-Wl,--export=__heap_base \t\t\\\n-Wl,--initial-memory=6553600 \t\\\n-o clay.wasm clay.c; rm clay.c;"
  },
  {
    "path": "renderers/web/canvas2d/clay-canvas2d-renderer.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <title>Clay - UI Layout Library</title>\n    <style>\n        html,\n        body {\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            padding: 0;\n            margin: 0;\n            pointer-events: none;\n        }\n\n        @font-face {\n            font-family: 'Calistoga';\n            font-style: normal;\n            font-weight: 400;\n            src: url('/fonts/Calistoga-Regular.ttf') format('truetype');\n        }\n\n        @font-face {\n            font-family: 'Quicksand';\n            font-style: normal;\n            font-weight: 400;\n            src: url('/fonts/Quicksand-Semibold.ttf') format('truetype');\n        }\n\n        body>canvas {\n            width: 100%;\n            height: 100%;\n        }\n    </style>\n</head>\n<script type=\"module\">\n    const CLAY_RENDER_COMMAND_TYPE_NONE = 0;\n    const CLAY_RENDER_COMMAND_TYPE_RECTANGLE = 1;\n    const CLAY_RENDER_COMMAND_TYPE_BORDER = 2;\n    const CLAY_RENDER_COMMAND_TYPE_TEXT = 3;\n    const CLAY_RENDER_COMMAND_TYPE_IMAGE = 4;\n    const CLAY_RENDER_COMMAND_TYPE_SCISSOR_START = 5;\n    const CLAY_RENDER_COMMAND_TYPE_SCISSOR_END = 6;\n    const CLAY_RENDER_COMMAND_TYPE_CUSTOM = 7;\n    const GLOBAL_FONT_SCALING_FACTOR = 0.8;\n    let renderCommandSize = 0;\n    let scratchSpaceAddress = 0;\n    let heapSpaceAddress = 0;\n    let memoryDataView;\n    let textDecoder = new TextDecoder(\"utf-8\");\n    let previousFrameTime;\n    let fontsById = [\n        // YOUR FONTS HERE\n    ];\n    let elementCache = {};\n    let imageCache = {};\n    let colorDefinition = {\n        type: 'struct', members: [\n            { name: 'r', type: 'float' },\n            { name: 'g', type: 'float' },\n            { name: 'b', type: 'float' },\n            { name: 'a', type: 'float' },\n        ]\n    };\n    let stringDefinition = {\n        type: 'struct', members: [\n            { name: 'length', type: 'uint32_t' },\n            { name: 'chars', type: 'uint32_t' },\n        ]\n    };\n    let borderDefinition = {\n        type: 'struct', members: [\n            { name: 'width', type: 'uint32_t' },\n            { name: 'color', ...colorDefinition },\n        ]\n    };\n    let cornerRadiusDefinition = {\n        type: 'struct', members: [\n            { name: 'topLeft', type: 'float' },\n            { name: 'topRight', type: 'float' },\n            { name: 'bottomLeft', type: 'float' },\n            { name: 'bottomRight', type: 'float' },\n        ]\n    };\n    let rectangleConfigDefinition = {\n        name: 'rectangle', type: 'struct', members: [\n            { name: 'color', ...colorDefinition },\n            { name: 'cornerRadius', ...cornerRadiusDefinition },\n            { name: 'link', ...stringDefinition },\n            { name: 'cursorPointer', type: 'uint8_t' },\n        ]\n    };\n    let borderConfigDefinition = {\n        name: 'text', type: 'struct', members: [\n            { name: 'left', ...borderDefinition },\n            { name: 'right', ...borderDefinition },\n            { name: 'top', ...borderDefinition },\n            { name: 'bottom', ...borderDefinition },\n            { name: 'betweenChildren', ...borderDefinition },\n            { name: 'cornerRadius', ...cornerRadiusDefinition }\n        ]\n    };\n    let textConfigDefinition = {\n        name: 'text', type: 'struct', members: [\n            { name: 'textColor', ...colorDefinition },\n            { name: 'fontId', type: 'uint16_t' },\n            { name: 'fontSize', type: 'uint16_t' },\n            { name: 'letterSpacing', type: 'uint16_t' },\n            { name: 'lineHeight', type: 'uint16_t' },\n            { name: 'wrapMode', type: 'uint32_t' },\n            { name: 'disablePointerEvents', type: 'uint8_t' }\n        ]\n    };\n    let imageConfigDefinition = {\n        name: 'image', type: 'struct', members: [\n            { name: 'imageData', type: 'uint32_t' },\n            {\n                name: 'sourceDimensions', type: 'struct', members: [\n                    { name: 'width', type: 'float' },\n                    { name: 'height', type: 'float' },\n                ]\n            },\n            { name: 'sourceURL', ...stringDefinition }\n        ]\n    };\n    let customConfigDefinition = {\n        name: 'custom', type: 'struct', members: [\n            { name: 'customData', type: 'uint32_t' },\n        ]\n    }\n    let renderCommandDefinition = {\n        name: 'CLay_RenderCommand',\n        type: 'struct',\n        members: [\n            {\n                name: 'boundingBox', type: 'struct', members: [\n                    { name: 'x', type: 'float' },\n                    { name: 'y', type: 'float' },\n                    { name: 'width', type: 'float' },\n                    { name: 'height', type: 'float' },\n                ]\n            },\n            { name: 'config', type: 'uint32_t' },\n            { name: 'text', ...stringDefinition },\n            { name: 'id', type: 'uint32_t' },\n            { name: 'commandType', type: 'uint32_t', },\n        ]\n    };\n\n    function getStructTotalSize(definition) {\n        switch (definition.type) {\n            case 'union':\n            case 'struct': {\n                let totalSize = 0;\n                for (const member of definition.members) {\n                    let result = getStructTotalSize(member);\n                    if (definition.type === 'struct') {\n                        totalSize += result;\n                    } else {\n                        totalSize = Math.max(totalSize, result);\n                    }\n                }\n                return totalSize;\n            }\n            case 'float': return 4;\n            case 'uint32_t': return 4;\n            case 'uint16_t': return 2;\n            case 'uint8_t': return 1;\n            default: {\n                throw \"Unimplemented C data type \" + definition.type\n            }\n        }\n    }\n\n    function readStructAtAddress(address, definition) {\n        switch (definition.type) {\n            case 'union':\n            case 'struct': {\n                let struct = { __size: 0 };\n                for (const member of definition.members) {\n                    let result = readStructAtAddress(address, member);\n                    struct[member.name] = result;\n                    if (definition.type === 'struct') {\n                        struct.__size += result.__size;\n                        address += result.__size;\n                    } else {\n                        struct.__size = Math.max(struct.__size, result.__size);\n                    }\n                }\n                return struct;\n            }\n            case 'float': return { value: memoryDataView.getFloat32(address, true), __size: 4 };\n            case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 };\n            case 'uint16_t': return { value: memoryDataView.getUint16(address, true), __size: 2 };\n            case 'uint8_t': return { value: memoryDataView.getUint16(address, true), __size: 1 };\n            default: {\n                throw \"Unimplemented C data type \" + definition.type\n            }\n        }\n    }\n\n    function getTextDimensions(text, font) {\n        // re-use canvas object for better performance\n        window.canvasContext.font = font;\n        let metrics = window.canvasContext.measureText(text);\n        return { width: metrics.width, height: metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent };\n    }\n\n    function createMainArena(arenaStructAddress, arenaMemoryAddress) {\n        let memorySize = instance.exports.Clay_MinMemorySize();\n        // Last arg is address to store return value\n        instance.exports.Clay_CreateArenaWithCapacityAndMemory(arenaStructAddress, memorySize, arenaMemoryAddress);\n    }\n    async function init() {\n        await Promise.all(fontsById.map(f => document.fonts.load(`12px \"${f}\"`)));\n        window.htmlRoot = document.body.appendChild(document.createElement('div'));\n        window.canvasRoot = document.body.appendChild(document.createElement('canvas'));\n        window.canvasContext = window.canvasRoot.getContext(\"2d\");\n        window.mousePositionXThisFrame = 0;\n        window.mousePositionYThisFrame = 0;\n        window.mouseWheelXThisFrame = 0;\n        window.mouseWheelYThisFrame = 0;\n        window.touchDown = false;\n        let zeroTimeout = null;\n        addEventListener(\"wheel\", (event) => {\n            window.mouseWheelXThisFrame = event.deltaX * -0.1;\n            window.mouseWheelYThisFrame = event.deltaY * -0.1;\n            clearTimeout(zeroTimeout);\n            zeroTimeout = setTimeout(() => {\n                window.mouseWheelXThisFrame = 0;\n                window.mouseWheelYThisFrame = 0;\n            }, 10);\n        });\n\n        function handleTouch(event) {\n            if (event.touches.length === 1) {\n                window.touchDown = true;\n                window.mousePositionXThisFrame = event.changedTouches[0].pageX;\n                window.mousePositionYThisFrame = event.changedTouches[0].pageY;\n            }\n        }\n\n        document.addEventListener(\"touchstart\", handleTouch);\n        document.addEventListener(\"touchmove\", handleTouch);\n        document.addEventListener(\"touchend\", () => {\n            window.touchDown = false;\n            window.mousePositionXThisFrame = 0;\n            window.mousePositionYThisFrame = 0;\n        })\n\n        document.addEventListener(\"mousemove\", (event) => {\n            window.mousePositionXThisFrame = event.x;\n            window.mousePositionYThisFrame = event.y;\n        });\n\n        document.addEventListener(\"mousedown\", (event) => {\n            window.mouseDown = true;\n        });\n\n        document.addEventListener(\"keydown\", (event) => {\n            if (event.key === \"ArrowDown\") {\n                window.arrowKeyDownPressedThisFrame = true;\n            }\n            if (event.key === \"ArrowUp\") {\n                window.arrowKeyUpPressedThisFrame = true;\n            }\n            if (event.key === \"d\") {\n                window.dKeyPressedThisFrame = true;\n            }\n        });\n\n        const importObject = {\n            clay: {\n                measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig) => {\n                    let stringLength = memoryDataView.getUint32(textToMeasure, true);\n                    let pointerToString = memoryDataView.getUint32(textToMeasure + 4, true);\n                    let textConfig = readStructAtAddress(addressOfConfig, textConfigDefinition);\n                    let textDecoder = new TextDecoder(\"utf-8\");\n                    let text = textDecoder.decode(memoryDataView.buffer.slice(pointerToString, pointerToString + stringLength));\n                    let sourceDimensions = getTextDimensions(text, `${Math.round(textConfig.fontSize.value * GLOBAL_FONT_SCALING_FACTOR)}px ${fontsById[textConfig.fontId.value]}`);\n                    memoryDataView.setFloat32(addressOfDimensions, sourceDimensions.width, true);\n                    memoryDataView.setFloat32(addressOfDimensions + 4, sourceDimensions.height, true);\n                }\n            },\n        };\n        const { instance } = await WebAssembly.instantiateStreaming(\n            fetch(\"./index.wasm\"), importObject\n        );\n        memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer);\n        scratchSpaceAddress = instance.exports.__heap_base.value;\n        heapSpaceAddress = instance.exports.__heap_base.value + 1024;\n        let arenaAddress = scratchSpaceAddress;\n        window.instance = instance;\n        createMainArena(arenaAddress, heapSpaceAddress);\n        instance.exports.Clay_Initialize(arenaAddress);\n        renderCommandSize = getStructTotalSize(renderCommandDefinition);\n        renderLoop();\n    }\n\n    function renderLoopCanvas() {\n    // Note: Rendering to canvas needs to be scaled up by window.devicePixelRatio in both width and height.\n    // e.g. if we're working on a device where devicePixelRatio is 2, we need to render\n    // everything at width^2 x height^2 resolution, then scale back down with css to get the correct pixel density.\n        let capacity = memoryDataView.getUint32(scratchSpaceAddress, true);\n        let length = memoryDataView.getUint32(scratchSpaceAddress + 4, true);\n        let arrayOffset = memoryDataView.getUint32(scratchSpaceAddress + 8, true);\n        window.canvasRoot.width = window.innerWidth * window.devicePixelRatio;\n        window.canvasRoot.height = window.innerHeight * window.devicePixelRatio;\n        window.canvasRoot.style.width = window.innerWidth + 'px';\n        window.canvasRoot.style.height = window.innerHeight + 'px';\n        let ctx = window.canvasContext;\n        let scale = window.devicePixelRatio;\n        for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) {\n            let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition);\n            let boundingBox = renderCommand.boundingBox;\n            \n            // note: commandType is packed to uint8_t and has 3 garbage bytes of padding\n            switch(renderCommand.commandType.value & 0xff) {\n                case (CLAY_RENDER_COMMAND_TYPE_NONE): {\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {\n                    let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition);\n                    let color = config.color;\n                    ctx.beginPath();\n                    window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                    window.canvasContext.roundRect(\n                        boundingBox.x.value * scale, // x\n                        boundingBox.y.value * scale, // y\n                        boundingBox.width.value * scale, // width\n                        boundingBox.height.value * scale,\n                        [config.cornerRadius.topLeft.value * scale, config.cornerRadius.topRight.value * scale, config.cornerRadius.bottomRight.value * scale, config.cornerRadius.bottomLeft.value * scale]) // height;\n                    ctx.fill();\n                    ctx.closePath();\n                    // Handle link clicks\n                    let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0;\n                    memoryDataView.setUint32(0, renderCommand.id.value, true);\n                    if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {\n                        window.location.href = linkContents;\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_BORDER): {\n                    let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition);\n                    ctx.beginPath();\n                    ctx.moveTo(boundingBox.x.value * scale, boundingBox.y.value * scale);\n                    // Top Left Corner\n                    if (config.cornerRadius.topLeft.value > 0) {\n                        let lineWidth = config.top.width.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale);\n                        let color = config.top.color;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, config.cornerRadius.topLeft.value * scale);\n                        ctx.stroke();\n                    }\n                    // Top border\n                    if (config.top.width.value > 0) {\n                        let lineWidth = config.top.width.value;\n                        let halfLineWidth = lineWidth / 2;\n                        let color = config.top.color;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    // Top Right Corner\n                    if (config.cornerRadius.topRight.value > 0) {\n                        let lineWidth = config.top.width.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);\n                        let color = config.top.color;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale, config.cornerRadius.topRight.value * scale);\n                        ctx.stroke();\n                    }\n                    // Right border\n                    if (config.right.width.value > 0) {\n                        let color = config.right.color;\n                        let lineWidth = config.right.width.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.topRight.value - halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    // Bottom Right Corner\n                    if (config.cornerRadius.bottomRight.value > 0) {\n                        let color = config.top.color;\n                        let lineWidth = config.top.width.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, config.cornerRadius.bottomRight.value * scale);\n                        ctx.stroke();\n                    }\n                    // Bottom Border\n                    if (config.bottom.width.value > 0) {\n                        let color = config.bottom.color;\n                        let lineWidth = config.bottom.width.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    // Bottom Left Corner\n                    if (config.cornerRadius.bottomLeft.value > 0) {\n                        let color = config.bottom.color;\n                        let lineWidth = config.bottom.width.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, (boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomLeft.value - halfLineWidth) * scale, config.cornerRadius.bottomLeft.value * scale);\n                        ctx.stroke();\n                    }\n                    // Left Border\n                    if (config.left.width.value > 0) {\n                        let color = config.left.color;\n                        let lineWidth = config.left.width.value;\n                        let halfLineWidth = lineWidth / 2;\n                        ctx.lineWidth = lineWidth * scale;\n                        ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                        ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomLeft.value - halfLineWidth) * scale);\n                        ctx.lineTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.bottomRight.value + halfLineWidth) * scale);\n                        ctx.stroke();\n                    }\n                    ctx.closePath();\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_TEXT): {\n                    let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition);\n                    let textContents = renderCommand.text;\n                    let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value));\n                    let fontSize = config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR * scale;\n                    ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`;\n                    let color = config.textColor;\n                    ctx.textBaseline = 'middle';\n                    ctx.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                    ctx.fillText(textDecoder.decode(stringContents), boundingBox.x.value * scale, (boundingBox.y.value + boundingBox.height.value / 2 + 1) * scale);\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {\n                    window.canvasContext.save();\n                    window.canvasContext.beginPath();\n                    window.canvasContext.rect(boundingBox.x.value * scale, boundingBox.y.value * scale, boundingBox.width.value * scale, boundingBox.height.value * scale);\n                    window.canvasContext.clip();\n                    window.canvasContext.closePath();\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {\n                    window.canvasContext.restore();\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {\n                    let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition);\n                    let src = textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value)));\n                    if (!imageCache[src]) {\n                        imageCache[src] = {\n                            image: new Image(),\n                            loaded: false,\n                        }\n                        imageCache[src].image.onload = () => imageCache[src].loaded = true;\n                        imageCache[src].image.src = src;\n                    } else if (imageCache[src].loaded) {\n                        ctx.drawImage(imageCache[src].image, boundingBox.x.value * scale, boundingBox.y.value * scale, boundingBox.width.value * scale, boundingBox.height.value * scale);\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;\n            }\n        }\n    }\n\n    function renderLoop(currentTime) {\n        const elapsed = currentTime - previousFrameTime;\n        previousFrameTime = currentTime;\n        instance.exports.UpdateDrawFrame(scratchSpaceAddress, window.innerWidth, window.innerHeight, window.mouseWheelXThisFrame, window.mouseWheelYThisFrame, window.mousePositionXThisFrame, window.mousePositionYThisFrame, window.touchDown, window.mouseDown, elapsed / 1000);\n        renderLoopCanvas();\n        requestAnimationFrame(renderLoop);\n        window.mouseDown = false;\n    }\n    init();\n</script>\n\n<body>\n</body>\n\n</html>"
  },
  {
    "path": "renderers/web/html/clay-html-renderer.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <title>Clay - UI Layout Library</title>\n    <style>\n        html,\n        body {\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            padding: 0;\n            margin: 0;\n            pointer-events: none;\n        }\n\n        @font-face {\n            font-family: 'Calistoga';\n            font-style: normal;\n            font-weight: 400;\n            src: url('/fonts/Calistoga-Regular.ttf') format('truetype');\n        }\n\n        @font-face {\n            font-family: 'Quicksand';\n            font-style: normal;\n            font-weight: 400;\n            src: url('/fonts/Quicksand-Semibold.ttf') format('truetype');\n        }\n\n        body>canvas {\n            width: 100%;\n            height: 100%;\n        }\n\n        div,\n        a,\n        img {\n            position: absolute;\n            box-sizing: border-box;\n            -webkit-backface-visibility: hidden;\n        }\n\n        a {\n            cursor: pointer;\n            pointer-events: all;\n        }\n\n        .text {\n            pointer-events: all;\n            white-space: pre;\n        }\n    </style>\n</head>\n<script type=\"module\">\n    const CLAY_RENDER_COMMAND_TYPE_NONE = 0;\n    const CLAY_RENDER_COMMAND_TYPE_RECTANGLE = 1;\n    const CLAY_RENDER_COMMAND_TYPE_BORDER = 2;\n    const CLAY_RENDER_COMMAND_TYPE_TEXT = 3;\n    const CLAY_RENDER_COMMAND_TYPE_IMAGE = 4;\n    const CLAY_RENDER_COMMAND_TYPE_SCISSOR_START = 5;\n    const CLAY_RENDER_COMMAND_TYPE_SCISSOR_END = 6;\n    const CLAY_RENDER_COMMAND_TYPE_CUSTOM = 7;\n    const GLOBAL_FONT_SCALING_FACTOR = 0.8;\n    let renderCommandSize = 0;\n    let scratchSpaceAddress = 0;\n    let heapSpaceAddress = 0;\n    let memoryDataView;\n    let textDecoder = new TextDecoder(\"utf-8\");\n    let previousFrameTime;\n    let fontsById = [\n        // YOUR FONTS HERE\n    ];\n    let elementCache = {};\n    let imageCache = {};\n    let colorDefinition = {\n        type: 'struct', members: [\n            { name: 'r', type: 'float' },\n            { name: 'g', type: 'float' },\n            { name: 'b', type: 'float' },\n            { name: 'a', type: 'float' },\n        ]\n    };\n    let stringDefinition = {\n        type: 'struct', members: [\n            { name: 'length', type: 'uint32_t' },\n            { name: 'chars', type: 'uint32_t' },\n        ]\n    };\n    let borderDefinition = {\n        type: 'struct', members: [\n            { name: 'width', type: 'uint32_t' },\n            { name: 'color', ...colorDefinition },\n        ]\n    };\n    let cornerRadiusDefinition = {\n        type: 'struct', members: [\n            { name: 'topLeft', type: 'float' },\n            { name: 'topRight', type: 'float' },\n            { name: 'bottomLeft', type: 'float' },\n            { name: 'bottomRight', type: 'float' },\n        ]\n    };\n    let rectangleConfigDefinition = {\n        name: 'rectangle', type: 'struct', members: [\n            { name: 'color', ...colorDefinition },\n            { name: 'cornerRadius', ...cornerRadiusDefinition },\n            { name: 'link', ...stringDefinition },\n            { name: 'cursorPointer', type: 'uint8_t' },\n        ]\n    };\n    let borderConfigDefinition = {\n        name: 'text', type: 'struct', members: [\n            { name: 'left', ...borderDefinition },\n            { name: 'right', ...borderDefinition },\n            { name: 'top', ...borderDefinition },\n            { name: 'bottom', ...borderDefinition },\n            { name: 'betweenChildren', ...borderDefinition },\n            { name: 'cornerRadius', ...cornerRadiusDefinition }\n        ]\n    };\n    let textConfigDefinition = {\n        name: 'text', type: 'struct', members: [\n            { name: 'textColor', ...colorDefinition },\n            { name: 'fontId', type: 'uint16_t' },\n            { name: 'fontSize', type: 'uint16_t' },\n            { name: 'letterSpacing', type: 'uint16_t' },\n            { name: 'lineHeight', type: 'uint16_t' },\n            { name: 'disablePointerEvents', type: 'uint8_t' }\n        ]\n    };\n    let imageConfigDefinition = {\n        name: 'image', type: 'struct', members: [\n            { name: 'imageData', type: 'uint32_t' },\n            {\n                name: 'sourceDimensions', type: 'struct', members: [\n                    { name: 'width', type: 'float' },\n                    { name: 'height', type: 'float' },\n                ]\n            },\n            { name: 'sourceURL', ...stringDefinition }\n        ]\n    };\n    let customConfigDefinition = {\n        name: 'custom', type: 'struct', members: [\n            { name: 'customData', type: 'uint32_t' },\n        ]\n    }\n    let renderCommandDefinition = {\n        name: 'CLay_RenderCommand',\n        type: 'struct',\n        members: [\n            {\n                name: 'boundingBox', type: 'struct', members: [\n                    { name: 'x', type: 'float' },\n                    { name: 'y', type: 'float' },\n                    { name: 'width', type: 'float' },\n                    { name: 'height', type: 'float' },\n                ]\n            },\n            { name: 'config', type: 'uint32_t' },\n            { name: 'text', ...stringDefinition },\n            { name: 'id', type: 'uint32_t' },\n            { name: 'commandType', type: 'uint32_t', },\n        ]\n    };\n\n    function getStructTotalSize(definition) {\n        switch (definition.type) {\n            case 'union':\n            case 'struct': {\n                let totalSize = 0;\n                for (const member of definition.members) {\n                    let result = getStructTotalSize(member);\n                    if (definition.type === 'struct') {\n                        totalSize += result;\n                    } else {\n                        totalSize = Math.max(totalSize, result);\n                    }\n                }\n                return totalSize;\n            }\n            case 'float': return 4;\n            case 'uint32_t': return 4;\n            case 'uint16_t': return 2;\n            case 'uint8_t': return 1;\n            default: {\n                throw \"Unimplemented C data type \" + definition.type\n            }\n        }\n    }\n\n    function readStructAtAddress(address, definition) {\n        switch (definition.type) {\n            case 'union':\n            case 'struct': {\n                let struct = { __size: 0 };\n                for (const member of definition.members) {\n                    let result = readStructAtAddress(address, member);\n                    struct[member.name] = result;\n                    if (definition.type === 'struct') {\n                        struct.__size += result.__size;\n                        address += result.__size;\n                    } else {\n                        struct.__size = Math.max(struct.__size, result.__size);\n                    }\n                }\n                return struct;\n            }\n            case 'float': return { value: memoryDataView.getFloat32(address, true), __size: 4 };\n            case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 };\n            case 'uint16_t': return { value: memoryDataView.getUint16(address, true), __size: 2 };\n            case 'uint8_t': return { value: memoryDataView.getUint16(address, true), __size: 1 };\n            default: {\n                throw \"Unimplemented C data type \" + definition.type\n            }\n        }\n    }\n\n    function getTextDimensions(text, font) {\n        // re-use canvas object for better performance\n        window.canvasContext.font = font;\n        let metrics = window.canvasContext.measureText(text);\n        return { width: metrics.width, height: metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent };\n    }\n\n    function createMainArena(arenaStructAddress, arenaMemoryAddress) {\n        let memorySize = instance.exports.Clay_MinMemorySize();\n        // Last arg is address to store return value\n        instance.exports.Clay_CreateArenaWithCapacityAndMemory(arenaStructAddress, memorySize, arenaMemoryAddress);\n    }\n    async function init() {\n        await Promise.all(fontsById.map(f => document.fonts.load(`12px \"${f}\"`)));\n        window.htmlRoot = document.body.appendChild(document.createElement('div'));\n        window.canvasRoot = document.body.appendChild(document.createElement('canvas'));\n        window.canvasContext = window.canvasRoot.getContext(\"2d\");\n        window.mousePositionXThisFrame = 0;\n        window.mousePositionYThisFrame = 0;\n        window.mouseWheelXThisFrame = 0;\n        window.mouseWheelYThisFrame = 0;\n        window.touchDown = false;\n        let zeroTimeout = null;\n        addEventListener(\"wheel\", (event) => {\n            window.mouseWheelXThisFrame = event.deltaX * -0.1;\n            window.mouseWheelYThisFrame = event.deltaY * -0.1;\n            clearTimeout(zeroTimeout);\n            zeroTimeout = setTimeout(() => {\n                window.mouseWheelXThisFrame = 0;\n                window.mouseWheelYThisFrame = 0;\n            }, 10);\n        });\n\n        function handleTouch(event) {\n            if (event.touches.length === 1) {\n                window.touchDown = true;\n                window.mousePositionXThisFrame = event.changedTouches[0].pageX;\n                window.mousePositionYThisFrame = event.changedTouches[0].pageY;\n            }\n        }\n\n        document.addEventListener(\"touchstart\", handleTouch);\n        document.addEventListener(\"touchmove\", handleTouch);\n        document.addEventListener(\"touchend\", () => {\n            window.touchDown = false;\n            window.mousePositionXThisFrame = 0;\n            window.mousePositionYThisFrame = 0;\n        })\n\n        document.addEventListener(\"mousemove\", (event) => {\n            window.mousePositionXThisFrame = event.x;\n            window.mousePositionYThisFrame = event.y;\n        });\n\n        document.addEventListener(\"mousedown\", (event) => {\n            window.mouseDown = true;\n        });\n\n        document.addEventListener(\"keydown\", (event) => {\n            if (event.key === \"ArrowDown\") {\n                window.arrowKeyDownPressedThisFrame = true;\n            }\n            if (event.key === \"ArrowUp\") {\n                window.arrowKeyUpPressedThisFrame = true;\n            }\n        });\n\n        const importObject = {\n            clay: {\n                measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig) => {\n                    let stringLength = memoryDataView.getUint32(textToMeasure, true);\n                    let pointerToString = memoryDataView.getUint32(textToMeasure + 4, true);\n                    let textConfig = readStructAtAddress(addressOfConfig, textConfigDefinition);\n                    let textDecoder = new TextDecoder(\"utf-8\");\n                    let text = textDecoder.decode(memoryDataView.buffer.slice(pointerToString, pointerToString + stringLength));\n                    let sourceDimensions = getTextDimensions(text, `${Math.round(textConfig.fontSize.value * GLOBAL_FONT_SCALING_FACTOR)}px ${fontsById[textConfig.fontId.value]}`);\n                    memoryDataView.setFloat32(addressOfDimensions, sourceDimensions.width, true);\n                    memoryDataView.setFloat32(addressOfDimensions + 4, sourceDimensions.height, true);\n                }\n            },\n        };\n        const { instance } = await WebAssembly.instantiateStreaming(\n            fetch(\"./index.wasm\"), importObject\n        );\n        memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer);\n        scratchSpaceAddress = instance.exports.__heap_base.value;\n        heapSpaceAddress = instance.exports.__heap_base.value + 1024;\n        let arenaAddress = scratchSpaceAddress;\n        window.instance = instance;\n        createMainArena(arenaAddress, heapSpaceAddress);\n        instance.exports.Clay_Initialize(arenaAddress);\n        renderCommandSize = getStructTotalSize(renderCommandDefinition);\n        renderLoop();\n    }\n\n    function MemoryIsDifferent(one, two, length) {\n        for (let i = 0; i < length; i++) {\n            if (one[i] !== two[i]) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    function renderLoopHTML() {\n        let capacity = memoryDataView.getUint32(scratchSpaceAddress, true);\n        let length = memoryDataView.getUint32(scratchSpaceAddress + 4, true);\n        let arrayOffset = memoryDataView.getUint32(scratchSpaceAddress + 8, true);\n        let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }];\n        for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) {\n            let entireRenderCommandMemory = new Uint32Array(memoryDataView.buffer.slice(arrayOffset, arrayOffset + renderCommandSize));\n            let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition);\n            let parentElement = scissorStack[scissorStack.length - 1];\n            let element = null;\n            if (!elementCache[renderCommand.id.value]) {\n                let elementType = 'div';\n                switch (renderCommand.commandType.value & 0xff) {\n                    case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {\n                        if (readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition).link.length.value > 0) {\n                            elementType = 'a';\n                        }\n                        break;\n                    }\n                    case CLAY_RENDER_COMMAND_TYPE_IMAGE: { elementType = 'img'; break; }\n                    default: break;\n                }\n                element = document.createElement(elementType);\n                element.id = renderCommand.id.value;\n                if (renderCommand.commandType.value === CLAY_RENDER_COMMAND_TYPE_SCISSOR_START) {\n                    element.style.overflow = 'hidden';\n                }\n                elementCache[renderCommand.id.value] = {\n                    exists: true,\n                    element: element,\n                    previousMemoryCommand: new Uint8Array(0),\n                    previousMemoryConfig: new Uint8Array(0),\n                    previousMemoryText: new Uint8Array(0)\n                };\n            }\n\n            let elementData = elementCache[renderCommand.id.value];\n            element = elementData.element;\n            if (Array.prototype.indexOf.call(parentElement.element.children, element) !== parentElement.nextElementIndex) {\n                if (parentElement.nextElementIndex === 0 || !parentElement.element.childNodes[parentElement.nextElementIndex - 1]) {\n                    parentElement.element.insertAdjacentElement('afterbegin', element);\n                } else {\n                    parentElement.element.childNodes[parentElement.nextElementIndex - 1].insertAdjacentElement('afterend', element);\n                }\n            }\n\n            elementData.exists = true;\n            // Don't get me started. Cheaper to compare the render command memory than to update HTML elements\n            let dirty = MemoryIsDifferent(elementData.previousMemoryCommand, entireRenderCommandMemory, renderCommandSize);\n            parentElement.nextElementIndex++;\n\n            elementData.previousMemoryCommand = entireRenderCommandMemory;\n            let offsetX = scissorStack.length > 0 ? scissorStack[scissorStack.length - 1].nextAllocation.x : 0;\n            let offsetY = scissorStack.length > 0 ? scissorStack[scissorStack.length - 1].nextAllocation.y : 0;\n            if (dirty) {\n                element.style.transform = `translate(${Math.round(renderCommand.boundingBox.x.value - offsetX)}px, ${Math.round(renderCommand.boundingBox.y.value - offsetY)}px)`\n                element.style.width = Math.round(renderCommand.boundingBox.width.value) + 'px';\n                element.style.height = Math.round(renderCommand.boundingBox.height.value) + 'px';\n            }\n\n            // note: commandType is packed to uint8_t and has 3 garbage bytes of padding\n            switch(renderCommand.commandType.value & 0xff) {\n                case (CLAY_RENDER_COMMAND_TYPE_NONE): {\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {\n                    let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition);\n                    let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size));\n                    let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0;\n                    memoryDataView.setUint32(0, renderCommand.id.value, true);\n                    if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {\n                        window.location.href = linkContents;\n                    }\n                    if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) {\n                        break;\n                    }\n                    if (linkContents.length > 0) {\n                        element.href = linkContents;\n                    }\n\n                    if (linkContents.length > 0 || config.cursorPointer.value) {\n                        element.style.pointerEvents = 'all';\n                        element.style.cursor = 'pointer';\n                    }\n                    elementData.previousMemoryConfig = configMemory;\n                    let color = config.color;\n                    element.style.backgroundColor = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;\n                    if (config.cornerRadius.topLeft.value > 0) {\n                        element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';\n                    }\n                    if (config.cornerRadius.topRight.value > 0) {\n                        element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px';\n                    }\n                    if (config.cornerRadius.bottomLeft.value > 0) {\n                        element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px';\n                    }\n                    if (config.cornerRadius.bottomRight.value > 0) {\n                        element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px';\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_BORDER): {\n                    let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition);\n                    let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size));\n                    if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) {\n                        break;\n                    }\n                    elementData.previousMemoryConfig = configMemory;\n                    if (config.left.width.value > 0) {\n                        let color = config.left.color;\n                        element.style.borderLeft = `${config.left.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.right.width.value > 0) {\n                        let color = config.right.color;\n                        element.style.borderRight = `${config.right.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.top.width.value > 0) {\n                        let color = config.top.color;\n                        element.style.borderTop = `${config.top.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.bottom.width.value > 0) {\n                        let color = config.bottom.color;\n                        element.style.borderBottom = `${config.bottom.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`\n                    }\n                    if (config.cornerRadius.topLeft.value > 0) {\n                        element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';\n                    }\n                    if (config.cornerRadius.topRight.value > 0) {\n                        element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px';\n                    }\n                    if (config.cornerRadius.bottomLeft.value > 0) {\n                        element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px';\n                    }\n                    if (config.cornerRadius.bottomRight.value > 0) {\n                        element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px';\n                    }\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_TEXT): {\n                    let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition);\n                    let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size));\n                    let textContents = renderCommand.text;\n                    let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value));\n                    if (MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) {\n                        element.className = 'text';\n                        let textColor = config.textColor;\n                        let fontSize = Math.round(config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR);\n                        element.style.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`;\n                        element.style.fontFamily = fontsById[config.fontId.value];\n                        element.style.fontSize = fontSize + 'px';\n                        element.style.pointerEvents = config.disablePointerEvents.value ? 'none' : 'all';\n                        elementData.previousMemoryConfig = configMemory;\n                    }\n                    if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) {\n                        element.innerHTML = textDecoder.decode(stringContents);\n                    }\n                    elementData.previousMemoryText = stringContents;\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {\n                    scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 });\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {\n                    scissorStack.splice(scissorStack.length - 1, 1);\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {\n                    let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition);\n                    let srcContents = new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value));\n                    if (srcContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(srcContents, elementData.previousMemoryText, srcContents.length)) {\n                        element.src = textDecoder.decode(srcContents);\n                    }\n                    elementData.previousMemoryText = srcContents;\n                    break;\n                }\n                case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;\n            }\n        }\n\n        for (const key of Object.keys(elementCache)) {\n            if (elementCache[key].exists) {\n                elementCache[key].exists = false;\n            } else {\n                elementCache[key].element.remove();\n                delete elementCache[key];\n            }\n        }\n    }\n\n    function renderLoop(currentTime) {\n        const elapsed = currentTime - previousFrameTime;\n        previousFrameTime = currentTime;\n        instance.exports.UpdateDrawFrame(scratchSpaceAddress, window.innerWidth, window.innerHeight, window.mouseWheelXThisFrame, window.mouseWheelYThisFrame, window.mousePositionXThisFrame, window.mousePositionYThisFrame, window.touchDown, window.mouseDown, elapsed / 1000);\n        renderLoopHTML();\n        requestAnimationFrame(renderLoop);\n        window.mouseDown = false;\n    }\n    init();\n</script>\n\n<body>\n</body>\n\n</html>"
  },
  {
    "path": "renderers/win32_gdi/README.md",
    "content": "The windows GDI renderer example is missing the following:\n\n- Images\n- Rendering Rounded Rectangle borders\n- Custom Fonts (font size)\n"
  },
  {
    "path": "renderers/win32_gdi/clay_renderer_gdi.c",
    "content": "#include <Windows.h>\r\n\r\n#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))\r\n#include <immintrin.h>  // AVX intrinsincs for faster sqrtf\r\n#endif\r\n\r\n#include \"../../clay.h\"\r\n\r\nHDC renderer_hdcMem = {0};\r\nHBITMAP renderer_hbmMem = {0};\r\nHANDLE renderer_hOld = {0};\r\nDWORD g_dwGdiRenderFlags;\r\n\r\n#ifndef RECTWIDTH\r\n#define RECTWIDTH(rc)   ((rc).right - (rc).left)\r\n#endif\r\n#ifndef RECTHEIGHT\r\n#define RECTHEIGHT(rc)  ((rc).bottom - (rc).top)\r\n#endif\r\n\r\n// Renderer options bit flags\r\n// RF clearly stated in the name to avoid confusion with possible macro definitions for other purposes\r\n#define CLAYGDI_RF_ALPHABLEND       0x00000001\r\n#define CLAYGDI_RF_SMOOTHCORNERS    0x00000002\r\n// These are bitflags, not indexes. Next would be 0x00000004\r\n\r\ninline DWORD Clay_Win32_GetRendererFlags() { return g_dwGdiRenderFlags; }\r\n\r\n// Replaces the rendering flags with new ones provided\r\ninline void Clay_Win32_SetRendererFlags(DWORD dwFlags) { g_dwGdiRenderFlags = dwFlags; }\r\n\r\n// Returns `true` if flags were modified\r\ninline bool Clay_Win32_ModifyRendererFlags(DWORD dwRemove, DWORD dwAdd)\r\n{\r\n    DWORD dwSavedFlags = g_dwGdiRenderFlags;\r\n    DWORD dwNewFlags = (dwSavedFlags & ~dwRemove) | dwAdd;\r\n\r\n    if (dwSavedFlags == dwNewFlags)\r\n        return false;\r\n\r\n    Clay_Win32_SetRendererFlags(dwNewFlags);\r\n    return true;\r\n}\r\n\r\n\r\n/*----------------------------------------------------------------------------+\r\n | Math stuff start                                                           |\r\n +----------------------------------------------------------------------------*/\r\n// Intrinsincs wrappers\r\n#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))\r\ninline float intrin_sqrtf(const float f)\r\n{\r\n    __m128 temp = _mm_set_ss(f);\r\n    temp = _mm_sqrt_ss(temp);\r\n    return _mm_cvtss_f32(temp);\r\n}\r\n#endif\r\n\r\n// Use fast inverse square root\r\n#if defined(USE_FAST_SQRT)\r\nfloat fast_inv_sqrtf(float number)\r\n{\r\n    const float threehalfs = 1.5f;\r\n\r\n    float x2 = number * 0.5f;\r\n    float y = number;\r\n\r\n    // Evil bit-level hacking\r\n    uint32_t i = *(uint32_t*)&y;\r\n    i = 0x5f3759df - (i >> 1);  // Initial guess for Newton's method\r\n    y = *(float*)&i;\r\n\r\n    // One iteration of Newton's method\r\n    y = y * (threehalfs - (x2 * y * y)); // y = y * (1.5 - 0.5 * x * y^2)\r\n\r\n    return y;\r\n}\r\n\r\n// Fast square root approximation using the inverse square root\r\nfloat fast_sqrtf(float number)\r\n{\r\n    if (number < 0.0f) return 0.0f; // Handle negative input\r\n    return number * fast_inv_sqrtf(number);\r\n}\r\n#endif\r\n\r\n// sqrtf_impl implementation chooser\r\n#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))\r\n#define sqrtf_impl(x) intrin_sqrtf(x)\r\n#elif defined(USE_FAST_SQRT)\r\n#define sqrtf_impl(x) fast_sqrtf(x)\r\n#else\r\n#define sqrtf_impl(x) sqrtf(x)  // Fallback to std sqrtf\r\n#endif\r\n/*----------------------------------------------------------------------------+\r\n | Math stuff end                                                             |\r\n +----------------------------------------------------------------------------*/\r\n\r\nstatic inline Clay_Color ColorBlend(Clay_Color base, Clay_Color overlay, float factor)\r\n{\r\n    Clay_Color blended;\r\n\r\n    // Normalize alpha values for multiplications\r\n    float base_a = base.a / 255.0f;\r\n    float overlay_a = overlay.a / 255.0f;\r\n\r\n    overlay_a *= factor;\r\n\r\n    float out_a = overlay_a + base_a * (1.0f - overlay_a);\r\n\r\n    // Avoid division by zero and fully transparent cases\r\n    if (out_a <= 0.0f)\r\n    {\r\n        return (Clay_Color) { .a = 0, .r = 0, .g = 0, .b = 0 };\r\n    }\r\n\r\n    blended.r = (overlay.r * overlay_a + base.r * base_a * (1.0f - overlay_a)) / out_a;\r\n    blended.g = (overlay.g * overlay_a + base.g * base_a * (1.0f - overlay_a)) / out_a;\r\n    blended.b = (overlay.b * overlay_a + base.b * base_a * (1.0f - overlay_a)) / out_a;\r\n    blended.a = out_a * 255.0f; // Denormalize alpha back\r\n\r\n    return blended;\r\n}\r\n\r\nstatic float RoundedRectPixelCoverage(int x, int y, const Clay_CornerRadius radius, int width, int height) {\r\n    // Check if the pixel is in one of the four rounded corners\r\n    if (x < radius.topLeft && y < radius.topLeft) {\r\n        // Top-left corner\r\n        float dx = radius.topLeft - x - 1;\r\n        float dy = radius.topLeft - y - 1;\r\n        float distance = sqrtf_impl(dx * dx + dy * dy);\r\n        if (distance > radius.topLeft)\r\n            return 0.0f;\r\n        if (distance <= radius.topLeft - 1)\r\n            return 1.0f;\r\n        return radius.topLeft - distance;\r\n    }\r\n    else if (x >= width - radius.topRight && y < radius.topRight) {\r\n        // Top-right corner\r\n        float dx = x - (width - radius.topRight);\r\n        float dy = radius.topRight - y - 1;\r\n        float distance = sqrtf_impl(dx * dx + dy * dy);\r\n        if (distance > radius.topRight)\r\n            return 0.0f;\r\n        if (distance <= radius.topRight - 1)\r\n            return 1.0f;\r\n        return radius.topRight - distance;\r\n    }\r\n    else if (x < radius.bottomLeft && y >= height - radius.bottomLeft) {\r\n        // Bottom-left corner\r\n        float dx = radius.bottomLeft - x - 1;\r\n        float dy = y - (height - radius.bottomLeft);\r\n        float distance = sqrtf_impl(dx * dx + dy * dy);\r\n        if (distance > radius.bottomLeft)\r\n            return 0.0f;\r\n        if (distance <= radius.bottomLeft - 1)\r\n            return 1.0f;\r\n        return radius.bottomLeft - distance;\r\n    }\r\n    else if (x >= width - radius.bottomRight && y >= height - radius.bottomRight) {\r\n        // Bottom-right corner\r\n        float dx = x - (width - radius.bottomRight);\r\n        float dy = y - (height - radius.bottomRight);\r\n        float distance = sqrtf_impl(dx * dx + dy * dy);\r\n        if (distance > radius.bottomRight)\r\n            return 0.0f;\r\n        if (distance <= radius.bottomRight - 1)\r\n            return 1.0f;\r\n        return radius.bottomRight - distance;\r\n    }\r\n    else {\r\n        // Not in a corner, full coverage\r\n        return 1.0f;\r\n    }\r\n}\r\n\r\ntypedef struct {\r\n    HDC hdcMem;\r\n    HBITMAP hbmMem;\r\n    HBITMAP hbmMemPrev;\r\n    void* pBits;\r\n    SIZE size;\r\n} HDCSubstitute;\r\n\r\nstatic void CreateHDCSubstitute(HDCSubstitute* phdcs, HDC hdcSrc, PRECT prc)\r\n{\r\n    if (prc == NULL)\r\n        return;\r\n\r\n    phdcs->size = (SIZE){ RECTWIDTH(*prc), RECTHEIGHT(*prc) };\r\n    if (phdcs->size.cx <= 0 || phdcs->size.cy <= 0)\r\n        return;\r\n\r\n    phdcs->hdcMem = CreateCompatibleDC(hdcSrc);\r\n    if (phdcs->hdcMem == NULL)\r\n        return;\r\n\r\n    // Create a 32-bit DIB section for the memory DC\r\n    BITMAPINFO bmi = { 0 };\r\n    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\r\n    bmi.bmiHeader.biWidth = phdcs->size.cx;\r\n    bmi.bmiHeader.biHeight = -phdcs->size.cy;   // I think it's faster? Probably\r\n    bmi.bmiHeader.biPlanes = 1;\r\n    bmi.bmiHeader.biBitCount = 32;\r\n    bmi.bmiHeader.biCompression = BI_RGB;\r\n\r\n    phdcs->pBits = NULL;\r\n\r\n    phdcs->hbmMem = CreateDIBSection(phdcs->hdcMem, &bmi, DIB_RGB_COLORS, &phdcs->pBits, NULL, 0);\r\n    if (phdcs->hbmMem == NULL)\r\n    {\r\n        DeleteDC(phdcs->hdcMem);\r\n        return;\r\n    }\r\n\r\n    // Select the DIB section into the memory DC\r\n    phdcs->hbmMemPrev = SelectObject(phdcs->hdcMem, phdcs->hbmMem);\r\n\r\n    // Copy the content of the target DC to the memory DC\r\n    BitBlt(phdcs->hdcMem, 0, 0, phdcs->size.cx, phdcs->size.cy, hdcSrc, prc->left, prc->top, SRCCOPY);\r\n}\r\n\r\nstatic void DestroyHDCSubstitute(HDCSubstitute* phdcs)\r\n{\r\n    if (phdcs == NULL)\r\n        return;\r\n\r\n    // Clean up\r\n    SelectObject(phdcs->hdcMem, phdcs->hbmMemPrev);\r\n    DeleteObject(phdcs->hbmMem);\r\n    DeleteDC(phdcs->hdcMem);\r\n\r\n    ZeroMemory(phdcs, sizeof(HDCSubstitute));\r\n}\r\n\r\nstatic void __Clay_Win32_FillRoundRect(HDC hdc, PRECT prc, Clay_Color color, Clay_CornerRadius radius)\r\n{\r\n    HDCSubstitute substitute = { 0 };\r\n    CreateHDCSubstitute(&substitute, hdc, prc);\r\n\r\n    bool has_corner_radius = radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;\r\n\r\n    if (has_corner_radius)\r\n    {\r\n        // Limit the corner radius to the minimum of half the width and half the height\r\n        float max_radius = (float)fmin(substitute.size.cx / 2.0f, substitute.size.cy / 2.0f);\r\n        if (radius.topLeft > max_radius)        radius.topLeft = max_radius;\r\n        if (radius.topRight > max_radius)       radius.topRight = max_radius;\r\n        if (radius.bottomLeft > max_radius)     radius.bottomLeft = max_radius;\r\n        if (radius.bottomRight > max_radius)    radius.bottomRight = max_radius;\r\n    }\r\n\r\n    // Iterate over each pixel in the DIB section\r\n    uint32_t* pixels = (uint32_t*)substitute.pBits;\r\n    for (int y = 0; y < substitute.size.cy; ++y)\r\n    {\r\n        for (int x = 0; x < substitute.size.cx; ++x)\r\n        {\r\n            float coverage = 1.0f;\r\n            if (has_corner_radius)\r\n                coverage = RoundedRectPixelCoverage(x, y, radius, substitute.size.cx, substitute.size.cy);\r\n\r\n            if (coverage > 0.0f)\r\n            {\r\n                uint32_t pixel = pixels[y * substitute.size.cx + x];\r\n                Clay_Color dst_color = {\r\n                    .r = (float)((pixel >> 16) & 0xFF), // Red\r\n                    .g = (float)((pixel >> 8) & 0xFF),  // Green\r\n                    .b = (float)(pixel & 0xFF),         // Blue\r\n                    .a = 255.0f                         // Fully opaque\r\n                };\r\n                Clay_Color blended = ColorBlend(dst_color, color, coverage);\r\n\r\n                pixels[y * substitute.size.cx + x] =\r\n                    ((uint32_t)(blended.b) << 0) |\r\n                    ((uint32_t)(blended.g) << 8) |\r\n                    ((uint32_t)(blended.r) << 16);\r\n            }\r\n        }\r\n    }\r\n\r\n    // Copy the blended content back to the target DC\r\n    BitBlt(hdc, prc->left, prc->top, substitute.size.cx, substitute.size.cy, substitute.hdcMem, 0, 0, SRCCOPY);\r\n    DestroyHDCSubstitute(&substitute);\r\n}\r\n\r\nvoid Clay_Win32_Render(HWND hwnd, Clay_RenderCommandArray renderCommands, HFONT* fonts)\r\n{\r\n    bool is_clipping = false;\r\n    HRGN clipping_region = {0};\r\n\r\n    PAINTSTRUCT ps;\r\n    HDC hdc;\r\n    RECT rc; // Top left of our window\r\n\r\n    GetWindowRect(hwnd, &rc);\r\n\r\n    hdc = BeginPaint(hwnd, &ps);\r\n\r\n    int win_width = rc.right - rc.left,\r\n        win_height = rc.bottom - rc.top;\r\n\r\n    // Create an off-screen DC for double-buffering\r\n    renderer_hdcMem = CreateCompatibleDC(hdc);\r\n    renderer_hbmMem = CreateCompatibleBitmap(hdc, win_width, win_height);\r\n\r\n    renderer_hOld = SelectObject(renderer_hdcMem, renderer_hbmMem);\r\n\r\n    // draw\r\n\r\n    for (int j = 0; j < renderCommands.length; j++)\r\n    {\r\n        Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, j);\r\n        Clay_BoundingBox boundingBox = renderCommand->boundingBox;\r\n\r\n        switch (renderCommand->commandType)\r\n        {\r\n        case CLAY_RENDER_COMMAND_TYPE_TEXT:\r\n        {\r\n            Clay_Color c = renderCommand->renderData.text.textColor;\r\n            SetTextColor(renderer_hdcMem, RGB(c.r, c.g, c.b));\r\n            SetBkMode(renderer_hdcMem, TRANSPARENT);\r\n\r\n            RECT r = rc;\r\n            r.left = boundingBox.x;\r\n            r.top = boundingBox.y;\r\n            r.right = boundingBox.x + boundingBox.width + r.right;\r\n            r.bottom = boundingBox.y + boundingBox.height + r.bottom;\r\n\r\n            uint16_t font_id = renderCommand->renderData.text.fontId;\r\n            HFONT hFont = fonts[font_id];\r\n            HFONT hPrevFont = SelectObject(renderer_hdcMem, hFont);\r\n\r\n            // Actually draw text\r\n            DrawTextA(renderer_hdcMem, renderCommand->renderData.text.stringContents.chars,\r\n                      renderCommand->renderData.text.stringContents.length,\r\n                      &r, DT_TOP | DT_LEFT);\r\n\r\n            SelectObject(renderer_hdcMem, hPrevFont);\r\n\r\n            break;\r\n        }\r\n        case CLAY_RENDER_COMMAND_TYPE_RECTANGLE:\r\n        {\r\n            DWORD dwFlags = Clay_Win32_GetRendererFlags();\r\n            Clay_RectangleRenderData rrd = renderCommand->renderData.rectangle;\r\n            RECT r = rc;\r\n\r\n            r.left = boundingBox.x;\r\n            r.top = boundingBox.y;\r\n            r.right = boundingBox.x + boundingBox.width;\r\n            r.bottom = boundingBox.y + boundingBox.height;\r\n\r\n            bool translucid = false;\r\n            // There is need to check that only if alphablending is enabled.\r\n            // In other case the blending will be always opaque and we can jump to simpler FillRgn/Rect\r\n            if (dwFlags & CLAYGDI_RF_ALPHABLEND)\r\n                translucid = rrd.backgroundColor.a > 0.0f && rrd.backgroundColor.a < 255.0f;\r\n            \r\n            bool has_rounded_corners = rrd.cornerRadius.topLeft > 0.0f\r\n                || rrd.cornerRadius.topRight > 0.0f\r\n                || rrd.cornerRadius.bottomLeft > 0.0f\r\n                || rrd.cornerRadius.bottomRight > 0.0f;\r\n\r\n            // We go here if CLAYGDI_RF_SMOOTHCORNERS flag is set and one of the corners is rounded\r\n            // Also we go here if GLAYGDI_RF_ALPHABLEND flag is set and the fill color is translucid\r\n            if ((dwFlags & CLAYGDI_RF_ALPHABLEND) && translucid || (dwFlags & CLAYGDI_RF_SMOOTHCORNERS) && has_rounded_corners)\r\n            {\r\n                __Clay_Win32_FillRoundRect(renderer_hdcMem, &r, rrd.backgroundColor, rrd.cornerRadius);\r\n            }\r\n            else\r\n            {\r\n                HBRUSH recColor = CreateSolidBrush(RGB(rrd.backgroundColor.r, rrd.backgroundColor.g, rrd.backgroundColor.b));\r\n\r\n                if (has_rounded_corners)\r\n                {\r\n                    HRGN roundedRectRgn = CreateRoundRectRgn(\r\n                        r.left, r.top, r.right + 1, r.bottom + 1,\r\n                        rrd.cornerRadius.topLeft * 2, rrd.cornerRadius.topLeft * 2);\r\n\r\n                    FillRgn(renderer_hdcMem, roundedRectRgn, recColor);\r\n                    DeleteObject(roundedRectRgn);\r\n                }\r\n                else\r\n                {\r\n                    FillRect(renderer_hdcMem, &r, recColor);\r\n                }\r\n\r\n                DeleteObject(recColor);\r\n            }\r\n\r\n            break;\r\n        }\r\n\r\n        // The renderer should begin clipping all future draw commands, only rendering content that falls within the provided boundingBox.\r\n        case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START:\r\n        {\r\n            is_clipping = true;\r\n\r\n            clipping_region = CreateRectRgn(boundingBox.x,\r\n                                            boundingBox.y,\r\n                                            boundingBox.x + boundingBox.width,\r\n                                            boundingBox.y + boundingBox.height);\r\n\r\n            SelectClipRgn(renderer_hdcMem, clipping_region);\r\n            break;\r\n        }\r\n\r\n        // The renderer should finish any previously active clipping, and begin rendering elements in full again.\r\n        case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END:\r\n        {\r\n            SelectClipRgn(renderer_hdcMem, NULL);\r\n\r\n            if (clipping_region)\r\n            {\r\n                DeleteObject(clipping_region);\r\n            }\r\n\r\n            is_clipping = false;\r\n            clipping_region = NULL;\r\n\r\n            break;\r\n        }\r\n\r\n        // The renderer should draw a colored border inset into the bounding box.\r\n        case CLAY_RENDER_COMMAND_TYPE_BORDER:\r\n        {\r\n            Clay_BorderRenderData brd = renderCommand->renderData.border;\r\n            RECT r = rc;\r\n\r\n            r.left = boundingBox.x;\r\n            r.top = boundingBox.y;\r\n            r.right = boundingBox.x + boundingBox.width;\r\n            r.bottom = boundingBox.y + boundingBox.height;\r\n\r\n            HPEN topPen = CreatePen(PS_SOLID, brd.width.top, RGB(brd.color.r, brd.color.g, brd.color.b));\r\n            HPEN leftPen = CreatePen(PS_SOLID, brd.width.left, RGB(brd.color.r, brd.color.g, brd.color.b));\r\n            HPEN bottomPen = CreatePen(PS_SOLID, brd.width.bottom, RGB(brd.color.r, brd.color.g, brd.color.b));\r\n            HPEN rightPen = CreatePen(PS_SOLID, brd.width.right, RGB(brd.color.r, brd.color.g, brd.color.b));\r\n\r\n            HPEN oldPen = SelectObject(renderer_hdcMem, topPen);\r\n\r\n            if (brd.cornerRadius.topLeft == 0)\r\n            {\r\n                MoveToEx(renderer_hdcMem, r.left, r.top, NULL);\r\n                LineTo(renderer_hdcMem, r.right, r.top);\r\n\r\n                SelectObject(renderer_hdcMem, leftPen);\r\n                MoveToEx(renderer_hdcMem, r.left, r.top, NULL);\r\n                LineTo(renderer_hdcMem, r.left, r.bottom);\r\n\r\n                SelectObject(renderer_hdcMem, bottomPen);\r\n                MoveToEx(renderer_hdcMem, r.left, r.bottom, NULL);\r\n                LineTo(renderer_hdcMem, r.right, r.bottom);\r\n\r\n                SelectObject(renderer_hdcMem, rightPen);\r\n                MoveToEx(renderer_hdcMem, r.right, r.top, NULL);\r\n                LineTo(renderer_hdcMem, r.right, r.bottom);\r\n            }\r\n            else\r\n            {\r\n                // todo: i should be rounded\r\n                MoveToEx(renderer_hdcMem, r.left, r.top, NULL);\r\n                LineTo(renderer_hdcMem, r.right, r.top);\r\n\r\n                SelectObject(renderer_hdcMem, leftPen);\r\n                MoveToEx(renderer_hdcMem, r.left, r.top, NULL);\r\n                LineTo(renderer_hdcMem, r.left, r.bottom);\r\n\r\n                SelectObject(renderer_hdcMem, bottomPen);\r\n                MoveToEx(renderer_hdcMem, r.left, r.bottom, NULL);\r\n                LineTo(renderer_hdcMem, r.right, r.bottom);\r\n\r\n                SelectObject(renderer_hdcMem, rightPen);\r\n                MoveToEx(renderer_hdcMem, r.right, r.top, NULL);\r\n                LineTo(renderer_hdcMem, r.right, r.bottom);\r\n                \r\n            }\r\n\r\n            SelectObject(renderer_hdcMem, oldPen);\r\n            DeleteObject(topPen);\r\n            DeleteObject(leftPen);\r\n            DeleteObject(bottomPen);\r\n            DeleteObject(rightPen);\r\n\r\n            break;\r\n        }\r\n\r\n            // case CLAY_RENDER_COMMAND_TYPE_IMAGE:\r\n            // {\r\n            //     // TODO: i couldnt get the win 32 api to load a bitmap.... So im punting on this one :(\r\n            //     break;\r\n            // }\r\n\r\n        default:\r\n            printf(\"Unhandled render command %d\\r\\n\", renderCommand->commandType);\r\n            break;\r\n        }\r\n    }\r\n\r\n    BitBlt(hdc, 0, 0, win_width, win_height, renderer_hdcMem, 0, 0, SRCCOPY);\r\n\r\n    // Free-up the off-screen DC\r\n    SelectObject(renderer_hdcMem, renderer_hOld);\r\n    DeleteObject(renderer_hbmMem);\r\n    DeleteDC(renderer_hdcMem);\r\n\r\n    EndPaint(hwnd, &ps);\r\n}\r\n\r\n/*\r\n    Hacks due to the windows api not making sence to use.... may measure too large, but never too small\r\n*/\r\n\r\n#ifndef WIN32_FONT_HEIGHT\r\n#define WIN32_FONT_HEIGHT (16)\r\n#endif\r\n\r\n#ifndef WIN32_FONT_WIDTH\r\n#define WIN32_FONT_WIDTH (8)\r\n#endif\r\n\r\nstatic inline Clay_Dimensions Clay_Win32_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData)\r\n{\r\n    Clay_Dimensions textSize = {0};\r\n\r\n    if (userData != NULL)\r\n    {\r\n        HFONT* fonts = (HFONT*)userData;\r\n        HFONT hFont = fonts[config->fontId];\r\n\r\n        if (hFont != NULL)\r\n        {\r\n            HDC hScreenDC = GetDC(NULL);\r\n            HDC hTempDC = CreateCompatibleDC(hScreenDC);\r\n\r\n            if (hTempDC != NULL)\r\n            {\r\n                HFONT hPrevFont = SelectObject(hTempDC, hFont);\r\n\r\n                SIZE size;\r\n                GetTextExtentPoint32(hTempDC, text.chars, text.length, &size);\r\n\r\n                textSize.width = size.cx;\r\n                textSize.height = size.cy;\r\n\r\n                SelectObject(hScreenDC, hPrevFont);\r\n                DeleteDC(hTempDC);\r\n\r\n                return textSize;\r\n            }\r\n\r\n            ReleaseDC(HWND_DESKTOP, hScreenDC);\r\n        }\r\n    }\r\n\r\n    // Fallback for system bitmap font\r\n    float maxTextWidth = 0.0f;\r\n    float lineTextWidth = 0;\r\n    float textHeight = WIN32_FONT_HEIGHT;\r\n\r\n    for (int i = 0; i < text.length; ++i)\r\n    {\r\n        if (text.chars[i] == '\\n')\r\n        {\r\n            maxTextWidth = fmax(maxTextWidth, lineTextWidth);\r\n            lineTextWidth = 0;\r\n            continue;\r\n        }\r\n\r\n        lineTextWidth += WIN32_FONT_WIDTH;\r\n    }\r\n\r\n    maxTextWidth = fmax(maxTextWidth, lineTextWidth);\r\n\r\n    textSize.width = maxTextWidth;\r\n    textSize.height = textHeight;\r\n\r\n    return textSize;\r\n}\r\n\r\nHFONT Clay_Win32_SimpleCreateFont(const char* filePath, const char* family, int height, int weight)\r\n{\r\n    // Add the font resource to the application instance\r\n    int fontAdded = AddFontResourceEx(filePath, FR_PRIVATE, NULL);\r\n    if (fontAdded == 0) {\r\n        return NULL;\r\n    }\r\n\r\n    int fontHeight = height;\r\n\r\n    // If negative, treat height as Pt rather than pixels\r\n    if (height < 0) {\r\n        // Get the screen DPI\r\n        HDC hScreenDC = GetDC(NULL);\r\n        int iScreenDPI = GetDeviceCaps(hScreenDC, LOGPIXELSY);\r\n        ReleaseDC(HWND_DESKTOP, hScreenDC);\r\n\r\n        // Convert font height from points to pixels\r\n        fontHeight = MulDiv(height, iScreenDPI, 72);\r\n    }\r\n\r\n    // Create the font using the calculated height and the font name\r\n    HFONT hFont = CreateFont(fontHeight, 0, 0, 0, weight, FALSE, FALSE, FALSE,\r\n        ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,\r\n        DEFAULT_PITCH, family);\r\n\r\n    return hFont;\r\n}\r\n"
  },
  {
    "path": "tests/docker-compose.yml",
    "content": "services:\n  gcc:\n    build:\n      context: ../\n      dockerfile: tests/gcc/9.4/Dockerfile\n    volumes:\n      - /tmp/clay/_deps"
  },
  {
    "path": "tests/gcc/9.4/Dockerfile",
    "content": "FROM --platform=linux/amd64 ubuntu:20.04\n\nRUN apt update -y\nRUN DEBIAN_FRONTEND=noninteractive apt install -y build-essential\nRUN DEBIAN_FRONTEND=noninteractive apt install -y wget\nWORKDIR /tmp/\nRUN wget https://github.com/Kitware/CMake/releases/download/v3.28.4/cmake-3.28.4-linux-x86_64.tar.gz\nRUN tar zxvf cmake-3.28.4-linux-x86_64.tar.gz\nRUN DEBIAN_FRONTEND=noninteractive apt install -y git\nRUN DEBIAN_FRONTEND=noninteractive apt install -y libwayland-dev\nRUN DEBIAN_FRONTEND=noninteractive apt install -y pkg-config\nRUN DEBIAN_FRONTEND=noninteractive apt install -y libxkbcommon-dev\nRUN DEBIAN_FRONTEND=noninteractive apt install -y xorg-dev\n\nADD . /tmp/clay\n\nWORKDIR /tmp/clay\n\nCMD /tmp/cmake-3.28.4-linux-x86_64/bin/cmake . && /tmp/cmake-3.28.4-linux-x86_64/bin/cmake --build ."
  },
  {
    "path": "tests/run-tests.sh",
    "content": "docker compose build && docker compose up && echo \"Tests complete.\""
  }
]