[
  {
    "path": ".clang-format",
    "content": "# Reference: https://clang.llvm.org/docs/ClangFormatStyleOptions.html\n# VS 2026 uses 20.1.8\n# https://releases.llvm.org/20.1.0/tools/clang/docs/ClangFormatStyleOptions.html\n# https://clang-format-configurator.site/\n# default values:\n# https://github.com/llvm/llvm-project/blob/main/clang/lib/Format/Format.cpp\n---\nLanguage: Cpp\nBasedOnStyle: Microsoft\n\nBreakBeforeBraces: Custom\nBraceWrapping:\n  AfterCaseLabel: true\n  AfterUnion: true\n  BeforeWhile: true\n\nColumnLimit: 130\nPointerAlignment: Left\nUseTab: Always\nBreakConstructorInitializers: BeforeComma\n\nInsertNewlineAtEOF: true\nIncludeBlocks: Regroup\n\nIncludeCategories:\n  - Regex:           '^\"(box2d\\/)'\n    Priority:        3\n  - Regex:           '^<.*'\n    Priority:        4\n  - Regex:           '^\"[^.]*\\.h\"'\n    Priority:        1\n  - Regex:           '^\"[^.]*\\.inl\"'\n    Priority:        2\n\nIndentExternBlock: NoIndent\nIndentCaseLabels: true\nIndentAccessModifiers: false\nAccessModifierOffset: -4\n\nSpacesInParens: Custom\nSpacesInParensOptions:\n  ExceptDoubleParentheses: false\n  InConditionalStatements: true\n  InCStyleCasts: false\n  InEmptyParentheses: true\n  Other: true\n  "
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n\n*.c text\n*.cpp text\n*.h text\n*.md text\n*.txt text\n*.sh text eol=lf\n\n*.ttf binary\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [erincatto]\npatreon: Box2D\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/issue_template.md",
    "content": "Make sure these boxes are checked before submitting your issue - thank you!\n\n- [ ] Ask for help on [discord](https://discord.gg/NKYgCBP)\n- [ ] Consider providing a dump file using b2World::Dump\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "Pull requests for core Box2D code are generally not accepted. Please consider filing an issue instead.\n\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: CI Build\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\nenv:\n  BUILD_TYPE: Debug\n\njobs:\n  build-ubuntu-gcc:\n    name: ubuntu-gcc\n    runs-on: ubuntu-latest\n    timeout-minutes: 4\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBOX2D_SAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DBOX2D_VALIDATE=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON\n\n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n    - name: Test\n      working-directory: ${{github.workspace}}/build\n      run: ./bin/test\n      \n  build-ubuntu-clang:\n    name: ubuntu-clang\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v4\n    \n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_C_COMPILER=clang -DBOX2D_SAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DBOX2D_VALIDATE=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON\n\n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n    - name: Test\n      working-directory: ${{github.workspace}}/build\n      run: ./bin/test\n      \n  build-macos:\n    name: macos\n    runs-on: macos-latest\n\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBOX2D_SAMPLES=OFF -DBOX2D_SANITIZE=ON -DBUILD_SHARED_LIBS=OFF -DBOX2D_VALIDATE=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON\n\n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n    - name: Test\n      working-directory: ${{github.workspace}}/build\n      run: ./bin/test\n      \n  build-windows:\n    name: windows\n    runs-on: windows-latest\n    steps:\n\n    - uses: actions/checkout@v4\n\n    - name: Setup MSVC dev command prompt\n      uses: TheMrMilchmann/setup-msvc-dev@v3\n      with:\n        arch: x64\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBOX2D_SAMPLES=OFF -DBOX2D_SANITIZE=ON -DBUILD_SHARED_LIBS=OFF -DBOX2D_VALIDATE=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON\n      # run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBOX2D_SAMPLES=OFF -DBUILD_SHARED_LIBS=OFF\n\n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n    - name: Test\n      working-directory: ${{github.workspace}}/build\n      run: ./bin/${{env.BUILD_TYPE}}/test\n      \n  samples-windows-static:\n    name: samples-windows-static\n    runs-on: windows-latest\n    steps:\n\n    - uses: actions/checkout@v4\n\n    - name: Setup MSVC dev command prompt\n      uses: TheMrMilchmann/setup-msvc-dev@v3\n      with:\n        arch: x64\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release -DBOX2D_SAMPLES=ON -DBUILD_SHARED_LIBS=OFF -DBOX2D_UNIT_TESTS=OFF\n      \n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config Release\n\n  samples-windows-dynamic:\n    name: samples-windows-dynamic\n    runs-on: windows-latest\n    steps:\n\n    - uses: actions/checkout@v4\n\n    - name: Setup MSVC dev command prompt\n      uses: TheMrMilchmann/setup-msvc-dev@v3\n      with:\n        arch: x64\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release -DBOX2D_SAMPLES=ON -DBUILD_SHARED_LIBS=ON -DBOX2D_UNIT_TESTS=OFF\n      \n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config Release\n\n  samples-macos-static:\n    name: samples-macos-static\n    runs-on: macos-latest\n\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release -DBOX2D_SAMPLES=ON -DBUILD_SHARED_LIBS=OFF -DBOX2D_UNIT_TESTS=OFF\n      \n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config Release\n\n  samples-macos-dynamic:\n    name: samples-macos-dynamic\n    runs-on: macos-latest\n\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release -DBOX2D_SAMPLES=ON -DBUILD_SHARED_LIBS=ON -DBOX2D_UNIT_TESTS=OFF\n      \n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config Release\n\n  samples-ubuntu-gcc-static:\n    name: samples-ubuntu-gcc-static\n    runs-on: ubuntu-latest\n    timeout-minutes: 4\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Install X11, Wayland & GL development libraries\n      run: sudo apt-get update && sudo apt-get install -y libx11-dev wayland-protocols libwayland-dev libxkbcommon-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release -DBOX2D_SAMPLES=ON -DBUILD_SHARED_LIBS=OFF -DBOX2D_UNIT_TESTS=OFF\n      \n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config Release\n      "
  },
  {
    "path": ".gitignore",
    "content": "build/\nimgui.ini\nsettings.ini\n.vscode/\n.vs/\n.cap\n.DS_Store\nCMakeUserPresets.json\n.cache/\n.idea/\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "﻿cmake_minimum_required(VERSION 3.22)\ninclude(FetchContent)\ninclude(CMakeDependentOption)\n\nproject(box2d\n\tVERSION 3.2.0\n\tDESCRIPTION \"A 2D physics engine for games\"\n\tHOMEPAGE_URL \"https://box2d.org\"\n\tLANGUAGES C CXX\n)\n\n# stuff to help debug cmake\n# message(STATUS \"cmake tool chain: ${CMAKE_TOOLCHAIN_FILE}\")\n# message(STATUS \"cmake source dir: ${CMAKE_SOURCE_DIR}\")\n# message(STATUS \"library postfix: ${CMAKE_DEBUG_POSTFIX}\")\nmessage(STATUS \"CMake C compiler: ${CMAKE_C_COMPILER_ID}\")\nmessage(STATUS \"CMake C++ compiler: ${CMAKE_CXX_COMPILER_ID}\")\nmessage(STATUS \"CMake system name: ${CMAKE_SYSTEM_NAME}\")\nmessage(STATUS \"CMake host system processor: ${CMAKE_HOST_SYSTEM_PROCESSOR}\")\n\n# static link to make leak checking easier (_crtBreakAlloc)\n# set(CMAKE_MSVC_RUNTIME_LIBRARY \"MultiThreaded$<$<CONFIG:Debug>:Debug>\")\n\nif (MSVC OR APPLE)\n\toption(BOX2D_SANITIZE \"Enable sanitizers for some builds\" OFF)\n\tif(BOX2D_SANITIZE)\n\t\tmessage(STATUS \"Box2D Sanitize\")\n\t\t# sanitizers need to apply to all compiled libraries to work well\n\t\tif(MSVC)\n\t\t\t# address sanitizer only in the debug build\n\t\t\tadd_compile_options(\"$<$<CONFIG:Debug>:/fsanitize=address>\")\n\t\t\tadd_link_options(\"$<$<CONFIG:Debug>:/INCREMENTAL:NO>\")\n\t\telseif(APPLE)\n\t\t\tadd_compile_options(-fsanitize=address -fsanitize-address-use-after-scope -fsanitize=undefined)\n\t\t\tadd_link_options(-fsanitize=address -fsanitize-address-use-after-scope -fsanitize=undefined)\n\t\tendif()\n\telse()\n\t\tif(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL \"MSVC\" AND PROJECT_IS_TOP_LEVEL)\n\t\t\t# enable hot reloading\n\t\t\tadd_compile_options(\"$<$<CONFIG:Debug>:/ZI>\")\n\t\t\tadd_link_options(\"$<$<CONFIG:Debug>:/INCREMENTAL>\")\n\t\tendif()\n\tendif()\nendif()\n\n# Deterministic math\n# https://box2d.org/posts/2024/08/determinism/\nif (MINGW OR APPLE OR UNIX)\n\tadd_compile_options(-ffp-contract=off)\nendif()\n\noption(BOX2D_DISABLE_SIMD \"Disable SIMD math (slower)\" OFF)\noption(BOX2D_COMPILE_WARNING_AS_ERROR \"Compile warnings as errors\" OFF)\n\nif(CMAKE_SYSTEM_PROCESSOR MATCHES \"x86_64|AMD64\")\n\tcmake_dependent_option(BOX2D_AVX2 \"Enable AVX2\" OFF \"NOT BOX2D_DISABLE_SIMD\" OFF)\nendif()\n\nset(CMAKE_EXPORT_COMPILE_COMMANDS ON)\nset_property(GLOBAL PROPERTY USE_FOLDERS ON)\nset(CMAKE_VERBOSE_MAKEFILE ON)\n\n# Needed for samples.exe to find box2d.dll\nset(CMAKE_LIBRARY_OUTPUT_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/bin\")\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/bin\")\n\nadd_subdirectory(src)\n\n# This hides samples, test, and doxygen from apps that use box2d via FetchContent\nif(PROJECT_IS_TOP_LEVEL)\n\toption(BOX2D_SAMPLES \"Build the Box2D samples\" ON)\n\toption(BOX2D_BENCHMARKS \"Build the Box2D benchmarks\" OFF)\n\toption(BOX2D_DOCS \"Build the Box2D documentation\" OFF)\n\toption(BOX2D_PROFILE \"Enable profiling with Tracy\" OFF)\n\toption(BOX2D_VALIDATE \"Enable heavy validation\" ON)\n\toption(BOX2D_UNIT_TESTS \"Build the Box2D unit tests\" ON)\n\t\n\tif(BOX2D_UNIT_TESTS OR BOX2D_SAMPLES OR BOX2D_BENCHMARKS)\n\t\t# Emscripten pthread support for enkiTS\n\t\tif(EMSCRIPTEN)\n\t\t\tset(EMSCRIPTEN_PTHREADS_COMPILER_FLAGS \"-pthread -s USE_PTHREADS=1\")\n\t\t\tset(EMSCRIPTEN_PTHREADS_LINKER_FLAGS \"${EMSCRIPTEN_PTHREADS_COMPILER_FLAGS} -s ALLOW_MEMORY_GROWTH\")\n\t\t\tstring(APPEND CMAKE_C_FLAGS \" ${EMSCRIPTEN_PTHREADS_COMPILER_FLAGS}\")\n\t\t\tstring(APPEND CMAKE_CXX_FLAGS \" ${EMSCRIPTEN_PTHREADS_COMPILER_FLAGS}\")\n\t\t\tstring(APPEND CMAKE_EXE_LINKER_FLAGS \" ${EMSCRIPTEN_PTHREADS_LINKER_FLAGS}\")\n\t\tendif()\n\n\t\t# Task system used in tests and samples\n\t\tset(ENKITS_BUILD_EXAMPLES OFF CACHE BOOL \"Build enkiTS examples\")\n\t\tFetchContent_Declare(\n\t\t\tenkits\n\t\t\tGIT_REPOSITORY https://github.com/dougbinks/enkiTS.git\n\t\t\tGIT_TAG master\n\t\t\tGIT_SHALLOW TRUE\n\t\t\tGIT_PROGRESS TRUE\n\t\t)\n\t\tFetchContent_MakeAvailable(enkits)\n\n\t\tadd_subdirectory(shared)\n\tendif()\n\n\t# Tests need static linkage because they test internal Box2D functions\n\tif(NOT BUILD_SHARED_LIBS AND BOX2D_UNIT_TESTS)\n\t\tmessage(STATUS \"Adding Box2D unit tests\")\n\t\tadd_subdirectory(test)\n\t\tset_target_properties(test PROPERTIES XCODE_GENERATE_SCHEME TRUE)\n\telse()\n\t\tmessage(STATUS \"Skipping Box2D unit tests\")\n\tendif()\n\n\tif(BOX2D_SAMPLES)\n\t\tadd_subdirectory(samples)\n\n\t\t# default startup project for Visual Studio\n\t\tif(MSVC)\n\t\t\tset_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT samples)\n\t\t\tset_property(TARGET samples PROPERTY VS_DEBUGGER_WORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\")\n\t\tendif()\n\n\t\tif(APPLE)\n\t\t\tset_target_properties(samples PROPERTIES\n\t\t\t\tXCODE_GENERATE_SCHEME TRUE\n\t\t\t\tXCODE_SCHEME_WORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\")\n\t\tendif()\n\tendif()\n\n\tif(BOX2D_BENCHMARKS)\n\t\tadd_subdirectory(benchmark)\n\t\tset_target_properties(benchmark PROPERTIES XCODE_GENERATE_SCHEME TRUE)\n\tendif()\n\n\tif(BOX2D_DOCS)\n\t\tadd_subdirectory(docs)\n\tendif()\nendif()\n\n# # Building on clang in windows\n# cmake -S .. -B . -G \"Visual Studio 17 2022\" -A x64 -T ClangCL\n# https://clang.llvm.org/docs/UsersManual.html#clang-cl\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Erin Catto\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![Box2D Logo](https://box2d.org/images/logo.svg)\n\n# Build Status\n[![Build Status](https://github.com/erincatto/box2d/actions/workflows/build.yml/badge.svg)](https://github.com/erincatto/box2d/actions)\n\n# Box2D \nBox2D is a 2D physics engine for games.\n\n[![Box2D Version 3.0 Release Demo](https://img.youtube.com/vi/dAoM-xjOWtA/0.jpg)](https://www.youtube.com/watch?v=dAoM-xjOWtA)\n\n## Features\n\n### Collision\n- Continuous collision detection\n- Contact events\n- Convex polygons, capsules, circles, rounded polygons, segments, and chains\n- Multiple shapes per body\n- Collision filtering\n- Ray casts, shape casts, and overlap queries\n- Sensor system\n\n### Physics\n- Robust _Soft Step_ rigid body solver\n- Continuous physics for fast translations and rotations\n- Island based sleep\n- Revolute, prismatic, distance, mouse joint, weld, and wheel joints\n- Joint limits, motors, springs, and friction\n- Joint and contact forces\n- Body movement events and sleep notification\n\n### System\n- Data-oriented design\n- Written in portable C17\n- Extensive multithreading and SIMD\n- Optimized for large piles of bodies\n\n### Samples\n- OpenGL with GLFW and enkiTS\n- Graphical user interface with imgui\n- Many samples to demonstrate features and performance\n\n## Building for Visual Studio\n- Install [CMake](https://cmake.org/)\n- Ensure CMake is in the user `PATH`\n- Run `create_sln.bat`\n- Open and build `build/box2d.sln`\n\n## Building for Linux\n- Run `build.sh` from a bash shell\n- Results are in the build sub-folder\n\n## Building for Xcode\n- Install [CMake](https://cmake.org)\n- Add Cmake to the path in .zprofile (the default Terminal shell is zsh)\n    - export PATH=\"/Applications/CMake.app/Contents/bin:$PATH\"\n- mkdir build\n- cd build\n- cmake -G Xcode ..\n- Open `box2d.xcodeproj`\n- Select the samples scheme\n- Build and run the samples\n\n## Building and installing\n- mkdir build\n- cd build\n- cmake ..\n- cmake --build . --config Release\n- cmake --install . (might need sudo)\n\n## Compatibility\nThe Box2D library and samples build and run on Windows, Linux, and Mac.\n\nYou will need a compiler that supports C17 to build the Box2D library.\n\nYou will need a compiler that supports C++20 to build the samples.\n\nBox2D uses SSE2 and Neon SIMD math to improve performance. This can be disabled by defining `BOX2D_DISABLE_SIMD`.\n\n## Documentation\n- [Manual](https://box2d.org/documentation/)\n- [Migration Guide](https://github.com/erincatto/box2d/blob/main/docs/migration.md)\n\n## Community\n- [Discord](https://discord.gg/NKYgCBP)\n\n## Contributing\nPlease do not submit pull requests. Instead, please file an issue for bugs or feature requests. For support, please visit the Discord server.\n\n# Giving Feedback\nPlease file an issue or start a chat on discord. You can also use [GitHub Discussions](https://github.com/erincatto/box2d/discussions).\n\n## License\nBox2D is developed by Erin Catto and uses the [MIT license](https://en.wikipedia.org/wiki/MIT_License).\n\n## Sponsorship\nSupport development of Box2D through [Github Sponsors](https://github.com/sponsors/erincatto).\n\nPlease consider starring this repository and subscribing to my [YouTube channel](https://www.youtube.com/@erin_catto).\n\n## External ports, wrappers, and bindings (unsupported)\n- Beef bindings - https://github.com/EnokViking/Box2DBeef\n- C++ bindings - https://github.com/HolyBlackCat/box2cpp\n- WASM - https://github.com/Birch-san/box2d3-wasm\n"
  },
  {
    "path": "benchmark/CMakeLists.txt",
    "content": "# Box2D benchmark app\n\nset(BOX2D_BENCHMARK_FILES\n\tmain.c\n)\nadd_executable(benchmark ${BOX2D_BENCHMARK_FILES})\n\nset_target_properties(benchmark PROPERTIES\n    C_STANDARD 17\n    C_STANDARD_REQUIRED YES\n    C_EXTENSIONS NO\n)\n\nif (BOX2D_COMPILE_WARNING_AS_ERROR)\n\tset_target_properties(benchmark PROPERTIES COMPILE_WARNING_AS_ERROR ON)\nendif()\n\ntarget_link_libraries(benchmark PRIVATE box2d shared enkiTS)\n\nsource_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX \"\" FILES ${BOX2D_BENCHMARK_FILES})"
  },
  {
    "path": "benchmark/amd7950x_avx2/joint_grid.csv",
    "content": "threads,ms\n1,3174.52\n2,1795.2\n3,1217.45\n4,996.27\n5,857.203\n6,763.975\n7,696.29\n8,648.49\n"
  },
  {
    "path": "benchmark/amd7950x_avx2/large_pyramid.csv",
    "content": "threads,ms\n1,1951.88\n2,1026.26\n3,721.553\n4,563.934\n5,473.542\n6,415.048\n7,367.646\n8,362.217\n"
  },
  {
    "path": "benchmark/amd7950x_avx2/many_pyramids.csv",
    "content": "threads,ms\n1,3312.37\n2,1674.87\n3,1116.48\n4,859.268\n5,691.412\n6,594.236\n7,513.412\n8,462.289\n"
  },
  {
    "path": "benchmark/amd7950x_avx2/rain.csv",
    "content": "threads,ms\n1,8415.32\n2,4830.51\n3,3684.98\n4,3008.05\n5,2568.19\n6,2275.1\n7,2054.01\n8,1896.27\n"
  },
  {
    "path": "benchmark/amd7950x_avx2/smash.csv",
    "content": "threads,ms\n1,1946.15\n2,1212.81\n3,937.648\n4,786.533\n5,699.338\n6,636.796\n7,597.153\n8,564.691\n"
  },
  {
    "path": "benchmark/amd7950x_avx2/spinner.csv",
    "content": "threads,ms\n1,5145.33\n2,3090.7\n3,2362.34\n4,1890.21\n5,1643.82\n6,1473.02\n7,1339.32\n8,1256.21\n"
  },
  {
    "path": "benchmark/amd7950x_avx2/tumbler.csv",
    "content": "threads,ms\n1,2035.14\n2,1273.93\n3,961.824\n4,790.221\n5,679.686\n6,609.556\n7,561.948\n8,531.353\n"
  },
  {
    "path": "benchmark/amd7950x_float/joint_grid.csv",
    "content": "threads,ms\n1,3070.3\n2,1743.41\n3,1190.54\n4,973.725\n5,839.892\n6,749.347\n7,684.411\n8,639.372\n"
  },
  {
    "path": "benchmark/amd7950x_float/large_pyramid.csv",
    "content": "threads,ms\n1,4324.94\n2,2218.25\n3,1489.84\n4,1132.51\n5,935.469\n6,818.171\n7,722.364\n8,658.744\n"
  },
  {
    "path": "benchmark/amd7950x_float/many_pyramids.csv",
    "content": "threads,ms\n1,6803.92\n2,3421.51\n3,2310.75\n4,1766.54\n5,1424.53\n6,1213.28\n7,1043.65\n8,915.268\n"
  },
  {
    "path": "benchmark/amd7950x_float/rain.csv",
    "content": "threads,ms\n1,10023.3\n2,5685.88\n3,4189.81\n4,3390.77\n5,2873.75\n6,2522.69\n7,2275.75\n8,2098.4\n"
  },
  {
    "path": "benchmark/amd7950x_float/smash.csv",
    "content": "threads,ms\n1,2660.24\n2,1604.04\n3,1208.81\n4,997.755\n5,871.282\n6,791.138\n7,731.148\n8,687.379\n"
  },
  {
    "path": "benchmark/amd7950x_float/spinner.csv",
    "content": "threads,ms\n1,8296.22\n2,4821.7\n3,3555.21\n4,2816.7\n5,2427.56\n6,2162.31\n7,2007.07\n8,1896.91\n"
  },
  {
    "path": "benchmark/amd7950x_float/tumbler.csv",
    "content": "threads,ms\n1,3327.12\n2,1933.37\n3,1436.84\n4,1169.7\n5,1014.31\n6,907.688\n7,840.671\n8,786.672\n"
  },
  {
    "path": "benchmark/amd7950x_sse2/joint_grid.csv",
    "content": "threads,ms\n1,2901.74\n2,1679.24\n3,1138.81\n4,930.556\n5,802.79\n6,719.061\n7,653.555\n8,609.054\n"
  },
  {
    "path": "benchmark/amd7950x_sse2/large_pyramid.csv",
    "content": "threads,ms\n1,2234.69\n2,1198.39\n3,821.019\n4,636.813\n5,533.46\n6,476.708\n7,422.339\n8,384.637\n"
  },
  {
    "path": "benchmark/amd7950x_sse2/many_pyramids.csv",
    "content": "threads,ms\n1,3718.08\n2,1861.18\n3,1279.77\n4,968.623\n5,781.502\n6,667.149\n7,572.954\n8,511.755\n"
  },
  {
    "path": "benchmark/amd7950x_sse2/rain.csv",
    "content": "threads,ms\n1,10450.7\n2,6050.77\n3,4647.11\n4,3631.8\n5,3096.74\n6,2735.92\n7,2460.2\n8,2256.3\n"
  },
  {
    "path": "benchmark/amd7950x_sse2/smash.csv",
    "content": "threads,ms\n1,1998.87\n2,1206.23\n3,894.774\n4,732.413\n5,636.455\n6,571.134\n7,527.128\n8,493.411\n"
  },
  {
    "path": "benchmark/amd7950x_sse2/spinner.csv",
    "content": "threads,ms\n1,5416.19\n2,3229.11\n3,2377.87\n4,1903.65\n5,1630.79\n6,1448.68\n7,1324.58\n8,1237.05\n"
  },
  {
    "path": "benchmark/amd7950x_sse2/tumbler.csv",
    "content": "threads,ms\n1,2338.19\n2,1413.12\n3,1081.55\n4,883.9\n5,769.756\n6,696.251\n7,647.607\n8,612.832\n"
  },
  {
    "path": "benchmark/amd7950x_sse2/washer.csv",
    "content": "threads,ms\n1,7934.7\n2,4531.48\n3,3218.58\n4,2523.46\n5,2100.84\n6,1842.67\n7,1638.05\n8,1529.17\n"
  },
  {
    "path": "benchmark/m2air_float/joint_grid.csv",
    "content": "threads,ms\n1,2367.43\n2,1449.57\n3,999.678\n4,838.636\n"
  },
  {
    "path": "benchmark/m2air_float/large_pyramid.csv",
    "content": "threads,ms\n1,2317.53\n2,1252.4\n3,891.763\n4,694.968\n"
  },
  {
    "path": "benchmark/m2air_float/many_pyramids.csv",
    "content": "threads,ms\n1,3559.49\n2,1888.42\n3,1357.7\n4,1085.2\n"
  },
  {
    "path": "benchmark/m2air_float/rain.csv",
    "content": "threads,ms\n1,7119.67\n2,4144.16\n3,3192.93\n4,2623.99\n"
  },
  {
    "path": "benchmark/m2air_float/smash.csv",
    "content": "threads,ms\n1,1757.78\n2,1079.8\n3,849.502\n4,709.022\n"
  },
  {
    "path": "benchmark/m2air_float/spinner.csv",
    "content": "threads,ms\n1,5434.23\n2,3244.67\n3,2462.03\n4,1998.78\n"
  },
  {
    "path": "benchmark/m2air_float/tumbler.csv",
    "content": "threads,ms\n1,2094.15\n2,1270.1\n3,1020.63\n4,835.34\n"
  },
  {
    "path": "benchmark/m2air_neon/joint_grid.csv",
    "content": "threads,ms\n1,2377.18\n2,1444.78\n3,998.22\n4,837.361\n"
  },
  {
    "path": "benchmark/m2air_neon/large_pyramid.csv",
    "content": "threads,ms\n1,1600.72\n2,880.846\n3,632.579\n4,502.146\n"
  },
  {
    "path": "benchmark/m2air_neon/many_pyramids.csv",
    "content": "threads,ms\n1,2455.91\n2,1329.33\n3,984.499\n4,820.902\n"
  },
  {
    "path": "benchmark/m2air_neon/rain.csv",
    "content": "threads,ms\n1,6834.98\n2,4020.86\n3,3123.64\n4,2574.69\n"
  },
  {
    "path": "benchmark/m2air_neon/smash.csv",
    "content": "threads,ms\n1,1595.13\n2,1005.41\n3,798.89\n4,670.542\n"
  },
  {
    "path": "benchmark/m2air_neon/spinner.csv",
    "content": "threads,ms\n1,4715.34\n2,2939.07\n3,2264.93\n4,1855.98\n"
  },
  {
    "path": "benchmark/m2air_neon/tumbler.csv",
    "content": "threads,ms\n1,1804.16\n2,1150.51\n3,945.004\n4,777.227\n"
  },
  {
    "path": "benchmark/main.c",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"TaskScheduler_c.h\"\n#include \"benchmarks.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <assert.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#if defined( _WIN64 )\n#include <Windows.h>\n#elif defined( __APPLE__ )\n#include <unistd.h>\n#elif defined( __linux__ )\n#include <unistd.h>\n#endif\n\n#define ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) )\n#define MAYBE_UNUSED( x ) ( (void)( x ) )\n\ntypedef void CreateFcn( b2WorldId worldId );\ntypedef float StepFcn( b2WorldId worldId, int stepCount );\n\ntypedef struct Benchmark\n{\n\tconst char* name;\n\tCreateFcn* createFcn;\n\tStepFcn* stepFcn;\n\tint totalStepCount;\n} Benchmark;\n\n#define MAX_TASKS 128\n#define THREAD_LIMIT 32\n\ntypedef struct TaskData\n{\n\tb2TaskCallback* box2dTask;\n\tvoid* box2dContext;\n} TaskData;\n\nenkiTaskScheduler* scheduler;\nenkiTaskSet* tasks[MAX_TASKS];\nTaskData taskData[MAX_TASKS];\nint taskCount;\n\nint GetNumberOfCores()\n{\n#if defined( _WIN64 )\n\tSYSTEM_INFO sysinfo;\n\tGetSystemInfo( &sysinfo );\n\treturn sysinfo.dwNumberOfProcessors;\n#elif defined( __APPLE__ )\n\treturn (int)sysconf( _SC_NPROCESSORS_ONLN );\n#elif defined( __linux__ )\n\treturn (int)sysconf( _SC_NPROCESSORS_ONLN );\n#elif defined( __EMSCRIPTEN__ )\n\treturn (int)sysconf( _SC_NPROCESSORS_ONLN );\n#else\n\treturn 1;\n#endif\n}\n\nvoid ExecuteRangeTask( uint32_t start, uint32_t end, uint32_t threadIndex, void* context )\n{\n\tTaskData* data = context;\n\tdata->box2dTask( start, end, threadIndex, data->box2dContext );\n}\n\nstatic void* EnqueueTask( b2TaskCallback* box2dTask, int itemCount, int minRange, void* box2dContext, void* userContext )\n{\n\tMAYBE_UNUSED( userContext );\n\n\tif ( taskCount < MAX_TASKS )\n\t{\n\t\tenkiTaskSet* task = tasks[taskCount];\n\t\tTaskData* data = taskData + taskCount;\n\t\tdata->box2dTask = box2dTask;\n\t\tdata->box2dContext = box2dContext;\n\n\t\tstruct enkiParamsTaskSet params;\n\t\tparams.minRange = minRange;\n\t\tparams.setSize = itemCount;\n\t\tparams.pArgs = data;\n\t\tparams.priority = 0;\n\n\t\tenkiSetParamsTaskSet( task, params );\n\t\tenkiAddTaskSet( scheduler, task );\n\n\t\t++taskCount;\n\n\t\treturn task;\n\t}\n\telse\n\t{\n\t\tprintf( \"MAX_TASKS exceeded!!!\\n\" );\n\t\tbox2dTask( 0, itemCount, 0, box2dContext );\n\t\treturn NULL;\n\t}\n}\n\nstatic void FinishTask( void* userTask, void* userContext )\n{\n\tMAYBE_UNUSED( userContext );\n\n\tenkiTaskSet* task = userTask;\n\tenkiWaitForTaskSet( scheduler, task );\n}\n\nstatic void MinProfile( b2Profile* p1, const b2Profile* p2 )\n{\n\tp1->step = b2MinFloat( p1->step, p2->step );\n\tp1->pairs = b2MinFloat( p1->pairs, p2->pairs );\n\tp1->collide = b2MinFloat( p1->collide, p2->collide );\n\tp1->solveConstraints = b2MinFloat( p1->solveConstraints, p2->solveConstraints );\n\tp1->transforms = b2MinFloat( p1->transforms, p2->transforms );\n\tp1->refit = b2MinFloat( p1->refit, p2->refit );\n\tp1->sleepIslands = b2MinFloat( p1->sleepIslands, p2->sleepIslands );\n}\n\n// Box2D benchmark application. On Windows it is important to use affinity avoid cross CCD\n// usage or efficiency cores. Also on Windows create a power plan with Processor power management\n// Min/Max of 99%. This prevents boosting and makes the benchmarks more repeatable.\n// Affinity [0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80]\n\n// Run all benchmarks with 1 to 8 threads.\n// start /affinity 0x5555 .\\build\\bin\\Release\\benchmark.exe -t=8\n\n// Run all benchmarks with 4 workers only.\n// start /affinity 0x5555 .\\build\\bin\\Release\\benchmark.exe -t=4 -w=4\n\n// Run benchmark 3 with 4 workers and repeat 20 times. Record the step times.\n// start /affinity 0x5555 .\\build\\bin\\Release\\benchmark.exe -t=4 -w=4 -b=3 -r=20 -s\n// start /affinity 0x5555 .\\build\\bin\\Release\\benchmark.exe -t=8 -b=7\n\n// Run benchmark 3 with 4 workers and run once. Disable continuous collision. Record the step times.\n// start /affinity 0x5555 .\\build\\bin\\Release\\benchmark.exe -t=4 -w=4 -b=3 -r=1 -nc -s\n\nint main( int argc, char** argv )\n{\n\tBenchmark benchmarks[] = {\n\t\t{ \"joint_grid\", CreateJointGrid, NULL, 500 },\n\t\t{ \"large_pyramid\", CreateLargePyramid, NULL, 500 },\n\t\t{ \"many_pyramids\", CreateManyPyramids, NULL, 200 },\n\t\t{ \"rain\", CreateRain, StepRain, 1000 },\n\t\t{ \"smash\", CreateSmash, NULL, 300 },\n\t\t{ \"spinner\", CreateSpinner, StepSpinner, 500 },\n\t\t{ \"tumbler\", CreateTumbler, NULL, 750 },\n\t\t{ \"washer\", CreateWasher, NULL, 500 },\n\t};\n\n\tint benchmarkCount = ARRAY_COUNT( benchmarks );\n\n\tint maxSteps = benchmarks[0].totalStepCount;\n\tfor ( int i = 1; i < benchmarkCount; ++i )\n\t{\n\t\tmaxSteps = b2MaxInt( maxSteps, benchmarks[i].totalStepCount );\n\t}\n\n\tb2Profile maxProfile = {\n\t\t.step = FLT_MAX,\n\t\t.pairs = FLT_MAX,\n\t\t.collide = FLT_MAX,\n\t\t.solve = FLT_MAX,\n\t\t.prepareStages = FLT_MAX,\n\t\t.solveConstraints = FLT_MAX,\n\t\t.prepareConstraints = FLT_MAX,\n\t\t.integrateVelocities = FLT_MAX,\n\t\t.warmStart = FLT_MAX,\n\t\t.solveImpulses = FLT_MAX,\n\t\t.integratePositions = FLT_MAX,\n\t\t.relaxImpulses = FLT_MAX,\n\t\t.applyRestitution = FLT_MAX,\n\t\t.storeImpulses = FLT_MAX,\n\t\t.splitIslands = FLT_MAX,\n\t\t.transforms = FLT_MAX,\n\t\t.hitEvents = FLT_MAX,\n\t\t.refit = FLT_MAX,\n\t\t.bullets = FLT_MAX,\n\t\t.sleepIslands = FLT_MAX,\n\t};\n\n\tb2Profile* profiles = malloc( maxSteps * sizeof( b2Profile ) );\n\tfor ( int i = 0; i < maxSteps; ++i )\n\t{\n\t\tprofiles[i] = maxProfile;\n\t}\n\n\tfloat* stepResults = malloc( maxSteps * sizeof( float ) );\n\tmemset( stepResults, 0, maxSteps * sizeof( float ) );\n\n\tint maxThreadCount = GetNumberOfCores();\n\tint runCount = 4;\n\tint singleBenchmark = -1;\n\tint singleWorkerCount = -1;\n\tb2Counters counters = { 0 };\n\tbool enableContinuous = true;\n\tbool recordStepTimes = false;\n\n\tassert( maxThreadCount <= THREAD_LIMIT );\n\n\tfor ( int i = 1; i < argc; ++i )\n\t{\n\t\tconst char* arg = argv[i];\n\t\tif ( strncmp( arg, \"-t=\", 3 ) == 0 )\n\t\t{\n\t\t\tint threadCount = atoi( arg + 3 );\n\t\t\tmaxThreadCount = b2ClampInt( threadCount, 1, maxThreadCount );\n\t\t}\n\t\telse if ( strncmp( arg, \"-b=\", 3 ) == 0 )\n\t\t{\n\t\t\tsingleBenchmark = atoi( arg + 3 );\n\t\t\tsingleBenchmark = b2ClampInt( singleBenchmark, 0, benchmarkCount - 1 );\n\t\t}\n\t\telse if ( strncmp( arg, \"-w=\", 3 ) == 0 )\n\t\t{\n\t\t\tsingleWorkerCount = atoi( arg + 3 );\n\t\t}\n\t\telse if ( strncmp( arg, \"-r=\", 3 ) == 0 )\n\t\t{\n\t\t\trunCount = b2ClampInt( atoi( arg + 3 ), 1, 1000 );\n\t\t}\n\t\telse if ( strncmp( arg, \"-nc\", 3 ) == 0 )\n\t\t{\n\t\t\tenableContinuous = false;\n\t\t\tprintf( \"Continuous disabled\\n\" );\n\t\t}\n\t\telse if ( strncmp( arg, \"-s\", 3 ) == 0 )\n\t\t{\n\t\t\trecordStepTimes = true;\n\t\t}\n\t\telse if ( strcmp( arg, \"-h\" ) == 0 )\n\t\t{\n\t\t\tprintf( \"Usage\\n\"\n\t\t\t\t\t\"-t=<integer>: the maximum number of threads to use\\n\"\n\t\t\t\t\t\"-b=<integer>: run a single benchmark\\n\"\n\t\t\t\t\t\"-w=<integer>: run a single worker count\\n\"\n\t\t\t\t\t\"-r=<integer>: number of repeats (default is 4)\\n\"\n\t\t\t\t\t\"-s: record step times\\n\" );\n\t\t\texit( 0 );\n\t\t}\n\t}\n\n\tif ( singleWorkerCount != -1 )\n\t{\n\t\tsingleWorkerCount = b2ClampInt( singleWorkerCount, 1, maxThreadCount );\n\t}\n\n\tprintf( \"Starting Box2D benchmarks\\n\" );\n\tprintf( \"======================================\\n\" );\n\n\tfor ( int benchmarkIndex = 0; benchmarkIndex < benchmarkCount; ++benchmarkIndex )\n\t{\n\t\tif ( singleBenchmark != -1 && benchmarkIndex != singleBenchmark )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n#ifdef NDEBUG\n\t\tint stepCount = benchmarks[benchmarkIndex].totalStepCount;\n#else\n\t\tint stepCount = 10;\n#endif\n\n\t\tBenchmark* benchmark = benchmarks + benchmarkIndex;\n\n\t\tbool countersAcquired = false;\n\n\t\tprintf( \"benchmark: %s, steps = %d\\n\", benchmarks[benchmarkIndex].name, stepCount );\n\n\t\tfloat minTime[THREAD_LIMIT] = { 0 };\n\n\t\tfor ( int threadCount = 1; threadCount <= maxThreadCount; ++threadCount )\n\t\t{\n\t\t\tif ( singleWorkerCount != -1 && singleWorkerCount != threadCount )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tprintf( \"thread count: %d\\n\", threadCount );\n\n\t\t\tfor ( int runIndex = 0; runIndex < runCount; ++runIndex )\n\t\t\t{\n\t\t\t\tscheduler = enkiNewTaskScheduler();\n\t\t\t\tstruct enkiTaskSchedulerConfig config = enkiGetTaskSchedulerConfig( scheduler );\n\t\t\t\tconfig.numTaskThreadsToCreate = threadCount - 1;\n\t\t\t\tenkiInitTaskSchedulerWithConfig( scheduler, config );\n\n\t\t\t\tfor ( int taskIndex = 0; taskIndex < MAX_TASKS; ++taskIndex )\n\t\t\t\t{\n\t\t\t\t\ttasks[taskIndex] = enkiCreateTaskSet( scheduler, ExecuteRangeTask );\n\t\t\t\t}\n\n\t\t\t\tb2WorldDef worldDef = b2DefaultWorldDef();\n\t\t\t\tworldDef.enableContinuous = enableContinuous;\n\t\t\t\tworldDef.enqueueTask = EnqueueTask;\n\t\t\t\tworldDef.finishTask = FinishTask;\n\t\t\t\tworldDef.workerCount = threadCount;\n\t\t\t\tb2WorldId worldId = b2CreateWorld( &worldDef );\n\n\t\t\t\tbenchmark->createFcn( worldId );\n\n\t\t\t\tfloat timeStep = 1.0f / 60.0f;\n\t\t\t\tint subStepCount = 4;\n\n\t\t\t\t// Initial step can be expensive and skew benchmark\n\t\t\t\tif ( benchmark->stepFcn != NULL )\n\t\t\t\t{\n\t\t\t\t\tstepResults[0] = benchmark->stepFcn( worldId, 0 );\n\t\t\t\t}\n\n\t\t\t\tassert( stepCount <= maxSteps );\n\n\t\t\t\tb2World_Step( worldId, timeStep, subStepCount );\n\n\t\t\t\tb2Profile profile = b2World_GetProfile( worldId );\n\t\t\t\tMinProfile( profiles + 0, &profile );\n\n\t\t\t\ttaskCount = 0;\n\n\t\t\t\tuint64_t ticks = b2GetTicks();\n\n\t\t\t\tfor ( int stepIndex = 1; stepIndex < stepCount; ++stepIndex )\n\t\t\t\t{\n\t\t\t\t\tif ( benchmark->stepFcn != NULL )\n\t\t\t\t\t{\n\t\t\t\t\t\tstepResults[stepIndex] = benchmark->stepFcn( worldId, stepIndex );\n\t\t\t\t\t}\n\n\t\t\t\t\tb2World_Step( worldId, timeStep, subStepCount );\n\t\t\t\t\ttaskCount = 0;\n\n\t\t\t\t\tprofile = b2World_GetProfile( worldId );\n\t\t\t\t\tMinProfile( profiles + stepIndex, &profile );\n\t\t\t\t}\n\n\t\t\t\tfloat ms = b2GetMilliseconds( ticks );\n\t\t\t\tprintf( \"run %d : %g (ms)\\n\", runIndex, ms );\n\n\t\t\t\tif (runIndex == 0)\n\t\t\t\t{\n\t\t\t\t\tminTime[threadCount - 1] = ms ;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tminTime[threadCount - 1] = b2MinFloat( minTime[threadCount - 1], ms );\n\t\t\t\t}\n\n\t\t\t\tif ( countersAcquired == false )\n\t\t\t\t{\n\t\t\t\t\tcounters = b2World_GetCounters( worldId );\n\t\t\t\t\tcountersAcquired = true;\n\t\t\t\t}\n\n\t\t\t\tb2DestroyWorld( worldId );\n\n\t\t\t\tfor ( int taskIndex = 0; taskIndex < MAX_TASKS; ++taskIndex )\n\t\t\t\t{\n\t\t\t\t\tenkiDeleteTaskSet( scheduler, tasks[taskIndex] );\n\t\t\t\t\ttasks[taskIndex] = NULL;\n\t\t\t\t\ttaskData[taskIndex] = ( TaskData ){ 0 };\n\t\t\t\t}\n\n\t\t\t\tenkiDeleteTaskScheduler( scheduler );\n\t\t\t\tscheduler = NULL;\n\n\t\t\t}\n\n\t\t\tif ( recordStepTimes )\n\t\t\t{\n\t\t\t\tchar fileName[64] = { 0 };\n\t\t\t\tsnprintf( fileName, 64, \"%s_t%d.dat\", benchmarks[benchmarkIndex].name, threadCount );\n\t\t\t\tFILE* file = fopen( fileName, \"w\" );\n\t\t\t\tif ( file == NULL )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor ( int stepIndex = 0; stepIndex < stepCount; ++stepIndex )\n\t\t\t\t{\n\t\t\t\t\tb2Profile p = profiles[stepIndex];\n\t\t\t\t\tfprintf( file, \"%g %g %g %g %g %g %g\\n\", p.step, p.pairs, p.collide, p.solveConstraints, p.transforms, p.refit, p.sleepIslands );\n\t\t\t\t}\n\n\t\t\t\tfclose( file );\n\t\t\t}\n\t\t}\n\n\t\tprintf( \"body %d / shape %d / contact %d / joint %d / stack %d\\n\\n\", counters.bodyCount, counters.shapeCount,\n\t\t\t\tcounters.contactCount, counters.jointCount, counters.stackUsed );\n\n\t\tchar fileName[64] = { 0 };\n\t\tsnprintf( fileName, 64, \"%s.csv\", benchmarks[benchmarkIndex].name );\n\t\tFILE* file = fopen( fileName, \"w\" );\n\t\tif ( file == NULL )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tfprintf( file, \"threads,ms\\n\" );\n\t\tfor ( int threadIndex = 1; threadIndex <= maxThreadCount; ++threadIndex )\n\t\t{\n\t\t\tfprintf( file, \"%d,%g\\n\", threadIndex, minTime[threadIndex - 1] );\n\t\t}\n\n\t\tfclose( file );\n\t}\n\n\tprintf( \"======================================\\n\" );\n\tprintf( \"All Box2D benchmarks complete!\\n\" );\n\n\tfree( profiles );\n\tfree( stepResults );\n\n\treturn 0;\n}\n"
  },
  {
    "path": "benchmark/n100_sse2/joint_grid.csv",
    "content": "threads,fps\n1,75.5947\n2,123.228\n3,160.379\n4,181.545\n"
  },
  {
    "path": "benchmark/n100_sse2/large_pyramid.csv",
    "content": "threads,fps\n1,127.236\n2,226.291\n3,297.628\n4,345.526\n"
  },
  {
    "path": "benchmark/n100_sse2/many_pyramids.csv",
    "content": "threads,fps\n1,30.8828\n2,55.0462\n3,69.5406\n4,77.7339\n"
  },
  {
    "path": "benchmark/n100_sse2/rain.csv",
    "content": "threads,fps\n1,72.2901\n2,118.753\n3,142.61\n4,162.35\n"
  },
  {
    "path": "benchmark/n100_sse2/smash.csv",
    "content": "threads,fps\n1,86.2381\n2,132.306\n3,160.725\n4,181.842\n"
  },
  {
    "path": "benchmark/n100_sse2/spinner.csv",
    "content": "threads,fps\n1,156.855\n2,258.638\n3,303.717\n4,358.492\n"
  },
  {
    "path": "benchmark/n100_sse2/tumbler.csv",
    "content": "threads,fps\n1,199.492\n2,313.012\n3,381.983\n4,441.825\n"
  },
  {
    "path": "build.sh",
    "content": "#!/usr/bin/env bash\n\n# Use this to build box2d on any system with a bash shell\nrm -rf build\nmkdir build\ncd build\n\n# I haven't been able to get Wayland working on WSL but X11 works.\n# https://www.glfw.org/docs/latest/compile.html\ncmake -DBOX2D_BUILD_DOCS=OFF -DGLFW_BUILD_WAYLAND=OFF ..\ncmake --build .\n"
  },
  {
    "path": "build_emscripten.sh",
    "content": "#!/usr/bin/env bash\n\n# source emsdk_env.sh\n\n# Use this to build box2d on any system with a bash shell\nrm -rf build\nmkdir build\ncd build\nemcmake cmake -DBOX2D_VALIDATE=OFF -DBOX2D_UNIT_TESTS=ON -DBOX2D_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug ..\ncmake --build .\n"
  },
  {
    "path": "create_sln.bat",
    "content": "rem Use this batch file to build box2d for Visual Studio\nrmdir /s /q build\nmkdir build\ncd build\ncmake ..\n"
  },
  {
    "path": "deploy_docs.sh",
    "content": "#!/usr/bin/env bash\n\n# Copies documentation to blog\ncp -R build/docs/html/. ../box2d_blog/public/documentation/\n"
  },
  {
    "path": "docs/CMakeLists.txt",
    "content": "find_package(Doxygen REQUIRED dot)\n\nset(DOXYGEN_PROJECT_NAME \"Box2D\")\nset(DOXYGEN_GENERATE_HTML YES)\nset(DOXYGEN_USE_MATHJAX YES)\nset(DOXYGEN_MATHJAX_VERSION MathJax_3)\nset(DOXYGEN_MATHJAX_FORMAT SVG)\nset(DOXYGEN_EXTRACT_ALL NO)\nset(DOXYGEN_FILE_PATTERNS *.h)\nset(DOXYGEN_ENABLE_PREPROCESSING YES)\nset(DOXYGEN_MACRO_EXPANSION YES)\nset(DOXYGEN_EXPAND_ONLY_PREDEF YES)\nset(DOXYGEN_PREDEFINED B2_API= B2_INLINE=)\nset(DOXYGEN_WARN_IF_UNDOCUMENTED YES)\n\n# In multiline comments, this takes the first line/sentence as a brief description to use in the table of functions.\n# So I don't need to use @brief tags to separate the short description from the full description.\nset(DOXYGEN_JAVADOC_AUTOBRIEF YES)\n\nset(DOXYGEN_IMAGE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/images\")\nset(DOXYGEN_HTML_EXTRA_STYLESHEET \"${CMAKE_CURRENT_SOURCE_DIR}/extra.css\")\nset(DOXYGEN_USE_MDFILE_AS_MAINPAGE \"${CMAKE_CURRENT_SOURCE_DIR}/overview.md\")\nset(DOXYGEN_PROJECT_LOGO \"${CMAKE_CURRENT_SOURCE_DIR}/images/logo.svg\")\nset(DOXYGEN_LAYOUT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/layout.xml\")\n\nset(DOXYGEN_INLINE_SIMPLE_STRUCTS YES)\nset(DOXYGEN_TYPEDEF_HIDES_STRUCT YES)\n# set(DOXYGEN_DISABLE_INDEX YES)\nset(DOXYGEN_GENERATE_TREEVIEW YES)\nset(DOXYGEN_FULL_SIDEBAR NO)\n\n# force dark mode to work with extra.css\nset(DOXYGEN_HTML_COLORSTYLE DARK)\n\n# this tells doxygen to label structs as structs instead of classes\nset(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES)\nset(DOXYGEN_WARN_IF_INCOMPLETE_DOC NO)\n\ndoxygen_add_docs(doc\n                \"${CMAKE_SOURCE_DIR}/include/box2d\"\n                \"overview.md\"\n                \"hello.md\"\n                \"samples.md\"\n                \"foundation.md\"\n                \"collision.md\"\n                \"simulation.md\"\n                \"loose_ends.md\"\n                \"character.md\"\n                \"reading.md\"\n                \"faq.md\"\n                \"migration.md\"\n                \"release_notes_v310.md\"\n                ALL\n                COMMENT \"Generate HTML documentation\")\n"
  },
  {
    "path": "docs/FAQ.md",
    "content": "# FAQ\n\n## What is Box2D?\nBox2D is a feature rich 2D rigid body physics engine, written in C17 by Erin Catto. It has been used in many games and in many\ngame engines.\n\nBox2D uses the [MIT license](https://en.wikipedia.org/wiki/MIT_License) license and can be used free of charge. Credit\nshould be included if possible. Support is [appreciated](https://github.com/sponsors/erincatto). You may use the\nBox2D [logo](https://box2d.org/images/logo.svg).\n\n## What platforms does Box2D support?\nBox2D is developed using C. Ports and bindings are likely available for most languages and platforms.\n\nErin Catto maintains the C version, but provides no support for other languages. Other languages are supported\nby the community and possibly by the authors of those ports.\n\n## Who makes it?\nErin Catto is the creator and sole contributor of the C version of Box2D, with various others supporting the ports. Box2D is an open source project, and accepts community feedback.\n\n## How do I get help?\nYou should read the documentation and the rest of this FAQ first. Also, you should study the examples included in the source distribution. Then you can visit the [Discord](https://discord.gg/aM4mRKxW) to ask any remaining questions.\n\nPlease to not message or email Erin Catto directly for support. It is best to ask questions on the Discord server so that everyone can benefit from the discussion.\n\n## Documentation\n\n### Why isn't a feature documented?\nIf you grab the latest code from the git main branch you will likely find features that are not documented in the manual. New features are added to the manual after they are mature and a new point release is imminent. However, all major features added to Box2D are accompanied by example code in the samples application to test the feature and show the intended usage.\n\n## Prerequisites\n\n### Programming\nYou should have a working knowledge of C before you use Box2D. You should understand functions, structures, and pointers. There are plenty of resources on the web for learning C. You should also understand your development environment: compilation, linking, and debugging.\n\n### Math and Physics\nYou should have a basic knowledge of rigid bodies, force, torque, and impulses. If you come across a math or physics concept you don't understand, please read about it on Wikipedia. Visit this [page](http://box2d.org/publications/) if you want a deeper knowledge of the algorithms used in Box2D.\n\n## API\n\n### What units does Box2D use?\nBox2D is tuned for meters-kilograms-seconds (MKS). This is recommend as the units for your game. However, you may use\ndifferent units if you are careful.\n\n### How do I convert pixels to meters?\nSuppose you have a sprite for a character that is 100x100 pixels. You decide to use a scaling factor that is 0.01. This will make the character physics box 1m x 1m. So go make a physics box that is 1x1. Now suppose the character starts out at pixel coordinate (345,679). So position the physics box at (3.45,6.79). Now simulate the physics world. Suppose the character physics box moves to (2.31,4.98), so move your character sprite to pixel coordinates (231,498).\n\nNow the only tricky part is choosing a scaling factor. This really depends on your game. You should try to get your moving objects in the range 0.1 - 10 meters, with 1 meter being the sweet spot.\n\nThis [repo](https://github.com/erincatto/box2d-raylib) shows how to convert meters to pixels.\n\n### Why don't you use this awesome language?\nBox2D is designed to be portable and easy to wrap with other languages, so I decided to use C17. I used C17 to get support for atomics.\n\n### Can I use Box2D in a DLL?\nYes. See the CMake option `BUILD_SHARED_LIBS`.\n\n### Is Box2D thread-safe?\nNo. Box2D will likely never be thread-safe. Box2D has a large API and trying to make such an API thread-safe would have a large performance and complexity impact. However, you can call read only functions from multiple threads. For example, all the\n[spatial query](#spatial) functions are read only.\n\n## Build Issues\n\n### Why doesn't my code compile and/or link?\nThere are many reasons why a build can go bad. Here are a few that have come up:\n* Using old Box2D headers with new code\n* Not linking the Box2D library with your application\n* Using old project files that don't include some new source files\n\n## Rendering\n\n### What are Box2D's rendering capabilities?\nBox2D is only a physics engine. How you draw stuff is up to you.\n\n### But the samples application draws stuff\nVisualization is very important for debugging collision and physics. I wrote the samples application to help me test Box2D and give you examples of how to use Box2D. The samples are not part of the Box2D library.\n\n### How do I draw shapes?\nImplement the `b2DebugDraw` interface and call `b2World_Draw()`.\n\n## Accuracy\nBox2D uses approximate methods for a few reasons.\n* Performance\n* Some differential equations don't have known solutions\n* Some constraints cannot be determined uniquely\n\nWhat this means is that constraints are not perfectly rigid and sometimes you will see some bounce even when the restitution is zero.\nBox2D uses [Gauss-Seidel](https://en.wikipedia.org/wiki/Gauss%E2%80%93Seidel_method) to approximately solve constraints.\nBox2D also uses [Semi-implicit Euler](https://en.wikipedia.org/wiki/Semi-implicit_Euler_method) to approximately solve the differential equations.\nBox2D also does not have exact collision. There is no continuous collision between dynamic shapes. Slow moving shapes may have small overlap for a few time steps. In extreme stacking scenarios, shapes may have sustained overlap.\n\n## Making Games\n\n### Worms Clones\nMaking a worms clone requires arbitrarily destructible terrain. This is beyond the scope of Box2D, so you will have to figure out how to do this on your own.\n\n### Tile Based Environment\nUsing many boxes for your terrain may not work well because box-like characters can get snagged on internal corners. Box2D provides chain shapes for smooth collision, see `b2ChainDef`. In general you should avoid using a rectangular character because collision tolerances will still lead to undesirable snagging. Box2D provides capsules and rounded polygons that may work better for characters.\n\n### Asteroid Type Coordinate Systems\nBox2D does not have any support for coordinate frame wrapping. You would likely need to customize Box2D for this purpose. You may need to use a different broad-phase for this to work.\n\n## Determinism\n\n### Is Box2D deterministic?\nFor the same input, and same binary, Box2D will reproduce any simulation. Box2D does not use any random numbers nor base any computation on random events (such as timers, etc).\n\nBox2D is also deterministic under multithreading. A simulation using two threads will give the same result as eight threads.\n\nBox2D has cross-platform determinism as of version 3.1.\n\nHowever, Box2D does not have rollback determinism.\n\n### But I really want determinism\nThis naturally leads to the question of fixed-point math. Box2D does not support fixed-point math. In the past Box2D was ported to the NDS in fixed-point and apparently it worked okay. Fixed-point math is slower and more tedious to develop, so I have chosen not to use fixed-point for the development of Box2D.\n\n## What are the common mistakes made by new users?\n* Using pixels for length instead of meters\n* Expecting Box2D to give pixel perfect results\n* Testing their code in release mode\n* Not learning C before using Box2D\n"
  },
  {
    "path": "docs/character.md",
    "content": "# Character mover\n\n> **Caution**:\n> The character mover feature is new to version 3.1 and should be considered experimental.\n\nBox2D provides a few structures and functions you can use to build a character mover.\n\nThese features support a `geometric` character mover. This is like\na `kinematic` mover except the character mover is not a rigid body and does\nnot exist in the simulation world. This is done to achieve features that\nwould be difficult with a kinematic body.\n\nThis type of mover may not be suitable for your game. It is less physical than a rigid body, but\nit gives you more control over the movement. It is the type of mover you might find\nin a first-person shooter or a game with platforming elements.\n\nThe mover is assumed to be a capsule. Using a capsule helps keep movement smooth. The capsule should\nhave a significant radius. It does not have to be a vertical capsule, but that is likely\nto be the easiest setup. There is no explicit handling of rotation. But slow rotation can\nwork with this system.\n\nLet's review the features. First there are a couple world query functions.\n\n`b2World_CastMover()` is a custom shape cast that tries to avoid getting stuck when shapes start out touching.\nThe feature is called _encroachment_. Since the capsule has a significant radius it can move closer\nto a surface it is touching without the inner line segment generating an overlap, which would cause\nthe shape cast to fail. Due to the internal use of GJK, encroachment has little cost. The idea with encroachment is that\nthe mover is trying to slide along a surface and we don't want to stop that even if there is some small movement into the surface.\n\n`b2World_CollideMover()` complements the cast function. This function generates collision planes for touching and/or overlapped surfaces. The character mover is assumed to have a fixed rotation, so it doesn't need contact manifolds or contact points. It just needs collision planes. Each plane is returned with the `b2Plane` and a `b2ShapeId` for each shape the mover is touching.\n\nOnce you have some collision planes from `b2World_CollideMover()`, you can process and filter them to generate an array of `b2CollisionPlane`. These collision planes can then be sent to `b2SolvePlanes()` to generate a new position for the mover\nthat attempts to find the optimal new position given the current position.\n\nThese collision planes support *soft collision* using a `pushLimit`. This push limit is a distance value. A rigid surface will have a push limit of `FLT_MAX`. However, you may want some surfaces to have a limited effect on the character. For example, you may want the mover to push through other players or enemies yet still resolve the collision so they are not overlapped. Another example is a door or elevator that could otherwise push the mover through the floor.\n\nFinally after calling `b2SolverPlanes()` you can call `b2ClipVector()` to clip your velocity vector so the mover will not keep trying to push into a wall, which could lead to a huge velocity accumulation otherwise.\n\nThe `Mover` sample shows all these functions being used together. It also includes other common character features such as acceleration and friction, jumping, and a pogo stick.\n\n![Character Mover](images/mover.png)\n"
  },
  {
    "path": "docs/collision.md",
    "content": "# Collision\nBox2D provides geometric types and functions. These include:\n- primitives: circles, capsules, segments, and convex polygons\n- convex hull and related helper functions\n- mass and bounding box computation\n- local ray and shape casts\n- contact manifolds\n- shape distance\n- time of impact\n- dynamic bounding volume tree\n- character movement solver\n\nThe collision interface is designed to be usable outside of rigid body simulation.\nFor example, you can use the dynamic tree for other aspects of your game besides physics.\n\nHowever, the main purpose of Box2D is to be a rigid body physics\nengine. So the collision interface only contains features that are also useful in\nthe physics simulation.\n\n## Shape Primitives\nShape primitives describe collision geometry and may be used independently of\nphysics simulation. At a minimum, you should understand how to create\nprimitives that can be later attached to rigid bodies.\n\nBox2D shape primitives support several operations:\n- Test a point for overlap with the primitive\n- Perform a ray cast against the primitive\n- Compute the primitive's bounding box\n- Compute the mass properties of the primitive\n\n### Circles\nCircles have a center and radius. Circles are solid.\n\n![Circle](images/circle.svg)\n\n```c\nb2Circle circle;\ncircle.center = (b2Vec2){2.0f, 3.0f};\ncircle.radius = 0.5f;\n```\n\nYou can also initialize a circle and other structures inline. This is an equivalent circle:\n\n```c\nb2Circle circle = {{2.0f, 3.0f}, 0.5f};\n```\n\n### Capsules\nCapsules have two center points and a radius. The center points are the centers of two\nsemicircles that are connected by a rectangle.\n\n![Capsule](images/capsule.svg)\n\n```c\nb2Capsule capsule;\ncapsule.center1 = (b2Vec2){1.0f, 1.0f};\ncapsule.center2 = (b2Vec2){2.0f, 3.0f};\ncapsule.radius = 0.25f;\n```\n\n### Polygons\nBox2D polygons are solid convex polygons. A polygon is convex when all\nline segments connecting two points in the interior do not cross any\nedge of the polygon. Polygons are solid and never hollow. A polygon must\nhave 3 or more vertices.\n\n![Convex and Concave Polygons](images/convex_concave.svg)\n\nPolygons vertices are stored with a counter clockwise winding (CCW). We\nmust be careful because the notion of CCW is with respect to a\nright-handed coordinate system with the z-axis pointing out of the\nplane. This might turn out to be clockwise on your screen, depending on\nyour coordinate system conventions.\n\n![Polygon Winding Order](images/winding.svg)\n\nThe polygon members are public, but you should use initialization\nfunctions to create a polygon. The initialization functions create\nnormal vectors and perform validation.\n\nPolygons in Box2D have a maximum of 8 vertices, as controlled by #B2_MAX_POLYGON_VERTICES.\nIf you have more complex shapes, I recommend to use multiple polygons.\n\nThere are a few ways to create polygons. You can attempt to create them manually,\nbut this is not recommended. Instead there are several functions provided to create them.\n\nFor example if you need a square or box you can use these functions:\n\n```c\nb2Polygon square = b2MakeSquare(0.5f);\nb2Polygon box = b2MakeBox(0.5f, 1.0f);\n```\n\nThe values provided to these functions are *extents*, which are half-widths or half-heights.\nThis corresponds with circles and capsules using radii instead of diameters.\n\nBox2D also supports rounded polygons. These are convex polygons with a thick rounded skin.\n\n```c\nfloat radius = 0.25f;\nb2Polygon roundedBox = b2MakeRoundedBox(0.5f, 1.0f, radius);\n```\n\nIf you want a box that is not centered on the body origin, you can use an offset box.\n\n```c\nb2Vec2 center = {1.0f, 0.0f};\nfloat angle = b2_pi / 4.0f;\nb2Rot rotation = b2MakeRot(angle);\nb2Polygon offsetBox = b2MakeOffsetBox(0.5f, 1.0f, center, rotation);\n```\n\nIf you want a more general convex polygon, you can compute the hull using `b2ComputeHull()`. Then you can\ncreate a polygon from the hull. You can make this rounded as well.\n\n```c\nb2Vec2 points[] = {{-1.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 1.0f}};\nb2Hull hull = b2ComputeHull(points, 3);\nfloat radius = 0.1f;\nb2Polygon roundedTriangle = b2MakePolygon(&hull, radius);\n```\n\nIf you have an automatic process for generating convex polygons, you may feed a degenerate set of points to `b2ComputeHull()`. You should check that the hull was created successfully before creating the polygon or you will get an assertion.\n\n```c\nb2Hull questionableHull = b2ComputeHull(randomPoints, 8);\nif (questionableHull.count == 0)\n{\n    // handle failure\n}\n```\n\nDegenerate points may be coincident and/or collinear. For the hull to be viable, the enclosed area must be sufficiently positive.\n\n### Segments\nSegments are line segments. Segment\nshapes can collide with circles, capsules, and polygons but not with other line segments.\nThe collision algorithms used by Box2D require that at least\none of two colliding shapes has sufficiently positive area. Segment shapes have no area, so\nsegment-segment collision is not possible.\n\n```c\nb2Segment segment1;\nsegment1.point1 = (b2Vec2){0.0f, 0.0f};\nsegment2.point2 = (b2Vec2){1.0f, 0.0f};\n\n// equivalent\nb2Segment segment2 = {{0.0f, 0.0f}, {1.0f, 0.0f}};\n```\n\n### Ghost Collisions\nIn many cases a game environment is constructed by connecting several\nsegment shapes end-to-end. This can give rise to an unexpected artifact\nwhen a polygon slides along the chain of segments. In the figure below there is\n a box colliding with an internal vertex. These *ghost* collisions\nare caused when the polygon collides with an internal vertex generating\nan internal collision normal.\n\n![Ghost Collision](images/ghost_collision.svg){html: width=30%}\n\nIf edge1 did not exist this collision would seem fine. With edge1\npresent, the internal collision seems like a bug. But normally when\nBox2D collides two shapes, it views them in isolation.\n\n`b2ChainSegment` provides a mechanism for eliminating ghost\ncollisions by storing the adjacent *ghost* vertices. Box2D uses these\nghost vertices to prevent internal collisions.\n\n![Ghost Vertices](images/ghost_vertices.svg){html: width=30%}\n\nThe Box2D algorithm for dealing with ghost collisions only supports\none-sided collision. The front face is to the right when looking from the first\nvertex towards the second vertex. This matches the counter-clockwise winding order\nused by polygons.\n\n### Chain segment\nChain segments use a concept called *ghost vertices* that Box2D can use to eliminate ghost\ncollisions.\n\n```c\nb2ChainSegment chainSegment = {0};\nchainSegment.ghost1 = (b2Vec2){1.7f, 0.0f};\nchainSegment.segment = (b2Segment){{1.0f, 0.25f}, {0.0f, 0.0f}};\nchainSegment.ghost2 = (b2Vec2){-1.7f, 0.4f};\n```\n\nThese ghost vertices must align with vertices of neighboring chain segments, making them\ntedious and error-prone to setup.\n\nChain segments are not created directly. Instead, you can create chains of line\nsegments. See `b2ChainDef` and `b2CreateChain()`.\n\n## Geometric Queries\nYou can perform a geometric queries on a single shape.\n\n### Shape Point Test\nYou can test a point for overlap with a shape. You provide a transform\nfor the shape and a world point.\n\n```c\nb2Vec2 point = {5.0f, 2.0f};\nbool hit = b2PointInCapsule(point, &myCapsule);\n```\n\nSee also `b2PointInCircle()` and `b2PointInPolygon()`.\n\n### Ray Cast\nYou can cast a ray at a shape to get the point of first intersection and normal vector.\n\n> **Caution**:\n> No hit will register if the ray starts inside a convex shape like a circle or polygon. This is\n> consistent with Box2D treating convex shapes as solid. \n\n```c\nb2RayCastInput input = {0};\ninput.origin = (b2Vec2){0.0f, 0.0f};\ninput.translation = (b2Vec2){1.0f, 0.0f};\ninput.maxFraction = 1.0f;\n\nb2CastOutput output = b2RayCastPolygon(&input, &myPolygon);\nif (output.hit == true)\n{\n    // do something\n}\n```\n\n### Shape Cast\nYou can also cast a shape at another shape. This uses an abstract way of describing the moving shape. It is represented as a point cloud with a radius. This implies a convex shape even if the input data is not convex. The internal algorithm (GJK) will essentially only use the convex portion.\n\n```c\nb2ShapeCastInput input = {0};\ninput.points[0] = (b2Vec2){1.0f, 0.0f};\ninput.points[1] = (b2Vec2){2.0f, -3.0f};\ninput.radius = 0.2f;\ninput.translation = (b2Vec2){1.0f, 0.0f};\ninput.maxFraction = 1.0f;\n\nb2CastOutput output = b2ShapeCastPolygon(&input, &myPolygon);\nif (output.hit == true)\n{\n    // do something\n}\n```\n\nEven more generic, you can use `b2ShapeCast()` to linearly cast one point cloud at another point cloud. All shape cast functions use this internally.\n\n### Distance\n`b2ShapeDistance()` function can be used to compute the distance between two\nshapes. The distance function needs both shapes to be converted into a\n`b2DistanceProxy` (which are point clouds with radii). There is also some caching used to warm start the\ndistance function for repeated calls. This can improve performance when the shapes move by small amounts.\n\n![Distance Function](images/distance.svg)\n\n### Time of Impact\nIf two shapes are moving fast, they may *tunnel* through each other in a\nsingle time step.\n\n![Tunneling](images/tunneling2.svg){html: width=30%}\n\nThe `b2TimeOfImpact()` function is used to determine the time when two moving shapes collide.\nThis is called the *time of impact* (TOI). The main purpose of `b2TimeOfImpact()` is for\ntunnel prevention. Box2D uses this internally to prevent moving objects from tunneling through\nstatic shapes.\n\nThe `b2TimeOfImpact()` identifies an initial separating axis and\nensures the shapes do not cross on that axis. This process is repeated\nas shapes are moved closer together, until they touch or pass by each other.\n\nThe TOI function might miss collisions that are clear at the final positions.\nNevertheless, it is very fast and adequate for tunnel prevention.\n\n![Captured Collision](images/captured_toi.svg){html: width=30%}\n\n![Missed Collision](images/missed_toi.svg){html: width=30%}\n\nIt is difficult to put a restriction on the rotation magnitude. There\nmay be cases where collisions are missed for small rotations. Normally,\nthese missed rotational collisions should not harm game play. They tend\nto be glancing collisions.\n\nThe function requires two shapes (converted to `b2DistanceProxy`) and two\n`b2Sweep` structures. The sweep structure defines the initial and final\ntransforms of the shapes.\n\nYou can use fixed rotations to perform a *shape cast*. In this case, the\ntime of impact function will not miss any collisions.\n\n### Contact Manifolds\nBox2D has functions to compute contact points for overlapping shapes. If\nwe consider circle-circle or circle-polygon, we can only get one contact\npoint and normal. In the case of polygon-polygon we can get two points.\nThese points share the same normal vector so Box2D groups them into a\nmanifold structure. The contact solver takes advantage of this to\nimprove stacking stability.\n\n![Contact Manifold](images/manifolds.svg)\n\nNormally you don't need to compute contact manifolds directly, however\nyou will likely use the results produced in the simulation.\n\nThe `b2Manifold` structure holds a normal vector and up to two contact\npoints. The contact points store the normal and tangential (friction) impulses\ncomputed in the rigid body simulation.\n\n## Dynamic Tree\n`b2DynamicTree` is used by Box2D to organize large numbers of\nshapes efficiently. The object does not know directly about shapes. Instead it\noperates on axis-aligned bounding boxes (`b2AABB`) with user data integers.\n\nThe dynamic tree is a hierarchical AABB tree. Each internal node in the\ntree has two children. A leaf node is a single user AABB. The tree uses\nrotations to keep the tree balanced, even in the case of degenerate\ninput.\n\nThe tree structure allows for efficient ray casts and region queries.\nFor example, you may have hundreds of shapes in your scene. You could\nperform a ray cast against the scene in a brute force manner by ray\ncasting each shape. This would be inefficient because it does not take\nadvantage of shapes being spread out. Instead, you can maintain a\ndynamic tree and perform ray casts against the tree. This traverses the\nray through the tree skipping large numbers of shapes.\n\nA region query uses the tree to find all leaf AABBs that overlap a query\nAABB. This is faster than a brute force approach because many shapes can\nbe skipped.\n\n![Ray-cast](images/raycast.svg){html: width=30%}\n\n![Overlap Test](images/overlap_test.svg){html: width=30%}\n\nNormally you will not use the dynamic tree directly. Rather you will go\nthrough the `b2World` functions for ray casts and region queries. If you plan\nto instantiate your own dynamic tree, you can learn how to use it by\nlooking at how Box2D uses it. Also see the `DynamicTree` sample.\n"
  },
  {
    "path": "docs/extra.css",
    "content": "/*\nDoxygen CSS overrides\nAdapted from: https://github.com/MaJerle/doxygen-dark-theme\nblog-light: #fafafa\nblog-dark: #252627\nDark background: #353629;\nNew light dark background #32363d\nLight background: #dfe5f2;\n*/\n\nbody {\n\tbackground: #292a2d;\n\tbackground-image: none;\n\tcolor: #D8D8D8;\n}\n\n#titlearea {\n    border-bottom: 1px solid #32363d;\n\tbackground-color: #292a2d;\n}\n\ndiv.contents {\n    max-width: 1000px;\n}\n\n/* this works with doxygen /image */\n.image {\n\tbackground-color: #CCCCCC;\n\tborder: 10px solid #CCCCCC;\n}\n\n/* this kind of works with doxygen markdown */\nimg.inline {\n\tbackground-color: #CCCCCC;\n\tborder: 10px solid #CCCCCC;\n\tmargin: 0px;\n}\n\n.caption {\n\tpadding-top: 10px;\n\tcolor: black;\n}\n\ndiv.fragment, pre.fragment {\n\tmargin: 20px 0px;\n\tpadding: 10px;\n}\n\nblockquote.doxtable {\n\tborder: 1px solid #000000;\n\tbackground: #32363d;\n    background-color: #bf5f82;\n    background-color: #456114;\n    margin: 10px 24px 10px 4px;\n}\n\ndiv.toc {\n\tmargin: 0 !important;\n\tborder-radius: 4px !important;\n}\n\ndiv.toc h3 {\n\tfont-size: 150%;\n\tcolor: inherit;\n}\n\n.contents table.doxtable {\n\tmargin: 0 auto;\n}\n\n.fieldtable {\n\tbox-shadow: none !important;\n\t-webkit-box-shadow: none;\n\t-moz-box-shadow: none;\n}\n\n.memitem,\n.memproto,\n.memdoc {\n\tbox-shadow: none;\n\t-webkit-box-shadow: none;\n\t-moz-box-shadow: none;\n\tbackground-image: none;\n}\n\n.tablist a:hover,\n.tablist li.current a {\n\ttext-shadow: none;\n\t-moz-text-shadow: none;\n\t-webkit-text-shadow: none;\n}\n\n.textblock h1 {\n    border-bottom: 1px solid #32363d;\n    border-left: 3px solid #32363d;\n    margin: 40px 0px 10px 0px;\n    padding-bottom: 10px;\n    padding-top: 10px;\n    padding-left: 5px;\n}\n\n.textblock h1:first-child {\n\tmargin-top: 10px;\n}\n\ndl.note,\ndl.warning,\ndl.todo,\ndl.deprecated,\ndl.reflist {\n\tborder: 0;\n\tpadding: 0px;\n\tmargin: 4px 0px 4px 0px;\n\tborder-radius: 4px;\n}\n\ndl.note dt,\ndl.warning dt,\ndl.todo dt,\ndl.deprecated dt,\ndl.reflist dt {\n\tmargin: 0;\n\tfont-size: 14px;\n\tpadding: 2px 4px;\n\n\tborder: none;\n\tborder-top-left-radius: 0px;\n\tborder-top-right-radius:0px;\n\n\tfont-weight: bold;\n\ttext-transform: uppercase;\n\tcolor: #FFFFFF !important;\n\n\tbox-shadow: none;\n\t-webkit-box-shadow: none;\n\t-moz-box-shadow: none;\n\ttext-shadow: none;\n}\n\ndl.note dd,\ndl.warning dd,\ndl.todo dd,\ndl.deprecated dd,\ndl.reflist dd {\n\tmargin: 0;\n\tpadding: 4px;\n\tbackground: none;\n\n\tcolor: #222222;\n\n\tborder: 1px solid;\n\tborder-bottom-left-radius: 0px;\n\tborder-bottom-right-radius: 0px;\n\tborder-top: none;\n\n\tbox-shadow: none;\n\t-webkit-box-shadow: none;\n\t-moz-box-shadow: none;\n\ttext-shadow: none;\n}\n\ndl.reflist dd {\n\tmargin-bottom: 15px;\n}\n\ndl.note dt {\n\tbackground-color: #cbc693;\n}\n\ndl.warning dt {\n\tbackground-color: #bf5f82;\n}\n\ndl.todo dt {\n\tbackground-color: #82b3c9;\n}\n\ndl.deprecated dt {\n\tbackground-color: #af8eb5;\n}\n\ndl.reflist dt {\n\tbackground-color: #cbae82;\n}\n\ndl.note dd {\n\tbackground-color: #fff9c4;\n\tborder-color: #cbc693;\n}\n\ndl.warning dd {\n\tbackground-color: #f48fb1;\n\tborder-color: #bf5f82;\n}\n\ndl.todo dd {\n\tbackground-color: #b3e5fc;\n\tborder-color: #82b3c9;\n}\n\ndl.deprecated dd {\n\tbackground-color: #e1bee7;\n\tborder-color: #af8eb5;\n}\n\ndl.reflist dd {\n\tbackground-color: #ffe0b2;\n\tborder-color: #cbae82;\n}\n\n#docs_list {\n\tpadding: 0 10px;\n}\n\n#docs_list ul {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n#docs_list ul li {\n\tdisplay: inline-block;\n\tborder-right: 1px solid #BFBFBF;\n}\n\n#docs_list ul li:last-child {\n\tborder-right: none;\n}\n\n#docs_list ul li a {\n\tdisplay: block;\n\tpadding: 8px 13px;\n\tfont-weight: bold;\n\tfont-size: 15px;\n}\n\n#docs_list ul li a:hover,\n#docs_list ul li a.docs_current {\n\ttext-decoration: underline;\n}\n\n.ui-resizable-e {\n\twidth: 3px;\n}\n\n.download_url {\n\tfont-weight: bold;\n\tfont-size: 150%;\n\tline-height: 150%;\n}\n\nspan.lineno a {\n\ttext-decoration: none;\n}\n\n.directory .arrow {\n\theight: initial;\n}\n\n.directory td.entry {\n\tpadding: 3px 6px;\n}\n\n.memproto table td {\n\tfont-family: monospace, fixed !important;\n}\n\ntd.memItemLeft, td.memItemRight {\n\tfont-family: monospace, fixed;\n}\n\n.paramname, .paramname em {\n\tfont-style: italic;\n}\n\n.memdoc {\n\ttext-shadow: none;\n}\n\n.memItem {\n\tfont-family: monospace, fixed;\n}\n\n.memItem table {\n\tfont-family: inherit;\n}\n\nimg.footer {\n\theight: 22px;\n}\n\n.sm-dox {\n\tbackground: #dfe5f2 !important;\n}\n\n.sm-dox a {\n\tbackground: none;\n}\n\ndiv.fragment, pre.fragment {\n\tborder: 1px solid #000000;\n\tbackground: #32363d;\n}\n\na, a:link, a:visited {\n\tcolor: #67d8ef !important;\n}\n\n.highlighted {\n\tbackground: none !important;\n}\n\na.highlighted {\n\tbackground: none !important;\n}\n\n#main-nav {\n\tborder-bottom: 1px solid #32363d;\n}\n\n#main-nav .sm-dox {\n\tbackground: transparent !important;\n}\n\n.sm-dox a {\n\ttext-shadow: none !important;\n\tbackground: transparent !important;\n}\n\n.sm-dox a:hover {\n\tbackground: #282923 !important;\n}\n\n.sm-dox {\n\ttext-shadow: none !important;\n\tbox-shadow: none !important;\n}\n\n.sm-dox ul {\n\tborder: 1px solid #000000;\n\tbackground: #32363d;\n}\n\n.directory tr.even {\n\tbackground: #36383d;\n}\n\n.directory tr.odd {\n\tbackground: #292a2d;\n}\n\n#MSearchSelectWindow {\n\tborder: 1px solid #000000;\n\tbackground: #32363d;\n}\n\na.selectItem {\n\tpadding: 3px;\n}\n\na.SelectItem:hover {\n\tbackground: #282923 !important;\n}\n\n#MSearchResultsWindow {\n\tborder: 1px solid #000000;\n\tbackground: #32363d;\n\tcolor: #67d8ef !important;;\n}\n\n#nav-tree {\n\tbackground: transparent;\n}\n\n#nav-tree .selected {\n\tbackground-image: none;\n\tbackground: #32363d;\n}\n\n\ndiv.toc {\n\tbackground: #32363d;\n\tborder: 1px solid #000000;\n}\n\ndiv.toc h3 {\n\tfont-size: 150%;\n\tcolor: inherit;\n}\n\ntable.doxtable tr:nth-child(even) td {\n\tbackground: #32363d;\n}\n\ndiv.header {\n\tbackground: transparent;\n\tborder-bottom: 1px solid #32363d;\n}\n\n.fieldtable th {\n\tbackground: #282923;\n\tcolor: inherit;\n}\n\n.memdoc {\n\tborder: 1px solid #A8B8D9;\n}\n\n.tabs, .tabs2, .tabs3 {\n\tbackground: #DDDDDD;\n}\n\n.tablist li {\n\tbackground: transparent !important;\n}\n\n.tablist a {\n\tbackground-image: none;\n\tborder-right: 1px solid #999999;\n\n\tcolor: #32363d;\n}\n\n.tablist a:hover,\n.tablist li.current a {\n\ttext-decoration: none;\n\tcolor: #000000;\n\tbackground: #CCCCCC;\n\tbackground-image: none;\n}\n\n#docs_list {\n\tbackground: #32363d;\n}\n\n#docs_list ul li {\n\tborder-right: 1px solid #BFBFBF;\n}\n\n#docs_list ul li a {\n\tcolor: #1b1e21;\n}\n\n#docs_list ul li a:hover,\n#docs_list ul li a.docs_current {\n\tbackground: #282923;\n}\n\n.ui-resizable-e {\n\tbackground: #32363d;\n}\n\ndiv.line {\n\tbackground: transparent;\n\tcolor: inherit;\n}\n\ndiv.line a {\n\ttext-decoration: underline;\n    color: inherit;\n}\n\nspan.keyword {\n\tcolor: #f92472;\n\tfont-style: italic;\n}\n\nspan.keywordtype {\n\tcolor: #67cfc1;\n\tfont-style: italic;\n}\n\nspan.keywordflow {\n\tcolor: #f92472;\n\tfont-style: italic;\n}\n\nspan.comment {\n\tcolor: #74705a;\n}\n\nspan.preprocessor {\n\tcolor: #a6e22b;\n}\n\nspan.stringliteral {\n\tcolor: #e7db74;\n}\n\nspan.charliteral {\n\tcolor: #e7db74;\n}\n\nspan.vhdldigit { \n\tcolor: #ff00ff;\n}\n\nspan.vhdlchar { \n\tcolor: #000000;\n}\n\nspan.vhdlkeyword { \n\tcolor: #700070;\n}\n\nspan.vhdllogic { \n\tcolor: #ff0000;\n}\n\nspan.lineno {\n\tbackground: transparent;\n}\n\nspan.lineno a {\n\tbackground: transparent;\n}\n\n.mdescLeft, .mdescRight, .memItemLeft, .memItemRight,\n.memTemplItemLeft, .memTemplItemRight, .memTemplParams {\n\tbackground: #32363d;\n\tcolor: inherit;\n}\n\n.memSeparator {\n\tborder: none;\n\tbackground: transparent;\n}\n\nh2.groupheader {\n\tcolor: #67d8ef;\n}\n\n.memtitle {\n\tbackground: #32363d !important;\n\tborder-color: #000000;\n}\n\n.memitem {\n\tbackground: #32363d !important;\n\tcolor: inherit;\n\ttext-shadow: none;\n}\n\n.memproto {\n\tbackground: inherit;\n\tborder-color: #000000;\n\tcolor: inherit;\n\ttext-shadow: none;\n}\n\n.memproto table td {\n\tfont-family: monospace, fixed !important;\n}\n\ntd.memItemLeft, td.memItemRight {\n\tfont-family: monospace, fixed;\n}\n\n.paramname, .paramname em {\n\tcolor: #bf5f82;\n}\n\n.memdoc {\n\tbackground: inherit;\n\tborder-color: #000000;\n}\n\n#nav-path {\n\tbackground: transparent;\n}\n\n#nav-path ul {\n\tbackground: transparent;\n\tcolor: inherit;\n\tborder: none;\n\tborder-top: 1px solid #32363d;\n}\n\n.navpath li.footer {\n\tcolor: inherit;\n}\n\n.navpath li.navelem a {\n\ttext-shadow: none;\n}\n\n::-webkit-scrollbar {\n\twidth: 10px;\n}\n\n::-webkit-scrollbar-track {\n\tborder-radius: 10px;\n}\n\n::-webkit-scrollbar-thumb {\n\tbackground: #234567;\n\tborder: none;\n}\n\n::-webkit-scrollbar-thumb:hover {\n\tbackground: #32363d; \n}\n\nh1.glow, h2.glow, h3.glow,\nh4.glow, h5.glow, h6.glow {\n\ttext-shadow: 0 0 15px #67d8ef;\n}\n"
  },
  {
    "path": "docs/foundation.md",
    "content": "# Foundations\nBox2D provides minimal base functionality for allocation hooks and vector math. The C interface\nallows most runtime data and types to be defined internally in the `src` folder.\n\n## Assertions\nBox2D will assert on bad input. This includes things like sending in NaN or infinity for values. It will assert if\nyou use negative values for things that should only be positive, such as density.\n\nBox2D will also assert if an internal bug is detected. For this reason, it is advisable to build Box2D from source.\nThe Box2D library compiles in about a second on my computer.\n\nYou may wish to capture assertions in your application. In this case you can use `b2SetAssertFcn()`. This allows you\nto override the debugger break and/or perform your own error handling.\n\n## Allocation\nBox2D uses memory efficiently and minimizes per frame allocations by pooling memory. The engine quickly adapts to the\nsimulation size. After the first step or two of simulation, there should be no further per frame allocations.\n\nAs bodies, shapes, and joints are created and destroyed, their memory will be recycled. Internally all this data is stored in contiguous arrays. When an object is destroyed, the array element will be marked as empty. And when an object is created it will use empty slots in the array using an efficient free list.\n\nOnce the internal memory pools are initially filled, the only allocations should be for sleeping islands since their data is copied out of the main simulation. Generally, these allocations should be infrequent.\n\nYou can provide a custom allocator using `b2SetAllocator()` and you can get the number of bytes allocated using `b2GetByteCount()`.\n\n## Version\nThe b2Version structure holds the current version so you can query this\nat run-time using `b2GetVersion()`.\n\n```c\nb2Version version = b2GetVersion();\nprintf(\"Box2D version %d.%d.%d\\n\", version.major, version.minor, version.revision);\n```\n\n## Vector Math\nBox2D includes a small vector math library including types `b2Vec2`, `b2Rot`, `b2Transform`, and `b2AABB`. This has been\ndesigned to suit the internal needs of Box2D and the interface. All the\nmembers are exposed, so you may use them freely in your application.\n\nThe math library is kept simple to make Box2D easy to port and maintain.\n\n## Multithreading {#multi}\nBox2D has been highly optimized for multithreading. Multithreading is not required and by default Box2D will run single-threaded. If performance is important for your application, you should consider using the multithreading interface.\n\nBox2D multithreading has been designed to work with your application's task system. Box2D does\nnot create threads. The Samples application shows how to do this using the open source tasks system [enkiTS](https://github.com/dougbinks/enkiTS).\n\nMultithreading is established for each Box2D world you create and must be hooked up to\nthe world definition. See `b2TaskCallback()`, `b2EnqueueTaskCallback()`, and `b2FinishTaskCallback()` for more details. Also see `b2WorldDef::workerCount`, `b2WorldDef::enqueueTask`, and `b2WorldDef::finishTask`.\n\nThe multithreading design for Box2D is focused on [data parallelism](https://en.wikipedia.org/wiki/Data_parallelism). The idea is to use multiple cores to complete the world simulation as fast as possible. Box2D multithreading is not designed for [task parallelism](https://en.wikipedia.org/wiki/Task_parallelism). Often in games you may have a render thread and an audio thread that do work in isolation from the main thread. Those are examples of task parallelism.\n\nSo when you design your game loop, you should let Box2D *go wide* and use multiple cores to finish its work quickly, without other threads trying to interact with the Box2D world.\n\nIn a multithreaded environment you must be careful to avoid [race conditions](https://en.wikipedia.org/wiki/Race_condition). Modifying the world while it is simulating will lead to unpredictable behavior and this is never safe. It is also not safe to read data from a Box2D world while it is simulating. Box2D may move data structures to improve cache performance. So it is very likely that you will read garbage data.\n\n> **Caution**:\n> Do not perform read or write operations on a Box2D world during `b2World_Step()`\n\n> **Caution**:\n> Do not write to the Box2D world from multiple threads\n\nIt *is safe* to do ray-casts, shape-casts, and overlap tests from multiple threads outside of `b2World_Step()`. Generally, any read-only operation is safe to do multithreaded outside of `b2World_Step()`. This can be very useful if you have multithreaded game logic.\n\n## Multithreading Multiple Worlds\nSome applications may wish to create multiple Box2D worlds and simulate them on different threads. This works fine because Box2D has very limited use of globals.\n\nThere are a few caveats:\n- You will get a race condition if you create or destroy Box2D worlds from multiple threads. You should use a mutex to guard this.\n- If you will simulate multiple Box2D worlds simultaneously, then they should probably not use a task system. Otherwise you're likely to get preemption.\n- Any callbacks you hook up to Box2D must be thread-safe, such as memory allocators.\n- All of the limitations for single world simulation still apply.\n"
  },
  {
    "path": "docs/hello.md",
    "content": "# Hello Box2D {#hello}\nIn the distribution of Box2D is a Hello World unit test written in C. The test\ncreates a large ground box and a small dynamic box. This code does not\ncontain any graphics. All you will see is text output in the console of\nthe box's position over time.\n\nThis is a good example of how to get up and running with Box2D.\n\n## Creating a World\nEvery Box2D program begins with the creation of a world object.\nThe world is the physics hub that manages memory, objects, and simulation.\nThe world is represented by an opaque handle called `b2WorldId`.\n\nIt is easy to create a Box2D world. First, I create the world definition:\n\n```c\nb2WorldDef worldDef = b2DefaultWorldDef();\n```\n\nThe world definition is a temporary object that you can create on the stack. The function\n`b2DefaultWorldDef()` populates the world definition with default values. This is necessary because C does not have constructors and zero initialization is not appropriate for `b2WorldDef`.\n\nNow I configure the world gravity vector. Note that Box2D has no concept of *up* and you may point gravity in any direction you like. Box2D example code uses the positive y-axis as the up direction.\n\n```c\nworldDef.gravity = (b2Vec2){0.0f, -10.0f};\n```\n\nNow I create the world object.\n\n```c\nb2WorldId worldId = b2CreateWorld(&worldDef);\n```\n\nThe world creation copies all the data it needs out of the world definition, so the world\ndefinition is no longer needed.\n\nSo now we have our physics world, let's start adding some stuff to it.\n\n## Creating a Ground Box\nBodies are built using the following steps:\n1. Define a body with position, damping, etc.\n2. Use the world id to create the body.\n3. Define shapes with friction, density, etc.\n4. Create shapes on the body.\n\nFor step 1 I create the ground body. For this I need a body\ndefinition. With the body definition I specify the initial position of\nthe ground body.\n\n```c\nb2BodyDef groundBodyDef = b2DefaultBodyDef();\ngroundBodyDef.position = (b2Vec2){0.0f, -10.0f};\n```\n\nFor step 2 the body definition and the world id are used to create\nthe ground body. Again, the definition is fully copied and may leave scope after\nthe body is created. Bodies are static by default. Static bodies don't collide\nwith other static bodies and are immovable by the simulation.\n\n```c\nb2BodyId groundId = b2CreateBody(worldId, &groundBodyDef);\n```\n\nNotice that `worldId` is passed by value. Ids are small structures that should\nbe passed by value.\n\nFor step 3 I create a ground polygon. I use the `b2MakeBox()` helper function to\nform the ground polygon into a box shape, with the box centered on the\norigin of the parent body.\n\n```c\nb2Polygon groundBox = b2MakeBox(50.0f, 10.0f);\n```\n\nThe `b2MakeBox()` function takes the **half-width** and\n**half-height** (extents). So in this case the ground box is 100\nunits wide (x-axis) and 20 units tall (y-axis). Box2D is tuned for\nmeters, kilograms, and seconds. So you can consider the extents to be in\nmeters. Box2D generally works best when objects are the size of typical\nreal world objects. For example, a barrel is about 1 meter tall. Due to\nthe limitations of floating point arithmetic, using Box2D to model the\nmovement of glaciers or dust particles might not work well.\n\nI'll finish the ground body in step 4 by creating the shape. For this step\nI need to create a shape definition which works fine with the default value.\n\n```c\nb2ShapeDef groundShapeDef = b2DefaultShapeDef();\nb2CreatePolygonShape(groundId, &groundShapeDef, &groundBox);\n```\n\nBox2D does not keep a reference to the shape data. It copies the data into the internal\ndata structures.\n\nNote that every shape must have a parent body, even shapes that are\nstatic. You may attach multiple shapes to a single parent body.\n\nWhen you attach a shape, the shape's\ncoordinates become local to the body. So when the body moves, so does\nthe shape. A shape's world transform is inherited from the parent\nbody. A shape does not have a transform independent of the body. So we\ndon't move a shape around on the body. Moving or modifying a shape that\nis on a body is possible with certain functions, but it should not be part\nof normal simulation. The reason is simple: a body with\nmorphing shapes is not a rigid body, but Box2D is a rigid body engine.\nMany of the algorithms in Box2D are based on the rigid body model and optimized with\nthat in mind. If this is violated you may get unexpected behavior.\n\n## Creating a Dynamic Body\nI can use the same technique to create a\ndynamic body. The main difference, besides dimensions, is that I must\nestablish the dynamic body's mass properties.\n\nFirst I create the body using CreateBody. By default bodies are static,\nso I should set the `b2BodyType` at creation time to make the body\ndynamic. I should also use the body definition to put the body at the\nintended position for simulation. Creating a body then moving it afterwards is\nvery inefficient and may cause lag spikes, especially if many bodies are created at\nthe origin.\n\n```c\nb2BodyDef bodyDef = b2DefaultBodyDef();\nbodyDef.type = b2_dynamicBody;\nbodyDef.position = (b2Vec2){0.0f, 4.0f};\nb2BodyId bodyId = b2CreateBody(worldId, &bodyDef);\n```\n\n> **Caution**:\n> You must set the body type to `b2_dynamicBody` if you want the body to\n> move in response to forces (such as gravity).\n\nNext I create and attach a polygon shape using a shape definition.\nFirst I create another box shape:\n\n```c\nb2Polygon dynamicBox = b2MakeBox(1.0f, 1.0f);\n```\n\nNext I create a shape definition for the box. Notice that I set\ndensity to 1. The default density is 1, so this is unnecessary. Also,\nthe friction on the surface material is set to 0.3.\n\n```c\nb2ShapeDef shapeDef = b2DefaultShapeDef();\nshapeDef.density = 1.0f;\nshapeDef.material.friction = 0.3f;\n```\n\n> **Caution**:\n> A dynamic body should have at least one shape with a non-zero density.\n> Otherwise you will get strange behavior.\n\nUsing the shape definition I can now create the shape. This\nautomatically updates the mass of the body. You can add as many shapes\nas you like to a body. Each one contributes to the total mass.\n\n```c\nb2CreatePolygonShape(bodyId, &shapeDef, &dynamicBox);\n```\n\nThat's it for initialization. We are now ready to begin simulating.\n\n## Simulating the World\nI have initialized the ground box and a dynamic box. Now we are\nready to set Newton loose to do his thing. I just have a couple more\nissues to consider.\n\nBox2D uses a computational algorithm called an integrator. Integrators\nsimulate the physics equations at discrete points of time. This goes\nalong with the traditional game loop where we essentially have a flip\nbook of movement on the screen. So we need to pick a time step for\nBox2D. Generally physics engines for games like a time step at least as\nfast as 60Hz or 1/60 seconds. You can get away with larger time steps,\nbut you will have to be more careful about setting up your simulation.\nIt is also not good for the time step to vary from frame to frame. A\nvariable time step produces variable results, which makes it difficult\nto debug. So don't tie the time step to your frame rate. Without further ado,\nhere is the time step.\n\n```c\nfloat timeStep = 1.0f / 60.0f;\n```\n\nIn addition to the integrator, Box2D also uses a larger bit of code\ncalled a constraint solver. The constraint solver solves all the\nconstraints in the simulation, one at a time. A single constraint can be\nsolved perfectly. However, when Box2D solves one constraint, it slightly\ndisrupts other constraints. To get a good solution, Box2D needs to iterate\nover all constraints a number of times.\n\nBox2D uses sub-stepping as a means of constraint iteration. It lets the\nsimulation move forward in time by small amounts and each constraint\ngets a chance to react to the changes.\n\nThe suggested sub-step count for Box2D is 4. You can tune this number\nto your liking, just keep in mind that this has a trade-off between\nperformance and accuracy. Using fewer sub-steps increases performance\nbut accuracy suffers. Likewise, using\nmore sub-steps decreases performance but improves the quality of your\nsimulation. For this example, I will use 4 sub-steps.\n\n```c\nint subStepCount = 4;\n```\n\nNote that the time step and the sub-step count are related. As the time step\ndecreases, the size of the sub-steps also decreases. For example, at 60Hz\ntime step and 4 sub-steps, the sub-steps operate at 240Hz. With 8 sub-steps\nthe sub-step is 480Hz.\n\nWe are now ready to begin the simulation loop. In your game the\nsimulation loop can be merged with your game loop. In each pass through\nyour game loop you call `b2World_Step()`. Just one call is usually enough,\ndepending on your frame rate and your physics time step. I recommend this article\n[Fix Your Timestep!](https://gafferongames.com/post/fix_your_timestep/) to run\nyour game simulation at a fixed rate.\n\nThe Hello World test was designed to be simple, so it has no\ngraphical output. The code prints out the position and rotation of the\ndynamic body. Here is the simulation loop that simulates 90 time steps\nfor a total of 1.5 seconds of simulated time.\n\n```c\nfor (int i = 0; i < 90; ++i)\n{\n\tb2World_Step(worldId, timeStep, subStepCount);\n    b2Vec2 position = b2Body_GetPosition(bodyId);\n    b2Rot rotation = b2Body_GetRotation(bodyId);\n    printf(\"%4.2f %4.2f %4.2f\\n\", position.x, position.y, b2Rot_GetAngle(rotation));\n}\n```\n\nNotice that the rotation of the body is returned in a `b2Rot` struct (short for rotation). This\nstruct holds the rotation in a format that is fast for simulation. You may use `b2Rot_GetAngle`\nto get the rotation in radians.\n\nThe output shows the box falling and landing on the ground box. Your\noutput should look like this:\n\n```\n0.00 4.00 0.00\n0.00 3.99 0.00\n0.00 3.98 0.00\n...\n0.00 1.25 0.00\n0.00 1.13 0.00\n0.00 1.01 0.00\n```\n\n## Cleanup\nWhen you are done with the simulation, you should destroy the world.\n\n```c\nb2DestroyWorld(worldId);\n```\n\nThis efficiently destroys all bodies, shapes, and joints in the simulation.\n"
  },
  {
    "path": "docs/layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doxygenlayout version=\"1.0\">\n  <!-- Generated by doxygen 1.10.0 -->\n  <!-- Navigation index tabs for HTML output -->\n  <navindex>\n    <tab type=\"mainpage\" visible=\"yes\" title=\"\"/>\n    <tab type=\"pages\" visible=\"yes\" title=\"\" intro=\"\"/>\n    <tab type=\"topics\" visible=\"yes\" title=\"Reference\" intro=\"\"/>\n    <tab type=\"modules\" visible=\"yes\" title=\"\" intro=\"\">\n      <tab type=\"modulelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n      <tab type=\"modulemembers\" visible=\"yes\" title=\"\" intro=\"\"/>\n    </tab>\n    <tab type=\"namespaces\" visible=\"yes\" title=\"\">\n      <tab type=\"namespacelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n      <tab type=\"namespacemembers\" visible=\"yes\" title=\"\" intro=\"\"/>\n    </tab>\n    <tab type=\"concepts\" visible=\"yes\" title=\"\">\n    </tab>\n    <tab type=\"interfaces\" visible=\"yes\" title=\"\">\n      <tab type=\"interfacelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n      <tab type=\"interfaceindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/>\n      <tab type=\"interfacehierarchy\" visible=\"yes\" title=\"\" intro=\"\"/>\n    </tab>\n    <tab type=\"classes\" visible=\"yes\" title=\"\">\n      <tab type=\"classlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n      <tab type=\"classindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/>\n      <tab type=\"hierarchy\" visible=\"yes\" title=\"\" intro=\"\"/>\n      <tab type=\"classmembers\" visible=\"yes\" title=\"\" intro=\"\"/>\n    </tab>\n    <tab type=\"structs\" visible=\"yes\" title=\"\">\n      <tab type=\"structlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n      <tab type=\"structindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/>\n    </tab>\n    <tab type=\"exceptions\" visible=\"yes\" title=\"\">\n      <tab type=\"exceptionlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n      <tab type=\"exceptionindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/>\n      <tab type=\"exceptionhierarchy\" visible=\"yes\" title=\"\" intro=\"\"/>\n    </tab>\n    <tab type=\"files\" visible=\"yes\" title=\"\">\n      <tab type=\"filelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n      <tab type=\"globals\" visible=\"yes\" title=\"\" intro=\"\"/>\n    </tab>\n    <tab type=\"examples\" visible=\"yes\" title=\"\" intro=\"\"/>\n  </navindex>\n\n  <!-- Layout definition for a class page -->\n  <class>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_HEADERFILE\"/>\n    <inheritancegraph visible=\"yes\"/>\n    <collaborationgraph visible=\"yes\"/>\n    <memberdecl>\n      <nestedclasses visible=\"yes\" title=\"\"/>\n      <publictypes title=\"\"/>\n      <services title=\"\"/>\n      <interfaces title=\"\"/>\n      <publicslots title=\"\"/>\n      <signals title=\"\"/>\n      <publicmethods title=\"\"/>\n      <publicstaticmethods title=\"\"/>\n      <publicattributes title=\"\"/>\n      <publicstaticattributes title=\"\"/>\n      <protectedtypes title=\"\"/>\n      <protectedslots title=\"\"/>\n      <protectedmethods title=\"\"/>\n      <protectedstaticmethods title=\"\"/>\n      <protectedattributes title=\"\"/>\n      <protectedstaticattributes title=\"\"/>\n      <packagetypes title=\"\"/>\n      <packagemethods title=\"\"/>\n      <packagestaticmethods title=\"\"/>\n      <packageattributes title=\"\"/>\n      <packagestaticattributes title=\"\"/>\n      <properties title=\"\"/>\n      <events title=\"\"/>\n      <privatetypes title=\"\"/>\n      <privateslots title=\"\"/>\n      <privatemethods title=\"\"/>\n      <privatestaticmethods title=\"\"/>\n      <privateattributes title=\"\"/>\n      <privatestaticattributes title=\"\"/>\n      <friends title=\"\"/>\n      <related title=\"\" subtitle=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <services title=\"\"/>\n      <interfaces title=\"\"/>\n      <constructors title=\"\"/>\n      <functions title=\"\"/>\n      <related title=\"\"/>\n      <variables title=\"\"/>\n      <properties title=\"\"/>\n      <events title=\"\"/>\n    </memberdef>\n    <allmemberslink visible=\"yes\"/>\n    <usedfiles visible=\"$SHOW_USED_FILES\"/>\n    <authorsection visible=\"yes\"/>\n  </class>\n\n  <!-- Layout definition for a namespace page -->\n  <namespace>\n    <briefdescription visible=\"yes\"/>\n    <memberdecl>\n      <nestednamespaces visible=\"yes\" title=\"\"/>\n      <constantgroups visible=\"yes\" title=\"\"/>\n      <interfaces visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <concepts visible=\"yes\" title=\"\"/>\n      <structs visible=\"yes\" title=\"\"/>\n      <exceptions visible=\"yes\" title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <properties title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <properties title=\"\"/>\n    </memberdef>\n    <authorsection visible=\"yes\"/>\n  </namespace>\n\n  <!-- Layout definition for a concept page -->\n  <concept>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_HEADERFILE\"/>\n    <definition visible=\"yes\" title=\"\"/>\n    <detaileddescription title=\"\"/>\n    <authorsection visible=\"yes\"/>\n  </concept>\n\n  <!-- Layout definition for a file page -->\n  <file>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_INCLUDE_FILES\"/>\n    <includegraph visible=\"yes\"/>\n    <includedbygraph visible=\"yes\"/>\n    <sourcelink visible=\"yes\"/>\n    <memberdecl>\n      <interfaces visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <structs visible=\"yes\" title=\"\"/>\n      <exceptions visible=\"yes\" title=\"\"/>\n      <namespaces visible=\"yes\" title=\"\"/>\n      <concepts visible=\"yes\" title=\"\"/>\n      <constantgroups visible=\"yes\" title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <properties title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <properties title=\"\"/>\n    </memberdef>\n    <authorsection/>\n  </file>\n\n  <!-- Layout definition for a group page -->\n  <group>\n    <briefdescription visible=\"yes\"/>\n    <groupgraph visible=\"yes\"/>\n    <memberdecl>\n      <nestedgroups visible=\"yes\" title=\"\"/>\n      <modules visible=\"yes\" title=\"\"/>\n      <dirs visible=\"yes\" title=\"\"/>\n      <files visible=\"yes\" title=\"\"/>\n      <namespaces visible=\"yes\" title=\"\"/>\n      <concepts visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <enumvalues title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <signals title=\"\"/>\n      <publicslots title=\"\"/>\n      <protectedslots title=\"\"/>\n      <privateslots title=\"\"/>\n      <events title=\"\"/>\n      <properties title=\"\"/>\n      <friends title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <pagedocs/>\n      <inlineclasses title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <enumvalues title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <signals title=\"\"/>\n      <publicslots title=\"\"/>\n      <protectedslots title=\"\"/>\n      <privateslots title=\"\"/>\n      <events title=\"\"/>\n      <properties title=\"\"/>\n      <friends title=\"\"/>\n    </memberdef>\n    <authorsection visible=\"yes\"/>\n  </group>\n\n  <!-- Layout definition for a C++20 module page -->\n  <module>\n    <briefdescription visible=\"yes\"/>\n    <exportedmodules visible=\"yes\"/>\n    <memberdecl>\n      <concepts visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <enums title=\"\"/>\n      <typedefs title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <membergroups title=\"\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdecl>\n      <files visible=\"yes\"/>\n    </memberdecl>\n  </module>\n\n  <!-- Layout definition for a directory page -->\n  <directory>\n    <briefdescription visible=\"yes\"/>\n    <directorygraph visible=\"yes\"/>\n    <memberdecl>\n      <dirs visible=\"yes\"/>\n      <files visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n  </directory>\n</doxygenlayout>\n"
  },
  {
    "path": "docs/loose_ends.md",
    "content": "# Loose Ends\n\n## User Data\nBodies, shapes, and joints allow you to attach user data\nas a `void*`. This is handy when you are examining Box2D data\nstructures and you want to determine how they relate to the objects in\nyour game engine.\n\nFor example, it is typical to attach an entity pointer to the rigid body\non that entity. This sets up a circular reference. If you have the entity,\nyou can get the body. If you have the body, you can get the entity.\n\n```c\nGameEntity* entity = GameCreateEntity();\nb2BodyDef bodyDef = b2DefaultBodyDef();\nbodyDef.userData = entity;\nentity->bodyId = b2CreateBody(myWorldId, &bodyDef);\n```\n\nHere are some examples of cases where you would need the user data:\n-   Applying damage to an entity using a collision result.\n-   Playing a scripted event if the player is inside an axis-aligned box.\n-   Accessing a game structure when Box2D notifies you that a joint is\n    going to be destroyed.\n\nKeep in mind that user data is optional and you can put anything in it.\nHowever, you should be consistent. For example, if you want to store an\nentity pointer on one body, you should keep an entity pointer on all\nbodies. Don't store a `GameEntity` pointer on one body, and a `ParticleSystem`\npointer on another body. Casting a `GameEntity` to a `ParticleSystem` pointer\nmay lead to a crash.\n\n## Pixels and Coordinate Systems\nI recommend using MKS (meters, kilograms, and seconds) units and\nradians for angles. You may have trouble working with meters because\nyour game is expressed in terms of pixels. To deal with this in the\nsample I have the whole *game* world in meters and just use an OpenGL\nviewport transformation to scale the world into screen space.\n\nYou use code like this to scale your graphics.\n\n```c\nfloat lowerX = -25.0f, upperX = 25.0f, lowerY = -5.0f, upperY = 25.0f;\ngluOrtho2D(lowerX, upperX, lowerY, upperY);\n```\n\nIf your game must work in pixel units then you could convert your\nlength units from pixels to meters when passing values from Box2D.\nLikewise you should convert the values received from Box2D from meters\nto pixels. This will improve the stability of the physics simulation.\n\nYou have to come up with a reasonable conversion factor. I suggest\nmaking this choice based on the size of your characters. Suppose you\nhave determined to use 50 pixels per meter (because your character is 75\npixels tall). Then you can convert from pixels to meters using these\nformulas:\n\n```cpp\nxMeters = 0.02f * xPixels;\nyMeters = 0.02f * yPixels;\n```\n\nIn reverse:\n\n```cpp\nxPixels = 50.0f * xMeters;\nyPixels = 50.0f * yMeters;\n```\n\nYou should consider using MKS units in your game code and just convert\nto pixels when you render. This will simplify your game logic and reduce\nthe chance for errors since the rendering conversion can be isolated to\na small amount of code.\n\nIf you use a conversion factor, you should try tweaking it globally to\nmake sure nothing breaks. You can also try adjusting it to improve\nstability.\n\nIf this conversion is not possible, you can set the length units used\nby Box2D using `b2SetLengthUnitsPerMeter()`. This is experimental and not\nwell tested.\n\n## Debug Drawing\nYou can implement the function pointers in `b2DebugDraw` struct to get detailed\ndrawing of the Box2D world. Debug draw provides:\n- shapes\n- joints\n- broad-phase axis-aligned bounding boxes (AABBs)\n- center of mass\n- contact points\n\nThis is the preferred method of drawing the Box2D simulation, rather\nthan accessing the data directly. The reason is that much of the\nnecessary data is internal and subject to change.\n\nThe samples application draws the Box2D world using the `b2DebugDraw`.\n\n## Limitations\nBox2D uses several approximations to simulate rigid body physics\nefficiently. This brings some limitations.\n\nHere are the current limitations:\n1. Extreme mass ratios may cause joint stretching and collision overlap.\n2. Box2D uses soft constraints to improve robustness. This can lead to joint and contact flexing.\n3. Continuous collision does not handle all situations. For example, general dynamic versus dynamic continuous collision is not handled. [Bullets](#bullets) handle this in a limited way. This is done for performance reasons.\n4. Continuous collision does not handle joints. So you may see joint stretching on fast moving objects. Usually the joints recover after a few time steps.\n5. Box2D uses the [semi-implicit Euler method](https://en.wikipedia.org/wiki/Semi-implicit_Euler_method) to solve the [equations of motion](https://en.wikipedia.org/wiki/Equations_of_motion). It does not reproduce exactly the parabolic motion of projectiles and has only first-order accuracy. However it is fast and has good stability.\n6. Box2D uses the [Gauss-Seidel method](https://en.wikipedia.org/wiki/Gauss%E2%80%93Seidel_method) to solve constraints and achieve real-time performance. You will not get precisely rigid collisions or pixel perfect accuracy. Increasing the sub-step count will improve accuracy.\n"
  },
  {
    "path": "docs/migration.md",
    "content": "# Migration Guide\n\n> **Caution**:\n> This guide only covers the transition from 2.4 to 3.0. Please see the release notes for future version changes.\n\n## Version 2.4 to Version 3.0\n\nBox2D version 3.0 is a full rewrite. You can read some background information [here](https://box2d.org/posts/2023/01/starting-box2d-3.0/).\n\nHere are the highlights that affect the API:\n- moved from C++ to C\n- identifiers (handles) instead of pointers\n- multithreading support\n- fewer callbacks\n- more features (such as capsules and shape casts)\n- new sub-stepping solver (*Soft Step*)\n- gear and pulley joint removed (temporarily)\n\nHowever, the scope of what Box2D does has not changed much. It is still a 2D rigid body engine. It is just faster and more robust (hopefully). And hopefully it is easier to work with and port/wrap for other languages/platforms.\n\nI'm going to describe migration by comparing code snippets between 2.4 and 3.0. These should give you and idea of the sort of transformations you need to make to your code to migrate to v3.0. These snippets are written in C and may need some small adjustments to work with C++.\n\nI'm not going to cover all the details of v3.0 in this guide. That is the job of the manual, the doxygen reference, and the samples.\n\nThe surface area of the Box2D is smaller in v3.0 because C++ is not good at hiding details. So hopefully you find the new API easier to work with.\n\n### Should I upgrade to Version 3?\nSince the behavior changed from version 2 to version 3, I recommend to only use version 3 for new projects. Version 2 no longer receives updates, but it is already battle tested. Version 3 is good for projects that need high performance.\n\n### Creating a world\nVersion 2.4:\n```cpp\n#include \"box2d/box2d.h\"\nb2Vec2 gravity(0.0f, -10.0f);\nb2World world(gravity);\n```\nVersion 3.0:\n```c\n#include \"box2d/box2d.h\"\nb2Vec2 gravity = {0.0f, -10.0f};\nb2WorldDef worldDef = b2DefaultWorldDef();\nworldDef.gravity = gravity;\nb2WorldId worldId = b2CreateWorld(&worldDef);\n```\nThere is now a required world definition. C does not have constructors, so you need to initialize **ALL** structures that you pass to Box2D. Box2D provides an initialization helper for almost all structures. For example `b2DefaultWorldDef()` is used here to initialize `b2WorldDef`. `b2WorldDef` provides many options, but the defaults are good enough to get going.\n\nIn Version 3.0, Box2D objects are generally hidden and you only have an identifier. This keeps the API small. So when you create a world you just get a `b2WorldId` which you should treat as an atomic object, like `int` or `float`. It is small and should be passed by value.\n\nIn Version 3.0 there are also no destructors, so you must destroy the world.\n```c\nb2DestroyWorld(worldId);\nworldId = b2_nullWorldId;\n```\nThis destroys all bodies, shapes, and joints as well. This is quicker than destroying them individually. Just like pointers, it is good practice to nullify identifiers. Box2D provides null values for all identifiers and also macros such as `B2_IS_NULL` to test if an identifier is null.\n\n### Creating a body\nVersion 2.4:\n```cpp\nb2BodyDef bodyDef;\nbodyDef.type = b2_dynamicBody;\nbodyDef.position.Set(0.0f, 4.0f);\nb2Body* body = world.CreateBody(&bodyDef);\n```\nVersion 3.0:\n```c\nb2BodyDef bodyDef = b2DefaultBodyDef();\nbodyDef.type = b2_dynamicBody;\nbodyDef.position = (b2Vec2){0.0f, 4.0f};\nb2BodyId bodyId = b2CreateBody(worldId, &bodyDef);\n```\nBody creation is very similar in v3.0. In this case there is a definition initialization function `b2DefaultBodyDef()`. This can help save a bit of typing in some cases. In v3.0 I recommend getting comfortable with curly brace initialization for initializing vectors. There are no member functions in C. Notice that the body is created using a loose function and providing the `b2WorldId` as an argument. Basically what you would expect going from C++ to C.\n\nDestroying a body is also similar.\nVersion 2.4:\n```cpp\nworld.DestroyBody(body);\nbody = nullptr;\n```\nVersion 3.0:\n```c\nb2DestroyBody(bodyId);\nbodyId = b2_nullBodyId;\n```\nNotice there is a little magic here in Version 3.0. `b2BodyId` knows what world it comes from. So you do not need to provide `worldId` when destroying the body. Version 3.0 supports up to 128 worlds. This may be increased or be overridden in the future.\n\nShapes and joints are still destroyed automatically. However, `b2DestructionListener` is gone. This holds to the theme of fewer callbacks. However, you can now use \n`b2Shape_IsValid()` and `b2Joint_IsValid()`.\n\n### Creating a shape\nShape creation has been streamlined in Version 3.0. `b2Fixture` is gone. I feel like it was a confusing concept so I hope you don't miss it.\n\nVersion 2.4:\n```cpp\nb2PolygonShape box;\nbox.SetAsBox(1.0f, 1.0f);\n\nb2FixtureDef fixtureDef;\nfixtureDef.shape = &box;\nfixtureDef.density = 1.0f;\nfixtureDef.friction = 0.3f;\n\nb2Fixture* fixture = body->CreateFixture(&fixtureDef);\n```\n\nVersion 3.0:\n```c\nb2Polygon box = b2MakeBox(1.0f, 1.0f);\n\nb2ShapeDef shapeDef = b2DefaultShapeDef();\nshapeDef.density = 1.0f;\nshapeDef.friction = 0.3f;\n\nb2ShapeId shapeId = b2CreatePolygonShape(bodyId, &shapeDef, &box);\n```\n\nSo basically v2.4 shapes are no longer shapes, they are *primitives* or *geometry* with no inheritance (of course). This freed the term _shape_ to be used where _fixture_ was used before. In v3.0 the shape definition is generic and there are different functions for creating each shape type, such as `b2CreateCircleShape` or `b2CreateSegmentShape`.\n\nAgain notice the structure initialization with `b2DefaultShapeDef()`. Unfortunately we cannot have meaningful definitions with zero initialization. You must initialize your structures.\n\nAnother important change for shapes is that the default density in the shape definition is now 1 instead of 0. Static and kinematic bodies will ignore the density. You can now make an entire game without touching the density.\n\nDestroying shapes is straight forward.\n\nVersion 2.4:\n```cpp\nbody->DestroyFixture(fixture);\nfixture = nullptr;\n```\n\nVersion 3.0:\n```c\nb2DestroyShape(shapeId);\nshapeId = b2_nullShapeId;\n```\n\n### Chains\nIn Version 2.4 chains are a type of shape. In Version 3.0 they are a separate concept. This leads to significant simplifications internally. In Version 2.4 all shapes had to support the notion of child shapes. This is gone.\n\nVersion 2.4:\n```cpp\nb2Vec2 points[5];\npoints[0].Set(-8.0f, 6.0f);\npoints[1].Set(-8.0f, 20.0f);\npoints[2].Set(8.0f, 20.0f);\npoints[3].Set(8.0f, 6.0f);\npoints[4].Set(0.0f, -2.0f);\n\nb2ChainShape chain;\nchain.CreateLoop(points, 5);\nb2FixtureDef fixtureDef;\nfixtureDef.shape = &chain;\nb2Fixture* chainFixture = body->CreateFixture(&fixtureDef);\n```\n\nVersion 3.0:\n```c\nb2Vec2 points[5] = {\n    {-8.0f, 6.0f},\n    {-8.0f, 20.0f},\n    {8.0f, 20.0f},\n    {8.0f, 6.0f},\n    {0.0f, -2.0f}\n};\n\nb2ChainDef chainDef = b2DefaultChainDef();\nchainDef.points = points;\nchainDef.count = 5;\nchainDef.loop = true;\nb2ChainId chainId = b2CreateChain(bodyId, &chainDef);\n```\n\nSince chains are their own concept now, they get their own identifier, `b2ChainId`. You can view chains as macro objects, they create many `b2ChainSegment` shapes internally. Normally you don't interact with these. However they are returned from queries. You can use `b2Shape_GetParentChain()` to get the `b2ChainId` for a chain segment that you get from a query.\n\n> DO NOT destroy or modify a `b2ChainSegment` that belongs to a chain shape directly\n\n### Creating a joint\nJoints are very similar in v3.0. The lack of C member functions changes initialization.\n\nVersion 2.4:\n```cpp\nb2RevoluteJointDef jointDef;\njointDef.Initialize(ground, body, b2Vec2(-10.0f, 20.5f));\njointDef.motorSpeed = 1.0f;\njointDef.maxMotorTorque = 100.0f;\njointDef.enableMotor = true;\njointDef.lowerAngle = -0.25f * b2_pi;\njointDef.upperAngle = 0.5f * b2_pi;\njointDef.enableLimit = true;:\nb2RevolutionJoint* joint = (b2RevoluteJoint*)world->CreateJoint(&jointDef);\n```\nVersion 3.0:\n```c\nb2Vec2 pivot = {-10.0f, 20.5f};\nb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\njointDef.bodyIdA = groundId;\njointDef.bodyIdB = bodyId;\njointDef.localAnchorA = b2Body_GetLocalPoint(jointDef.bodyIdA, pivot);\njointDef.localAnchorB = b2Body_GetLocalPoint(jointDef.bodyIdB, pivot);\njointDef.motorSpeed = 1.0f;\njointDef.maxMotorTorque = 100.0f;\njointDef.enableMotor = true;\njointDef.lowerAngle = -0.25f * b2_pi;\njointDef.upperAngle = 0.5f * b2_pi;\njointDef.enableLimit = true;\nb2JointId jointId = b2CreateRevoluteJoint(worldId, &jointDef);\n```\n\nSome of the joints have more options now. Check the code comments and samples for details.\n\nThe friction joint has been removed since it is a subset of the motor joint.\n\nThe pulley and gear joints have been removed. I'm not satisfied with how they work in 2.4 and plan to implement improved versions in the future.\n\n### New solver\nThere is a new solver that uses sub-stepping called *Soft Step*. Instead of specifying velocity iterations or position iterations, you now specify the number of sub-steps.\n```c\nvoid b2World_Step(b2WorldId worldId, float timeStep, int32_t subStepCount);\n```\nIt is recommended to start with 4 sub-steps and adjust as needed. The sub-stepping only computes contact points once per full time step, so contact events are for the full time step.\n\nWith a sub-stepping solver you need to think differently about how you interact with bodies. Externally applied impulses or velocity adjustments no longer exist after the first sub-step. So if you try to control the movement of a body by setting the velocity every time step then you may get unexpected results. You will get more predictable results by applying a force and/or torque. Forces and torques are spread across all time steps.\n\nIf you want full control over the movement of a body, considering setting the body type to `b2_kinematicBody`. Preferably this is done in the `b2BodyDef`:\n```c\nb2BodyDef bodyDef = b2DefaultBodyDef();\nbodyDef.type = b2_kinematicBody;\n```\n\n### Contact data\nIn v2.4 `b2ContactListener` provided `BeginContact`, `EndContact`, `PreSolve`, and `PostSolve`. You could also iterate over the contacts associated with a body using `b2Body::GetContactList`. The latter was rarely used due to how continuous collision worked in v2.4 meant that you could miss some contacts using `GetContactList`.\n\nIn v3.0 there is a strong emphasis on multithreading. Callbacks in multithreading are problematic for a few reasons:\n  * chance of race conditions in user code\n  * user code becomes non-deterministic\n  * uncertain performance impact\n\nTherefore all callbacks except `PreSolve` have been removed. Instead you can now access all events and contact data after the time step. Version 3.0 no longer uses collision sub-stepping for continuous collision. This means all contacts data are valid at the end of the time step. Just keep in mind that Box2D computes contact points at the beginning of the time step, so the contact points apply to the previous position of the body.\n\nHere is how you access contact data in v3.0:\n```c\nb2ContactEvents contactEvents = b2World_GetContactEvents(worldId);\n```\nThe contact events structure has begin and end events:\n```c \ntypedef struct b2ContactEvents\n{\n\tb2ContactBeginTouchEvent* beginEvents;\n\tb2ContactEndTouchEvent* endEvents;\n  b2ContactHitEvent* hitEvents;\n\tint beginCount;\n\tint endCount;\n  int hitCount;\n} b2ContactEvents;\n```\nYou can loop through these events after the time step. These events are in deterministic order, even with multithreading. See the `sample_events.cpp` file for examples.\n\nYou may not want Box2D to save all contact events, so you can disable them for a given shape using `enableContactEvents` on `b2ShapeDef`.\n\nIf you want to access persistent contacts, you can get the data from bodies or shapes.\n```c\nb2ContactData contactData[10];\nint count = b2Body_GetContactData(bodyId, contactData, 10);\n```\n```c\nb2ContactData contactData[10];\nint count = b2Shape_GetContactData(shapeId, contactData, 10);\n```\nThis includes contact data for contacts reported in begin events. This data is also in deterministic order.\n\nPre-solve contact modification is available using a callback.\n```c\ntypedef bool b2PreSolveFcn(b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Manifold* manifold, void* context);\nvoid b2World_SetPreSolveCallback(b2WorldId worldId, b2PreSolveFcn* fcn, void* context);\n```\nYou can define a pre-solve callback and register that with the world. You can also provide a context variable that will be passed back to your callback. This is **not** enough to get a pre-solve callback. You also need to enable it on your shape using `enablePreSolveEvents` in `b2ShapeDef`. This is false by default.\n\n> Pre-solve callbacks are dangerous. You must avoid race conditions and you must understand that behavior may not be deterministic. This is especially true if you have multiple pre-solve callbacks that are sensitive to order.\n\n### Sensors\nIn v2.4 sensor events were mixed in with contact events. I have split them up to make user code simpler.\n```c\nb2SensorEvents sensorEvents = b2World_GetSensorEvents(b2WorldId worldId);\n```\nNote that contact data on bodies and shapes have no information about sensors. That data only has touching contacts.\n\nSensor events are available to all shapes on dynamic bodies except chains. You can disable them using `enableSensorEvents` on `b2ShapeDef`.\n\n### Queries\nVersion 2.4 has `b2World::QueryAABB` and `b2World::RayCast`. This functionality is largely the same in v3.0, but more features have been added such as precise overlap tests and shape casts.\n\nAnother new feature is `b2QueryFilter` which allows you to filter raycast results before they reach your callback.\nThis query filter is tested against `b2Filter` on shapes that the query encounters.\n\nRay casts now take an origin and translation rather than start and end points. This convention works better with the added shape cast functions.\n\n### World iteration\nIterating over all bodies/shapes/joints/contacts in a world is very inefficient and has been removed from Version 3.0. Instead, you should be using `b2BodyEvents` and `b2ContactEvents`. Events are efficient and data-oriented.\n\n### Library configuration\nVersion 3.0 offers more library configuration. You can override the allocator and you can intercept assertions by registering global callbacks. These are for expert users and they must be thread safe.\n```c\nvoid b2SetAllocator(b2AllocFcn* allocFcn, b2FreeFcn* freeFcn);\nvoid b2SetAssertFcn(b2AssertFcn* assertFcn);\n```\n"
  },
  {
    "path": "docs/overview.md",
    "content": "# Overview\nBox2D is a 2D rigid body simulation library for games. Programmers can\nuse it in their games to make objects move in realistic ways and make\nthe game world more interactive. From the game engine's point of view,\na physics engine is a system for procedural animation.\n\nBox2D also provides many collision routines that can be used even when\nrigid body simulation is not used. There are functions for overlap and\ncast queries. There is also a bounding volume hierarchy (dynamic tree)\nthat can be used for game specific spatial sorting needs.\n\nBox2D is written in portable C17. Most of the types defined in the\nengine begin with the b2 prefix. Hopefully this is sufficient to avoid\nname clashing with your application.\n\n## Prerequisites\nIn this manual I'll assume you are familiar with basic physics\nconcepts, such as mass, force, torque, and impulses. If not, please\nfirst consult Google search and Wikipedia.\n\nBox2D was created as part of a physics tutorial at the Game Developer\nConference. You can get these tutorials from the publications section of\n[box2d.org](https://box2d.org/publications/).\n\nSince Box2D is written in C, you are expected to be experienced in C\nprogramming. Box2D should not be your first C programming project. You\nshould be comfortable with compiling, linking, and debugging.\n\n> **Caution**:\n> Box2D should not be your first C project. Please learn C\n> programming, compiling, linking, and debugging before working with\n> Box2D. There are many resources for this online.\n\n## Scope\nThis manual covers the majority of the Box2D API. However, not every\naspect is covered. Please look at the Reference section and samples\napplication included with Box2D to learn more.\n\nThis manual is only updated with new releases. The latest version of\nBox2D may be out of sync with this manual.\n\n> **Caution**:\n> This manual applies to the associated release and not necessarily the\n> latest version on the main branch.\n\n## Feedback and Bugs\nPlease file bugs and feature requests here:\n[Box2D Issues](https://github.com/erincatto/box2d/issues)\n\nYou can help to ensure your issue gets fixed if you provide sufficient\ndetail. A testbed example that reproduces the problem is ideal. You can\nread about the testbed later in this document.\n\nThere is also a [Discord server](https://discord.gg/NKYgCBP) and a\n[subreddit](https://reddit.com/r/box2d) for Box2D. You may also use\n[GitHub Discussions](https://github.com/erincatto/box2d/discussions).\n\n## Core Concepts\nBox2D works with several fundamental concepts and objects. I briefly\ndefine these objects here and more details are given later in this\ndocument.\n\n### rigid body\nA chunk of matter that is so strong that the distance between any two\nbits of matter on the chunk is constant. They are hard like a diamond.\nIn the following discussion I use *body* interchangeably with rigid body.\n\n### shape\nA shape binds collision geometry to a body and adds material properties such as\ndensity, friction, and restitution. A shape puts collision geometry into the\ncollision system (broad-phase) so that it can collide with other shapes.\n\n### constraint\nA constraint is a physical connection that removes degrees of freedom\nfrom bodies. A 2D body has 3 degrees of freedom (two translation\ncoordinates and one rotation coordinate). If I take a body and pin it\nto the wall (like a pendulum) I have constrained the body to the wall.\nAt this point the body can only rotate about the pin, so the constraint\nhas removed 2 degrees of freedom.\n\n### contact constraint\nA special constraint designed to prevent penetration of rigid bodies and\nto simulate friction and restitution. You do not create contact\nconstraints; they are created automatically by Box2D.\n\n### joint constraint\nThis is a constraint used to hold two or more bodies together. Box2D\nsupports several joint types: revolute, prismatic, distance, and more.\nJoints may have limits, motors, and/or springs.\n\n### joint limit\nA joint limit restricts the range of motion of a joint. For example, the\nhuman elbow only allows a certain range of angles.\n\n### joint motor\nA joint motor drives the motion of the connected bodies according to the\njoint's degrees of freedom. For example, you can use a motor to drive\nthe rotation of an elbow. Motors have a target speed and a maximum force\nor torque. The simulation will apply the force or torque required to\nachieve the desired speed.\n\n### joint spring\nA joint spring has a stiffness and damping. In Box2D spring stiffness is\nexpressed in terms or Hertz or cycles per second. This lets you configure how\nquickly a spring reacts regardless of the body masses. Joint springs also\nhave a damping ratio to let you specify how quickly the spring will come to\nrest.\n\n### world\nA physics world is a collection of bodies, shapes, joints, and contacts\nthat interact together. Box2D supports the creation of multiple worlds which\nare completely independent.\n\n### solver\nThe physics world has a solver that is used to advance time and to\nresolve contact and joint constraints. The Box2D solver is a high\nperformance sequential solver that operates in order N time, where N is\nthe number of constraints.\n\n### continuous collision\nThe solver advances bodies in time using discrete time steps. Without\nintervention this can lead to tunneling.\n![Tunneling Effect](images/tunneling1.svg)\n\nBox2D contains specialized algorithms to deal with tunneling. First, the\ncollision algorithms can interpolate the motion of two bodies to find\nthe first time of impact (TOI). Second, speculative collision is used to create\ncontact constraints between bodies before they touch.\n\n### events\nWorld simulation leads to the creation of events that are available at the end\nof the time step:\n\n- body movement events\n- contact begin and end events\n- sensor begin and end events\n- contact hit events\n\nThese events allow your application to react to changes in the simulation.\n\n## Modules\nBox2D's primary purpose is to provide rigid body simulation. However,\nthere are math and collision features that may be useful apart from the\nrigid body simulation. These are provided in the `include` directory. Anything\nin the `include` directory is considered public, while everything in the `src`\ndirectory is consider internal.\n\nPublic features are supported and you can get help with these on the Discord\nserver. Using internal code directly is not supported. However, feel free to\nstudy the code and ask questions. I'm happy to share all the details of how\nBox2D works internally.\n\n## Units\nBox2D works with floating point numbers and tolerances have to be used\nto make Box2D perform well. These tolerances have been tuned to work\nwell with meters-kilogram-second (MKS) units. In particular, Box2D has\nbeen tuned to work well with moving shapes between 0.1 and 10 meters. So\nthis means objects between soup cans and buses in size should work well.\n\nStatic shapes may be up to 50 meters long without trouble. If you have a\nlarge world, you should split it up into multiple static bodies. This will\nimprove precision and simulation behavior.\n\nBeing a 2D physics engine, it is tempting to use pixels as your units.\nUnfortunately this will lead to a poor simulation and possibly weird\nbehavior. An object of length 200 pixels would be seen by Box2D as the\nsize of a 45 story building.\n\n> **Caution**: \n> Box2D is tuned for MKS units. Keep the size of moving objects larger than 1cm.\n> You'll need to use some scaling system when\n> you render your environment and actors. The Box2D samples application\n> does this by using an OpenGL viewport transform. Do not use pixel units\n> unless you understand the implications.\n\nIt is best to think of Box2D bodies as moving billboards upon which you\nattach your artwork. The billboard may move in a unit system of meters,\nbut you can convert that to pixel coordinates with a simple scaling\nfactor. You can then use those pixel coordinates to place your sprites,\netc. You can also account for flipped coordinate axes.\n\nAnother limitation to consider is overall world size. If your world units\nbecome larger than 12 kilometers or so, then the lost precision can affect\nstability.\n\n> **Caution**: \n> Box2D works best with world sizes less than 12 kilometers. If you are\n> careful with your simulation tuning, this can be pushed up to around 24\n> kilometers, which is much larger than most game worlds.\n\nBox2D uses radians for angles. The body rotation is stored a complex number,\nso when you access the angle of a body, it will be between \\f$-\\pi\\f$ and \\f$\\pi\\f$ radians.\n\n> **Caution**:\n> Box2D uses radians, not degrees.\n\n## Changing the length units\nAdvanced users may change the length unit by calling `b2SetLengthUnitsPerMeter()`\nat application startup. If you keep Box2D in a shared library, you will need\nto call this if the shared library is reloaded.\n\nIf you change the length units to pixels you will need to decide how many pixels\nrepresent a meter. You will also need to figure out reasonable values for gravity,\ndensity, force, and torque. One of the benefits of using MKS units for physics\nsimulation is that you can use real world values to get reasonable results.\n\nIt is also harder to get support for using Box2D if you change the unit\nsystem, because values are harder to communicate and may become non-intuitive.\n\n## Ids and Definitions\nFast memory management plays a central role in the design of the Box2D\ninterface. When you create a world, body, shape or joint, you will receive\na handle called an *id*. These ids are opaque and are passed to various functions\nto access the underlying data.\n\nThese ids provide some safety. If you use an id after it has been freed you will\nusually get an assertion. All ids support 64k generations of safety. All ids\nalso have a corresponding function you can call to check if it is valid.\n\nWhen you create a world, body, shape, or joint, you need to provide a definition structure.\nThese definitions contain all the information needed to build the Box2D object. By using\nthis approach I can prevent construction errors, keep the number of function parameters\nsmall, provide sensible defaults, and reduce the number of accessors.\n\nHere is an example of body creation:\n\n```c\nb2BodyDef bodyDef = b2DefaultBodyDef();\nbodyDef.position = (b2Vec2){10.0f, 5.0f};\nb2BodyId myBodyId = b2CreateBody(myWorldId, &bodyDef);\n```\n\nNotice the body definition is initialized by calling `b2DefaultBodyDef()`. This is needed\nbecause C does not have constructors and zero initialization is generally not suitable for the definitions used in Box2D.\n\nAlso notice that the body definition is a temporary object that is fully copied into the internal\nbody data structures. Definitions should usually be created on the stack as temporaries.\n\nThis is how a body is destroyed:\n\n```c\nb2DestroyBody(myBodyId);\nmyBodyId = b2_nullBodyId;\n```\n\nNotice that the body id is set to null using the constant `b2_nullBodyId`. You should treat\nids as opaque data, however you may zero initialize all Box2D ids and they will be considered\n*null*.\n\nShapes are created in a similar way. For example, here is how a box shape is created:\n\n```c\nb2ShapeDef shapeDef = b2DefaultShapeDef();\nshapeDef.material.friction = 0.42f;\nb2Polygon box = b2MakeBox(0.5f, 0.25f);\nb2ShapeId myShapeId = b2CreatePolygonShape(myBodyId, &shapeDef, &box);\n```\n\nAnd the shape may be destroyed as follows:\n\n```c\nb2DestroyShape(myShapeId);\nmyShapeId = b2_nullShapeId;\n```\n\nFor convenience, Box2D will destroy all shapes on a body when the body is destroyed. You don't need to store the shape id.\n\nThere are some macros to assist using ids in logical operations.\n\n```c\nbool isNull = B2_IS_NULL(myBodyId);\nbool isNonNull = B2_IS_NON_NULL(myJointId);\nbool areEqual = B2_ID_EQUALS(myShapeIdA, myShapeIdB);\n```\n"
  },
  {
    "path": "docs/reading.md",
    "content": "# Further Reading\n- [Erin Catto's Publications](https://box2d.org/publications/)\n- [Erin Catto's Blog Posts](https://box2d.org/posts/)\n- Collision Detection in Interactive 3D Environments, Gino van den Bergen, 2004\n- Real-Time Collision Detection, Christer Ericson, 2005\n"
  },
  {
    "path": "docs/release_notes_v310.md",
    "content": "# v3.1 Release Notes\n\n## API Changes\n- 64-bit filter categories and masks\n- 64-bit dynamic tree user data\n- Renamed `b2SmoothSegment` to `b2ChainSegment`\n- Cast and overlap functions modified for argument consistency\n- Contact begin events now provide the manifold\n- More consistent functions to make polygons\n- Contact events are now disabled by default\n- Replaced `b2Timer` with `uint64_t`\n- Shape material properties now use `b2SurfaceMaterial`\n\n## New Features\n- New character mover features and sample\n- Revised sensor system is now independent of body type and sleep\n- Rolling resistance and tangent speed\n- Friction and restitution mixing callbacks\n- World explosions\n- World access to the maximum linear speed\n- More control over body mass updates\n- Filter joint to disable collision between specific bodies\n- Bodies can now have names for debugging\n- Added `b2Body_SetTargetTransform` for kinematic bodies\n\n## Improvements\n- Cross-platform determinism\n- Custom SSE2 and Neon for significantly improved performance\n- SSE2 is the default instead of AVX2\n- Removed SIMDE library dependency\n- Faster ray and shape casts\n- Faster continuous collision\n- Each segment of a chain shape may have a different surface material\n- Reduced overhead of restitution when not used\n- Implemented atomic platform wrappers eliminating the `experimental:c11atomics` flag\n\n## Bugs Fixes\n- Many bug fixes based on user testing\n- Fixed missing hit events\n- Capsule and polygon manifold fixes\n- Fixed missing contact end events\n- PreSolve is now called in continuous collision\n- Reduced clipping into chain shapes by fast bodies\n- Friction and restitution are now remixed in the contact solver every time step\n- Body move events are now correctly adjusted for time of impact\n\n## Infrastructure\n- Unit test for API coverage\n- macOS and Windows samples built in GitHub actions\n- CMake install\n- imgui and glfw versions are now pinned in FetchContent\n- Initial Emscripten support\n"
  },
  {
    "path": "docs/samples.md",
    "content": "# Samples {#samples}\nOnce you have conquered the HelloWorld example, you should start looking\nat Box2D's samples application. The samples application is a testing framework and demo\nenvironment. Here are some of the features:\n- Camera with pan and zoom\n- Mouse dragging of dynamic bodies\n- Many samples in a tree view\n- GUI for selecting samples, parameter tuning, and debug drawing options\n- Pause and single step simulation\n- Multithreading and performance data\n\n![Box2D Samples](images/samples.png)\n\nThe samples application has many examples of Box2D usage in the test cases and the\nframework itself. I encourage you to explore and tinker with the samples\nas you learn Box2D.\n\nNote: the sample application is written using [GLFW](https://www.glfw.org),\n[imgui](https://github.com/ocornut/imgui), and [enkiTS](https://github.com/dougbinks/enkiTS).\nThe samples app is not part of the Box2D library. The Box2D library is agnostic about rendering.\nAs shown by the HelloWorld example, you don't need a renderer to use Box2D.\n"
  },
  {
    "path": "docs/simulation.md",
    "content": "# Simulation\nRigid body simulation is the primary feature of Box2D. It is the most complex part of\nBox2D and is the part you will likely interact with the most. Simulation sits on top of\nthe foundation and collision layers, so you should be somewhat familiar with those by now.\n\nRigid body simulation contains:\n- worlds\n- bodies\n- shapes\n- contacts\n- joints\n- events\n\nThere are many dependencies between these objects so it is difficult to\ndescribe one without referring to another. In the following, you\nmay see some references to objects that have not been described yet.\nTherefore, you may want to quickly skim this section before reading it\nclosely.\n\n## Ids\nBox2D has a C interface. Typically in a C/C++ library when you create an object with a long lifetime\nyou will keep a pointer (or smart pointer) to the object.\n\nBox2D works differently. Instead of pointers, you are given an *id* when you create an object.\nThis *id* acts as a [handle](https://en.wikipedia.org/wiki/Handle_(computing)) which helps avoid\nproblems with [dangling pointers](https://en.wikipedia.org/wiki/Dangling_pointer).\n\nThis also allows Box2D to use [data-oriented design](https://en.wikipedia.org/wiki/Data-oriented_design) internally.\nThis helps to reduce cache misses drastically and also allows for [SIMD](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) optimizations.\n\nSo you will be dealing with `b2WorldId`, `b2BodyId`, etc. These are small opaque structures that you\nwill pass around by value, just like pointers. Box2D creation functions return an id. Functions\nthat operate on Box2D objects take ids.\n\n```c\nb2BodyId myBodyId = b2CreateBody(myWorldId, &myBodyDef);\n```\n\nThere are functions to check if an id is valid. Box2D functions will assert if you use an invalid id.\nThis makes debugging easier than using dangling pointers.\n\n```c\nif (b2Body_IsValid(myBodyId) == false)\n{\n    // oops\n}\n```\n\nNull ids can be established in a couple ways. You can use predefined constants or zero initialization.\n```c\nb2BodyId myNullBodyId = b2_nullBodyId;\nb2BodyId otherNullBodyId = {0};\n```\n\nYou can test if an id is null using some helper macros:\n```c\nif (B2_IS_NULL(myBodyId))\n{\n    // do something\n}\n```\n\n```c\nif (B2_IS_NON_NULL(myShapeId))\n{\n    // do something\n}\n```\n\n## World\nThe Box2D world contains the bodies and joints. It manages all aspects\nof the simulation and allows for asynchronous queries (like AABB queries\nand ray-casts). Much of your interactions with Box2D will be with a\nworld object, using `b2WorldId`.\n\n### World Definition\nWorlds are created using a *definition* structure. This is temporary structure that\nyou can use to configure options for world creation. You **must** initialize the world definition\nusing `b2DefaultWorldDef()`.\n\n```c\nb2WorldDef worldDef = b2DefaultWorldDef();\n```\n\nThe world definition has lots of options, but for most you will use the defaults. You may want to set the gravity:\n\n```c\nworldDef.gravity = (b2Vec2){0.0f, -10.0f};\n```\n\nIf your game doesn't need sleep, you can get a performance boost by completely disabling sleep:\n\n```c\nworldDef.enableSleep = false;\n```\n\nYou can also configure multithreading to improve performance:\n\n```c\nworldDef.workerCount = 4;\nworldDef.enqueueTask = myAddTaskFunction;\nworldDef.finishTask = myFinishTaskFunction;\nworldDef.userTaskContext = &myTaskSystem;\n```\n\nMultithreading is not required but it can improve performance substantially. Read more [here](#multi).\n\n### World Lifetime\nCreating a world is done using a world definition.\n\n```c\nb2WorldId myWorldId = b2CreateWorld(&worldDef);\n\n// ... do stuff ...\n\nb2DestroyWorld(myWorldId);\n\n// Nullify id for safety\nmyWorldId = b2_nullWorldId;\n```\n\nYou can create up to 128 worlds. These worlds do not interact and may be simulated in parallel.\n\nWhen you destroy a world, every body, shape, and joint is also destroyed. This is much faster\nthan destroying individual objects.\n\n### Simulation\nThe world is used to drive the simulation. You specify a time step\nand a sub-step count. For example:\n\n```c\nfloat timeStep = 1.0f / 60.f;\nint32_t subSteps = 4;\nb2World_Step(myWorldId, timeStep, subSteps);\n```\n\nAfter the time step you can examine your bodies and joints for\ninformation. Most likely you will grab the position off the bodies so\nthat you can update your game objects and render them. Or more optimally, you\nwill use `b2World_GetBodyEvents()`.\n\nYou can perform the time step anywhere in your game loop, but you should be aware of the\norder of things. For example, you must create bodies before the time\nstep if you want to get collision results for the new bodies in that\nframe.\n\nAs I discussed in the [HelloWorld tutorial](#hello), you should use a fixed\ntime step. By using a larger time step you can improve performance in\nlow frame rate scenarios. But generally you should use a time steps 1/30 seconds (30Hz) or smaller.\nA time step of 1/60 seconds (60Hz) will usually deliver a high quality simulation.\n\nThe sub-step count is used to increase accuracy. By sub-stepping the solver\ndivides up time into small increments and the bodies move by a small amount.\nThis allows joints and contacts to respond with finer detail. The recommended\nsub-step count is 4. However, increasing the sub-step count may improve \naccuracy. For example, long joint chains will stretch less with more sub-steps.\n\nThe scissor lift sample shown [here](#samples) works better with more sub-steps\nand is configured to use 8 sub-steps. With a primary time step of 1/60 seconds,\nthe scissor lift is taking sub-steps at 480Hz!\n\n## Rigid Bodies\nRigid bodies, or just *bodies* have position and velocity. You can apply forces, torques,\nand impulses to bodies. Bodies can be static, kinematic, or dynamic. Here\nare the body type definitions:\n\n### Body types\n#b2_staticBody:\nA static body does not move under simulation and behaves as if it has infinite mass.\nInternally, Box2D stores zero for the mass and the inverse mass. A static body has zero\nvelocity. Static bodies do not collide with other static or kinematic bodies.\n\n#b2_kinematicBody:\nA kinematic body moves under simulation according to its velocity.\nKinematic bodies do not respond to forces. A kinematic body is moved by setting its\nvelocity. A kinematic body behaves as if it has infinite mass, however,\nBox2D stores zero for the mass and the inverse mass. Kinematic bodies do\nnot collide with other kinematic or static bodies. Generally you should use\na kinematic body if you want a shape to be animated and not affected by\nforces or collisions.\n\n#b2_dynamicBody:\nA dynamic body is fully simulated and moves according to forces and torques.\nA dynamic body can collide with all body types. A dynamic body always has\nfinite, non-zero mass.\n\n> **Caution**:\n> Generally you should not set the transform on bodies after creation.\n> Box2D treats this as a teleport and may result in undesirable behavior and/or performance problems.\n\nBodies carry shapes and moves them around in the world. Bodies are always\nrigid bodies in Box2D. That means that two shapes attached to the same rigid body never move\nrelative to each other and shapes attached to the same body don't collide.\n\nShapes have collision geometry and density. Normally, bodies acquire\ntheir mass properties from the shapes. However, you can override the\nmass properties after a body is constructed.\n\nYou usually keep ids to all the bodies you create. This way you can\nquery the body positions to update the positions of your graphical\nentities. You should also keep body ids so you can destroy them\nwhen you are done with them.\n\n### Body Definition\nBefore a body is created you must create a body definition (`b2BodyDef`).\nThe body definition holds the data needed to create and initialize a\nbody correctly.\n\nBecause Box2D uses a C API, a function is provided to create a default\nbody definition.\n\n```c\nb2BodyDef myBodyDef = b2DefaultBodyDef();\n```\n\nThis ensures the body definition is valid and this initialization is **mandatory**.\n\nBox2D copies the data out of the body definition; it does not keep a\npointer to the body definition. This means you can recycle a body\ndefinition to create multiple bodies.\n\nLet's go over some of the key members of the body definition.\n\n### Body Type\nAs discussed previously, there are three different\nbody types: static, kinematic, and dynamic. b2_staticBody is the default.\nYou should establish the body type at creation because changing the body type\nlater is expensive.\n\n```c\nb2BodyDef bodyDef;\nbodyDef.type = b2_dynamicBody;\n```\n\n### Position and Angle\nYou can initialize the body position and angle in the body definition. This has far\nbetter performance than creating the body at the world origin and then moving the body.\n\n> **Caution**:\n> Do not create a body at the origin and then move it. If you create\n> several bodies at the origin, then performance will suffer.\n\nA body has two main points of interest. The first point is the body's\norigin. Shapes and joints are attached relative to the body's origin.\nThe second point of interest is the center of mass. The center of mass\nis determined from the mass distribution of the attached shapes or is\nexplicitly set using `b2MassData`. Much of Box2D's internal computations\nuse the center of mass position. For example the body stores the linear\nvelocity for the center of mass, not the body origin.\n\n![Body Origin and Center of Mass](images/center_of_mass.svg)\n\nWhen you are building the body definition, you may not know where the\ncenter of mass is located. Therefore you specify the position of the\nbody's origin. You may also specify the body's angle in radians. If you later\nchange the mass properties of the body, then the center of mass may move\non the body, but the origin position and body angle does not change and the attached\nshapes and joints do not move.\n\n```c\nb2BodyDef bodyDef = b2DefaultBodyDef();\nbodyDef.position = (b2Vec2){0.0f, 2.0f};\nbodyDef.angle = 0.25f * b2_pi;\n```\n\nA rigid body is a frame of reference. You can define shapes and\njoints in that frame. Those shapes and joint anchors never move in the\nlocal frame of the body.\n\n### Damping\nDamping is used to reduce the world velocity of bodies. Damping is\ndifferent than friction because friction only occurs with contact.\nDamping is not a replacement for friction and the two effects are\nused together.\n\nDamping parameter are non-negative. Normally you will use a\ndamping value between 0 and 1. I generally do not use linear damping\nbecause it makes bodies look like they are floating.\n\n```c\nbodyDef.linearDamping = 0.0f;\nbodyDef.angularDamping = 0.1f;\n```\n\nDamping is approximated to improve performance. At small damping\nvalues the damping effect is mostly independent of the time step. At\nlarger damping values, the damping effect will vary with the time step.\nThis is not an issue if you use a fixed time step (recommended).\n\nHere's some math for the curious. A first-order differential equation for velocity damping is:\n\n\\f[\n\\frac{dv}{dt} + c v = 0\n\\f]\n\nThe solution with initial velocity \\f$v_0\\f$ is\n\\f[\nv = v_0 e^{-c t}\n\\f]\n\nAcross a single time step \\f$h\\f$ the velocity evolves like so\n\\f[\nv(t + h) = v_0 e^{-c (t + h)} = v_0 e^{-c t} e^{-c h} = v(t) e^{-c h}\n\\f]\n\nUsing the [Pade approximation](https://en.wikipedia.org/wiki/Pad%C3%A9_table) for the\nexponential function gives the update formula:\n\\f[\nv(t + h) \\approx \\frac{1}{1 + c h} v(t)\n\\f]\n\nThis is the formula used in the Box2D solver.\n\n### Gravity Scale\nYou can use the gravity scale to adjust the gravity on a single body. Be\ncareful though, a large gravity magnitude can decrease stability.\n\n```c\n// Set the gravity scale to zero so this body will float\nbodyDef.gravityScale = 0.0f;\n```\n\n### Sleep Parameters\nWhat does sleep mean? Well it is expensive to simulate bodies, so the\nless we have to simulate the better. When a body comes to rest we would\nlike to stop simulating it.\n\nWhen Box2D determines that a body (or group of bodies) has come to rest,\nthe body enters a sleep state which has very little CPU overhead. If a\nbody is awake and collides with a sleeping body, then the sleeping body\nwakes up. Bodies will also wake up if a joint or contact attached to\nthem is destroyed. You can also wake a body manually.\n\nThe body definition lets you specify whether a body can sleep and\nwhether a body is created sleeping.\n\n```c\nbodyDef.enableSleep = true;\nbodyDef.isAwake = true;\n```\n\nThe `isAwake` flag is ignored if `enableSleep` is false.\n\n### Fixed Rotation\nYou may want a rigid body, such as a character, to have a fixed\nrotation. Such a body does not rotate, even under load. You can use\nthe fixed rotation setting to achieve this:\n\n```c\nbodyDef.fixedRotation = true;\n```\n\nThe fixed rotation flag causes the rotational inertia and its inverse to\nbe set to zero.\n\n### Bullets {#bullets}\nGame simulation usually generates a sequence of transforms that are played\nat some frame rate. This is called discrete simulation. In discrete\nsimulation, rigid bodies can move by a large amount in one time step. If\na physics engine doesn't account for the large motion, you may see some\nobjects incorrectly pass through each other. This effect is called\n*tunneling*.\n\nBy default, Box2D uses continuous collision detection (CCD) to prevent\ndynamic bodies from tunneling through static bodies. This is done by\nsweeping shapes from their old position to their new positions. The\nengine looks for new collisions during the sweep and computes the time\nof impact (TOI) for these collisions. Bodies are moved to their first\nTOI at the end of the time step.\n\nNormally CCD is not used between dynamic bodies. This is done to keep\nperformance reasonable. In some game scenarios you need dynamic bodies\nto use CCD. For example, you may want to shoot a high speed bullet at a\nstack of dynamic bricks. Without CCD, the bullet might tunnel through\nthe bricks.\n\nFast moving objects in Box2D can be configured as *bullets*. Bullets will\nperform CCD with all body types, but **not** other bullets. You should decide what\nbodies should be bullets based on your game design. If you decide a body\nshould be treated as a bullet, use the following setting.\n\n```c\nbodyDef.isBullet = true;\n```\n\nThe bullet flag only affects dynamic bodies. I recommend using bullets sparingly.\n\n### Disabling\nYou may wish a body to be created but not participate in collision or\nsimulation. This state is similar to sleeping except the body will not be\nwoken by other bodies and the body's shapes will not collide with anything.\nThis means the body will not participate in collisions, ray\ncasts, etc.\n\nYou can create a body as disabled and later enable it.\n\n```c\nbodyDef.isEnabled = false;\n\n// Later ...\nb2Body_Enable(myBodyId);\n```\n\nJoints may be connected to disabled bodies. These joints will not be\nsimulated. You should be careful when you enable a body that its\njoints are not distorted.\n\nNote that enabling a body is almost as expensive as creating the body\nfrom scratch. So you should not use body disabling for streaming worlds. Instead, use\ncreation/destruction for streaming worlds to save memory.\n\nBody disabling is a convenience and is generally not good for performance.\n\n### User Data\nUser data is a void pointer. This gives you a hook to link your\napplication objects to bodies. You should be consistent to use the same\nobject type for all body user data.\n\n```c\nbodyDef.userData = &myGameObject;\n```\n\nThis is useful when you receive results from a query such as a ray-cast\nor event and you want to get back to your game object. You can acquire the\nuse data from a body using `b2Body_GetUserData()`.\n\n### Body Lifetime\nBodies are created and destroyed using a world id. This lets the world create\nthe body with an efficient allocator and add the body to the world data structure.\n\n```c\nb2BodyId myBodyId = b2CreateBody(myWorldId, &bodyDef);\n\n// ... do stuff ...\n\nb2DestroyBody(myBodyId);\n\n// Nullify body id for safety\nmyBodyId = b2_nullBodyId;\n```\n\nBox2D does not keep a reference to the body definition or any of the\ndata it holds (except user data pointers). So you can create temporary\nbody definitions and reuse the same body definitions.\n\nBox2D allows you to avoid destroying bodies by destroying the world\ndirectly using `b2DestroyWorld()`, which does all the cleanup work for you.\nHowever, you should be mindful to nullify body ids that you keep in your application.\n\nWhen you destroy a body, the attached shapes and joints are\nautomatically destroyed. This has important implications for how you\nmanage shape and joint ids. You should nullify these ids after destroying\na body.\n\n### Using a Body\nAfter creating a body, there are many operations you can perform on the\nbody. These include setting mass properties, accessing position and\nvelocity, applying forces, and transforming points and vectors.\n\n### Mass Data\nA body has mass (scalar), center of mass (2-vector), and rotational\ninertia (scalar). For static bodies, the mass and rotational inertia are\nset to zero. When a body has fixed rotation, its rotational inertia is\nzero.\n\nNormally the mass properties of a body are established automatically\nwhen shapes are added to the body. You can also adjust the mass of a\nbody at run-time. This is usually done when you have special game\nscenarios that require altering the mass.\n\n```c\nb2MassData myMassData;\nmyMassData.mass = 10.0f;\nmyMassData.center = (b2Vec2){0.0f, 0.0f};\nmyMassData.rotationalInertia = 100.0f;\nb2Body_SetMassData(myBodyId, myMassData);\n```\n\nAfter setting a body's mass directly, you may wish to revert to the \nmass determined by the shapes. You can do this with:\n\n```c\nb2Body_ApplyMassFromShapes(myBodyId);\n```\n\nThe body's mass data is available through the following functions:\n\n```c\nfloat mass = b2Body_GetMass(myBodyId);\nfloat inertia = b2Body_GetRotationalInertia(myBodyId);\nb2Vec2 localCenter b2Body_GetLocalCenterOfMass(myBodyId);\nb2MassData massData = b2Body_GetMassData(myBodyId);\n```\n\n### State Information\nThere are many aspects to the body's state. You can access this state\ndata through the following functions:\n\n```c\nb2Body_SetType(myBodyId, b2_kinematicBody);\nb2BodyType bodyType = b2Body_GetType(myBodyId);\nb2Body_SetBullet(myBodyId, true);\nbool isBullet = b2Body_IsBullet(myBodyId);\nb2Body_EnableSleep(myBodyId, false);\nbool isSleepEnabled = b2Body_IsSleepingEnabled(myBodyId);\nb2Body_SetAwake(myBodyId, true);\nbool isAwake = b2Body_IsAwake(myBodyId);\nb2Body_Disable(myBodyId);\nb2Body_Enable(myBodyId);\nbool isEnabled = b2Body_IsEnabled(myBodyId);\nb2Body_SetFixedRotation(myBodyId, true);\nbool isFixedRotation = b2Body_IsFixedRotation(myBodyId);\n```\n\nPlease see the comments on these functions for more details.\n\n### Position and Velocity\nYou can access the position and rotation of a body. This is common when\nrendering your associated game object. You can also set the position and angle,\nalthough this is less common since you will normally use Box2D to\nsimulate movement.\n\nKeep in mind that the Box2D interface uses *radians*.\n\n```c\nb2Body_SetTransform(myBodyId, position, rotation);\nb2Transform transform = b2Body_GetTransform(myBodyId);\nb2Vec2 position = b2Body_GetPosition(myBodyId);\nb2Rot rotation = b2Body_GetRotation(myBodyId);\nfloat angleInRadians = b2Rot_GetAngle(rotation);\n```\n\nYou can access the center of mass position in local and world\ncoordinates. Much of the internal simulation in Box2D uses the center of\nmass. However, you should normally not need to access it. Instead you\nwill usually work with the body transform. For example, you may have a\nbody that is square. The body origin might be a corner of the square,\nwhile the center of mass is located at the center of the square.\n\n```c\nb2Vec2 worldCenter = b2Body_GetWorldCenterOfMass(myBodyId);\nb2Vec2 localCenter = b2Body_GetLocalCenterOfMass(myBodyId);\n```\n\nYou can access the linear and angular velocity. The linear velocity is\nfor the center of mass. Therefore, the linear velocity may change if the\nmass properties change. Since Box2D uses radians, the angular velocity is\nin radians per second.\n\n```c\nb2Vec2 linearVelocity = b2Body_GetLinearVelocity(myBodyId);\nfloat angularVelocity = b2Body_GetAngularVelocity(myBodyId);\n```\n\nYou can drive a body to a specific transform. This is useful for kinematic bodies.\n\n```c\nb2Vec2 targetPosition = {42.0f, -100.0f};\nb2Rot targetRotation = b2MakeRot(B2_PI);\nb2Transform target = {targetPosition, targetRotation};\nfloat timeStep = 1.0f / 60.0f;\nb2Body_SetTargetTransform(myBodyId, target, timeStep);\n```\n\n### Forces and Impulses\nYou can apply forces, torques, and impulses to a body. When you apply a\nforce or an impulse, you can provide a world point where the load is\napplied. This often results in a torque about the center of mass.\n\n```c\nb2Body_ApplyForce(myBodyId, force, worldPoint, wake);\nb2Body_ApplyTorque(myBodyId, torque, wake);\nb2Body_ApplyLinearImpulse(myBodyId, linearImpulse, worldPoint, wake);\nb2Body_ApplyAngularImpulse(myBodyId, angularImpulse, wake);\n```\n\nApplying a force, torque, or impulse optionally wakes the body. If you don't\nwake the body and it is asleep, then the force or impulse will be ignored.\n\nYou can also apply a force and linear impulse to the center of mass to avoid rotation.\n\n```c\nb2Body_ApplyForceToCenter(myBodyId, force, wake);\nb2Body_ApplyLinearImpulseToCenter(myBodyId, linearImpulse, wake);\n```\n\n> **Caution**:\n> Since Box2D uses sub-stepping, you should not apply a steady impulse\n> for several frames. Instead you should apply a force which Box2D will\n> spread out evenly across the sub-steps, resulting in smoother movement.\n\n### Coordinate Transformations\nThe body has some utility functions to help you transform points\nand vectors between local and world space. If you don't understand\nthese concepts, I recommend reading \\\"Essential Mathematics for Games and\nInteractive Applications\\\" by Jim Van Verth and Lars Bishop.\n\n```c\nb2Vec2 worldPoint = b2Body_GetWorldPoint(myBodyId, localPoint);\nb2Vec2 worldVector = b2Body_GetWorldVector(myBodyId, localVector);\nb2Vec2 localPoint = b2Body_GetLocalPoint(myBodyId, worldPoint);\nb2Vec2 localVector = b2Body_GetLocalVector(myBodyId, worldVector);\n```\n\n### Accessing Shapes and Joints\nYou can access the shapes on a body. You can get the number of shapes first.\n\n```c\nint shapeCount = b2Body_GetShapeCount(myBodyId);\n```\n\nIf you have bodies with many shapes, you can allocate an array or if you\nknow the number is limited you can use a fixed size array.\n\n```c\nb2ShapeId shapeIds[10];\nint returnCount = b2Body_GetShapes(myBodyId, shapeIds, 10);\n\nfor (int i = 0; i < returnCount; ++i)\n{\n    b2ShapeId shapeId = shapeIds[i];\n\n    // do something with shapeId\n}\n```\n\nYou can similarly get an array of the joints on a body.\n\n### Body Events\nWhile you can gather transforms from all your bodies after every time step, this is inefficient.\nMany bodies may not have moved because they are sleeping. Also iterating across many bodies\nwill have lots of cache misses.\n\nBox2D provides `b2BodyEvents` that you can access after every call to `b2World_Step()` to get\nan array of body movement events. Since this data is contiguous, it is cache friendly.\n\n```c\nb2BodyEvents events = b2World_GetBodyEvents(m_worldId);\nfor (int i = 0; i < events.moveCount; ++i)\n{\n    const b2BodyMoveEvent* event = events.moveEvents + i;\n    MyGameObject* gameObject = event->userData;\n    MoveGameObject(gameObject, event->transform);\n    if (event->fellAsleep)\n    {\n        SleepGameObject(gameObject);\n    }\n}\n```\n\nThe body event also indicates if the body fell asleep this time step. This might be useful to\noptimize your application.\n\n## Shapes\nA body may have zero or more shapes. A body with multiple shapes is sometimes\ncalled a *compound body.*\n\nShapes hold the following:\n- a shape primitive\n- density, friction, and restitution\n- collision filtering flags\n- parent body id\n- user data\n- sensor flag\n\nThese are described in the following sections.\n\n### Shape Lifetime\nShapes are created by initializing a shape definition and a shape primitive.\nThese are passed to a creation function specific to each shape type.\n\n```c\nb2ShapeDef shapeDef = b2DefaultShapeDef();\nshapeDef.density = 10.0f;\nshapeDef.material.friction = 0.7f;\n\nb2Polygon box = b2MakeBox(0.5f, 1.0f);\nb2ShapeId myShapeId = b2CreatePolygonShape(myBodyId, &shapeDef, &box);\n```\n\nThis creates a polygon and attaches it to the body. You do not need to\nstore the shape id since the shape will automatically be\ndestroyed when the parent body is destroyed. However, you may wish to store the shape id if you plan\nto change properties on it later.\n\nYou can create multiple shapes on a single body. They all can contribute\nto the mass of the body. These shapes never collide with each other and may overlap.\n\nYou can destroy a shape on the parent body. You may do this to model a\nbreakable object. Otherwise you can just leave the shape alone and let\nthe body destruction take care of destroying the attached shapes.\n\n```c\nb2DestroyShape(myShapeId);\n```\n\nMaterial properties such as density, friction, and restitution are associated with shapes\ninstead of bodies. Since you can attach multiple shapes to a body, this allows for more\npossible setups. For example, you can make a car that is heavier in the back.\n\n### Density\nThe shape density is used to compute the mass properties of the parent\nbody. The density can be zero or positive. You should generally use\nsimilar densities for all your shapes. This will improve stacking\nstability.\n\nThe mass of a body is not adjusted when you set the density. You must\ncall `b2Body_ApplyMassFromShapes()` for this to occur. Generally you should establish\nthe shape density in `b2ShapeDef` and avoid modifying it later because this\ncan be expensive, especially on a compound body.\n\n```c\nb2Shape_SetDensity(myShapeId, 5.0f);\nb2Body_ApplyMassFromShapes(myBodyId);\n```\n\n### Friction\nFriction is used to make objects slide along each other realistically.\nBox2D supports static and dynamic friction, but uses the same parameter\nfor both. Box2D attempts to simulate friction accurately and the friction\nstrength is proportional to the normal force. This is called [Coulomb\nfriction](https://en.wikipedia.org/wiki/Friction). The friction parameter\nis usually set between 0 and 1, but\ncan be any non-negative value. A friction value of 0 turns off friction\nand a value of 1 makes the friction strong. When the friction force is\ncomputed between two shapes, Box2D must combine the friction parameters\nof the two parent shapes. This is done with the\n[geometric mean](https://en.wikipedia.org/wiki/Geometric_mean):\n\n```c\nfloat mixedFriction = sqrtf(b2Shape_GetFriction(shapeIdA) * b2Shape_GetFriction(shapeIdB));\n```\n\nIf one shape has zero friction then the mixed friction will be zero.\n\n### Restitution\n[Restitution](https://en.wikipedia.org/wiki/Coefficient_of_restitution) is used to make\nobjects bounce. The restitution value is\nusually set to be between 0 and 1. Consider dropping a ball on a table.\nA value of zero means the ball won't bounce. This is called an\n*inelastic* collision. A value of one means the ball's velocity will be\nexactly reflected. This is called a *perfectly elastic* collision.\nRestitution is combined using the following formula.\n\n```c\nfloat mixedRestitution = b2MaxFloat(b2Shape_GetRestitution(shapeIdA), b2Shape_GetRestitution(shapeIdB));\n```\n\nRestitution is combined this way so that you can have a bouncy super\nball without having a bouncy floor.\n\nWhen a shape develops multiple contacts, restitution is simulated\napproximately. This is because Box2D uses a sequential solver. Box2D\nalso uses inelastic collisions when the collision velocity is small.\nThis is done to prevent jitter. See `b2WorldDef::restitutionThreshold`.\n\n### Friction and restitution callbacks\nAdvanced users can override friction and restitution mixing using b2FrictionCallback\nand b2RestitutionCallback. These should be very light weight functions because they\nare called frequently. See the API reference for details.\n\n```c\nfloat MyFrictionCallback(float frictionA, int userMaterialIdA, float frictionB, int userMaterialIdB)\n{\n    if (userMaterialIdA > userMaterialIdB)\n    {\n        return frictionA;\n    }\n\n    return frictionB;\n}\n\nb2WorldDef worldDef = b2DefaultWorldDef();\nworldDef.frictionCallback = MyFrictionCallback;\n```\n\n### Filtering {#filtering}\nCollision filtering allows you to efficiently prevent collision between shapes.\nFor example, say you make a character that rides a bicycle. You want the\nbicycle to collide with the terrain and the character to collide with\nthe terrain, but you don't want the character to collide with the\nbicycle (because they must overlap). Box2D supports such collision\nfiltering using categories, masks, and groups.\n\nBox2D supports 64 collision categories. For each shape you can specify\nwhich category it belongs to. You can also specify what other categories\nthis shape can collide with. For example, you could specify in a\nmultiplayer game that players don't collide with each other. Rather\nthan identifying all the situations where things should not collide, I recommend\nidentifying all the situations where things should collide. This way you\ndon't get into situations where you are using \n[double negatives](https://en.wikipedia.org/wiki/Double_negative).\nYou can specify which things can collide using mask bits. For example:\n\n```c\nenum MyCategories\n{\n    PLAYER = 0x00000002,\n    MONSTER = 0x00000004,\n};\n\nb2ShapeDef playerShapeDef = b2DefaultShapeDef();\nb2ShapeDef monsterShapeDef = b2DefaultShapeDef();\nplayerShapeDef.filter.categoryBits = PLAYER;\nmonsterShapeDef.filter.categoryBits = MONSTER;\n\n// Players collide with monsters, but not with other players\nplayerShapeDef.filter.maskBits = MONSTER;\n\n// Monsters collide with players and other monsters\nmonsterShapeDef.filter.maskBits = PLAYER | MONSTER;\n```\n\nHere is the rule for a collision to occur:\n\n```c\nuint64_t catA = shapeA.filter.categoryBits;\nuint64_t maskA = shapeA.filter.maskBits;\nuint64_t catB = shapeB.filter.categoryBits;\nuint64_t maskB = shapeB.filter.maskBits;\n\nif ((catA & maskB) != 0 && (catB & maskA) != 0)\n{\n    // shapes can collide\n}\n```\n\nAnother filtering feature is *collision group*.\nCollision groups let you specify a group index. You can have\nall shapes with the same group index always collide (positive index)\nor never collide (negative index). Group indices are usually used for\nthings that are somehow related, like the parts of a bicycle. In the\nfollowing example, shape1 and shape2 always collide, but shape3\nand shape4 never collide.\n\n```c\nshape1Def.filter.groupIndex = 2;\nshape2Def.filter.groupIndex = 2;\nshape3Def.filter.groupIndex = -8;\nshape4Def.filter.groupIndex = -8;\n```\n\nCollisions between shapes of different group indices are filtered\naccording the category and mask bits. If two shapes have the\nsame non-zero group index, then this overrides the category and mask.\nCollision groups have a higher priority than categories and masks.\n\nNote that additional collision filtering occurs automatically in Box2D. Here is a\nlist:\n- A shape on a static body can only collide with a dynamic body.\n- A shape on a kinematic body can only collide with a dynamic body.\n- Shapes on the same body never collide with each other.\n- You can optionally enable/disable collision between bodies connected by a joint.\n\nSometimes you might need to change collision filtering after a shape\nhas already been created. You can get and set the `b2Filter` structure on\nan existing shape using `b2Shape_GetFilter()` and\n`b2Shape_SetFilter()`. Changing the filter is expensive because\nit causes contacts to be destroyed.\n\n### Chain Shapes\nThe chain shape provides an efficient way to connect many line segments together\nto construct your static game worlds. Chain shapes automatically\neliminate ghost collisions and provide one-sided collision.\n\nIf you don't care about ghost collisions, you can create a bunch of\ntwo-sided segment shapes. The performance is similar.\n\nThe simplest way to use chain shapes is to create loops. Simply provide an\narray of vertices.\n\n```c\nb2Vec2 points[4] = {\n    {1.7f, 0.0f},\n    {1.0f, 0.25f},\n    {0.0f, 0.0f},\n    {-1.7f, 0.4f}};\n\nb2ChainDef chainDef = b2DefaultChainDef();\nchainDef.points = points;\nchainDef.count = 4;\n\nb2ChainId myChainId = b2CreateChain(myBodyId, &chainDef);\n\n// Later ...\nb2DestroyChain(myChainId);\n\n// Nullify id for safety\nmyChainId = b2_nullChainId;\n```\n\nThe segment normal depends on the winding order. A counter-clockwise winding order orients the normal outwards and a clockwise winding order orients the normal inwards.\n\n![Chain Shape Outwards Loop](images/chain_loop_outwards.svg)\n\n![Chain Shape Inwards Loop](images/chain_loop_inwards.svg)\n\nYou may have a scrolling game world and would like to connect several chains together.\nYou can connect chains together using ghost vertices. To do this you must have the first three or last three points of each chain overlap. See the sample `ChainLink` for details.\n\n![Chain Shape](images/chain_shape.svg)\n\nSelf-intersection of chain shapes is not supported. It might work, it\nmight not. The code that prevents ghost collisions assumes there are no\nself-intersections of the chain. Also, very close vertices can cause\nproblems. Make sure all your points are more than than about a centimeter apart.\n\n![Self Intersection is Bad](images/self_intersect.svg)\n\nEach segment in the chain is created as a `b2ChainSegment` shape on the body. If you have the\nshape id for a chain segment shape, you can get the owning chain id. This will return `b2_nullChainId`\nif the shape is not a chain segment.\n\n```c\nb2ChainId chainId = b2SHape_GetParentChain(myShapeId);\n```\n\nYou cannot create a chain segment shape directly.\n\n### Sensors\nSometimes game logic needs to know when two shapes overlap yet there\nshould be no collision response. This is done by using sensors. A sensor\nis a shape that detects overlap but does not produce a response.\n\nYou can flag any shape as being a sensor. Sensors may be static,\nkinematic, or dynamic. Remember that you may have multiple shapes per\nbody and you can have any mix of sensors and solid shapes. Sensors can also\ndetect other sensors.\n\n```c\nb2ShapeDef shapeDef = b2DefaultShapeDef();\nshapeDef.isSensor = true;\n```\n\nFor both sensors and non-sensors, sensor events must also be enabled. There is a\nperformance cost to generate sensor events, so they are disabled by default.\n\n```c\nshapeDef.enableSensorEvents = true;\n```\n\nSensors are processed at the end of the world step and generate begin and end\nevents without delay. User operations may cause overlaps to begin or end. These\nare processed the next time step. Such operations include:\n- destroying a body or shape\n- changing a shape filter\n- disabling or enabling a body\n- setting a body transform\n- disabling or enabling sensor events on a shape\n\nSensors do not detect objects that pass through the sensor shape within \none time step. So sensors do not have continuous collision detection.\nIf you have fast moving object and/or small sensors then you should use a\nray or shape cast to detect these events.\n\nYou can access the current sensor overlaps. Be careful because some shape ids may\nbe invalid due to a shape being destroyed. Use `b2Shape_IsValid` to ensure an\noverlapping shape is still valid.\n\n```cpp\n// First determine the required array capacity to hold all the overlapping shape ids.\nint capacity = b2Shape_GetSensorCapacity( sensorShapeId );\nstd::vector<b2ShapeId> overlaps;\noverlaps.resize( capacity );\n\n// Now get all overlaps and record the actual count\nint count = b2Shape_GetSensorOverlaps( sensorShapeId, overlaps.data(), capacity );\noverlaps.resize( count );\n\nfor ( int i = 0; i < count; ++i )\n{\n    b2ShapeId visitorId = overlaps[i];\n\n    // Ensure the visitorId is valid\n    if ( b2Shape_IsValid( visitorId ) == false )\n    {\n        continue;\n    }\n\n    // process overlap using game logic\n}\n```\n\nSensor overlap can also be determined using events, which are described below.\n\n### Sensor Events\nSensor events are available after every call to `b2World_Step()`.\nSensor events are the best way to get information about sensors overlaps. There are\nevents for when a shape begins to overlap with a sensor.\n\n```c\nb2SensorEvents sensorEvents = b2World_GetSensorEvents(myWorldId);\nfor (int i = 0; i < sensorEvents.beginCount; ++i)\n{\n    b2SensorBeginTouchEvent* beginTouch = sensorEvents.beginEvents + i;\n    void* myUserData = b2Shape_GetUserData(beginTouch->visitorShapeId);\n    // process begin event\n}\n```\n\nAnd there are events when a shape stops overlapping with a sensor. Be careful with end\ntouch events because they may be generated when shapes are destroyed. Test the shape\nids with `b2Shape_IsValid`.\n\n```c\nfor (int i = 0; i < sensorEvents.endCount; ++i)\n{\n    b2SensorEndTouchEvent* endTouch = sensorEvents.endEvents + i;\n    if (b2Shape_IsValid(endTouch->visitorShapeId))\n    {\n        void* myUserData = b2Shape_GetUserData(endTouch->visitorShapeId);\n        // process end event\n    }\n}\n```\n\nSensor events should be processed after the world step and before other game logic. This should\nhelp you avoid processing stale data.\n\nSensor events are only enabled for shapes and sensors if b2ShapeDef::enableSensorEvents is set to true.\n\n> **Note**:\n> A shape cannot start or stop being a sensor. Such a feature would break\n> sensor events, potentially causing bugs in game logic.\n\n## Contacts\nContacts are internal objects created by Box2D to manage collision between pairs of\nshapes. They are fundamental to rigid body simulation in games.\n\n### Terminology\nContacts have a fair bit of terminology that are important to review.\n\n#### contact point\nA contact point is a point where two shapes touch. Box2D approximates\ncontact with a small number of points. Specifically, contact between\ntwo shapes has 0, 1, or 2 points. This is possible because Box2D uses\nconvex shapes.\n\n#### contact normal\nA contact normal is a unit vector that points from one shape to another.\nBy convention, the normal points from shapeA to shapeB.\n\n#### contact separation\nSeparation is the opposite of penetration. Separation is negative when\nshapes overlap.\n\n#### contact manifold\nContact between two convex polygons may generate up to 2 contact points.\nBoth of these points use the same normal, so they are grouped into a\ncontact manifold, which is an approximation of a continuous region of\ncontact.\n\n#### normal impulse\nThe normal force is the force applied at a contact point to prevent the\nshapes from penetrating. For convenience, Box2D uses impulses. The\nnormal impulse is just the normal force multiplied by the time step. Since\nBox2D uses sub-stepping, this is the sub-step time step.\n\n#### tangent impulse\nThe tangent force is generated at a contact point to simulate friction.\nFor convenience, this is stored as an impulse.\n\n#### contact point id\nBox2D tries to re-use the contact impulse results from a time step as the\ninitial guess for the next time step. Box2D uses contact point ids to match\ncontact points across time steps. The ids contain geometric feature\nindices that help to distinguish one contact point from another.\n\n#### speculative contact\nWhen two shapes are close together, Box2D will create up to two contact\npoints even if the shapes are not touching. This lets Box2D anticipate\ncollision to improve behavior. Speculative contact points have positive\nseparation.\n\n### Contact Lifetime\nContacts are created when two shape's AABBs (bounding boxes) begin to overlap. Sometimes\ncollision filtering will prevent the creation of contacts. Contacts are\ndestroyed with the AABBs cease to overlap.\n\nSo you might gather that there may be contacts created for shapes that\nare not touching (just their AABBs). Well, this is correct. It's a\n\\\"chicken or egg\\\" problem. We don't know if we need a contact object\nuntil one is created to analyze the collision. We could delete the\ncontact right away if the shapes are not touching, or we can just wait\nuntil the AABBs stop overlapping. Box2D takes the latter approach\nbecause it lets the system cache information to improve performance.\n\n### Contact Data\nAs mentioned before, the contact is created and destroyed by\nBox2D automatically. Contact data is not created by the user. However, you are\nable to access the contact data.\n\nYou can get contact data from shapes or bodies. The contact data\non a shape is a sub-set of the contact data on a body. The contact\ndata is only returned for touching contacts. Contacts that are not\ntouching provide no meaningful information for an application.\n\nContact data is returned in arrays. So first you can ask a shape or\nbody how much space you'll need in your array. This number is conservative\nand the actual number of contacts you'll receive may be less than\nthis number, but never more.\n\n```c\nint shapeContactCapacity = b2Shape_GetContactCapacity(myShapeId);\nint bodyContactCapacity = b2Body_GetContactCapacity(myBodyId);\n```\n\nYou could allocate array space to get all the contact data in all cases, or you could use a fixed size\narray and get a limited number of results.\n\n```c\nb2ContactData contactData[10];\nint shapeContactCount = b2Shape_GetContactData(myShapeId, contactData, 10);\nint bodyContactCount = b2Body_GetContactData(myBodyId, contactData, 10);\n```\n\n`b2ContactData` contains the two shape ids and the manifold.\n\n```c\nfor (int i = 0; i < bodyContactCount; ++i)\n{\n    b2ContactData* data = contactData + i;\n    printf(\"point count = %d\\n\", data->manifold.pointCount);\n}\n```\n\nGetting contact data off shapes and bodies is not the most efficient\nway to handle contact data. Instead you should use contact events.\n\n### Contact Events\n\nContact events are available after each world step. Like sensor events these should be\nretrieved and processed before performing other game logic. Otherwise\nyou may be accessing orphaned/invalid data.\n\nYou can access all contact events in a single data structure. This is much more efficient\nthan using functions like `b2Body_GetContactData()`.\n\n```c\nb2ContactEvents contactEvents = b2World_GetContactEvents(myWorldId);\n```\n\nNone of this data applies to sensors because they are handled separately. All events involve\nat least one dynamic body.\n\nThere are three kinds of contact events:\n1. Begin touch events\n2. End touch events\n3. Hit events\n\n#### Contact Touch Event\n`b2ContactBeginTouchEvent` is recorded when two shapes begin touching. These only\ncontain the two shape ids.\n\n```c\nfor (int i = 0; i < contactEvents.beginCount; ++i)\n{\n    b2ContactBeginTouchEvent* beginEvent = contactEvents.beginEvents + i;\n    ShapesStartTouching(beginEvent->shapeIdA, beginEvent->shapeIdB);\n}\n```\n\n`b2ContactEndTouchEvent` is recorded when two shapes stop touching. These only\ncontain the two shape ids.\n\n\n\n```c\nfor (int i = 0; i < contactEvents.endCount; ++i)\n{\n    b2ContactEndTouchEvent* endEvent = contactEvents.endEvents + i;\n\n    // Use b2Shape_IsValid because a shape may have been destroyed\n    if (b2Shape_IsValid(endEvent->shapeIdA) && b2Shape_IsValid(endEvent->shapeIdB))\n    {\n        ShapesStopTouching(endEvent->shapeIdA, endEvent->shapeIdB);\n    }\n}\n```\n\nSimilar to `b2SensorEndTouchEvent`, `b2ContactEndTouchEvent` may be generated due to a user operation,\nsuch as destroying a body or shape. These events are included with simulation events after the next `b2World_Step`.\n\nShapes only generate begin and end touch events if `b2ShapeDef::enableContactEvents` is true.\n\n#### Hit Events\nTypically in games you are mainly concerned about getting contact events for when\ntwo shapes collide at a significant speed so you can play a sound and/or particle effect. Hit\nevents are the answer for this.\n\n```c\nfor (int i = 0; i < contactEvents.hitCount; ++i)\n{\n    b2ContactHitEvent* hitEvent = contactEvents.hitEvents + i;\n    if (hitEvent->approachSpeed > 10.0f)\n    {\n        // play sound\n    }\n}\n```\n\nShapes only generate hit events if `b2ShapeDef::enableHitEvents` is true.\nI recommend you only enable this for shapes that need hit events because\nit creates some overhead. Box2D also only reports hit events that have an\napproach speed larger than `b2WorldDef::hitEventThreshold`.\n\n### Contact Filtering\nOften in a game you don't want all objects to collide. For example, you\nmay want to create a door that only certain characters can pass through.\nThis is called contact filtering, because some interactions are filtered\nout.\n\nContact filtering is setup on shapes and is covered [here](#filtering).\n\n### Advanced Contact Handling\n\n#### Custom Filtering Callback\nFor the best performance, use the contact filtering provided by `b2Filter`.\nHowever, in some cases you may need custom filtering. You can do\nthis by registering a custom filter callback that implements `b2CustomFilterFcn()`.\n\n```c\nbool MyCustomFilter(b2ShapeId shapeIdA, b2ShapeId shapeIdB, void* context)\n{\n    MyGame* myGame = context;\n    return myGame->WantsCollision(shapeIdA, shapeIdB);\n}\n\n// Elsewhere\nb2World_SetCustomFilterCallback(myWorldId, MyCustomFilter, myGame);\n```\n\nThis function must be [thread-safe](https://en.wikipedia.org/wiki/Thread_safety) and must not read from or write to the Box2D world. Otherwise you will get a [race condition](https://en.wikipedia.org/wiki/Race_condition). \n\n#### Pre-Solve Callback\nThis is called after collision detection, but before collision\nresolution. This gives you a chance to disable the contact based on the contact geometry. For example, you can implement a one-sided platform using this callback.\n\nThe contact will be re-enabled each time through collision processing,\nso you will need to disable the contact every time-step. This function must be thread-safe\nand must not read from or write to the Box2D world.\n\n```c\nbool MyPreSolve(b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Manifold* manifold, void* context)\n{\n    MyGame* myGame = context;\n\n    if (myGame->IsHittingBelowPlatform(shapeIdA, shapeIdB, manifold))\n    {\n        return false;\n    }\n\n    return true;\n}\n\n// Elsewhere\nb2World_SetPreSolveCallback(myWorldId, MyPreSolve, myGame);\n```\n\nNote this currently does not work with high speed collisions, so you may see a\npause in those situations.\n\nSee the `Platformer` sample for more details.\n\n## Joints\nJoints are used to constrain bodies to the world or to each other.\nTypical examples in games include ragdolls, teeters, and pulleys. Joints\ncan be combined in many different ways to create interesting motions.\n\nSome joints provide limits so you can control the range of motion. Some\njoints provide motors which can be used to drive the joint at a\nprescribed speed until a prescribed force/torque is exceeded. And some\njoints provide springs with damping.\n\nJoint motors can be used in many ways. You can use motors to control\nposition by specifying a joint velocity that is proportional to the\ndifference between the actual and desired position. You can also use\nmotors to simulate joint friction: set the joint velocity to zero and\nprovide a small, but significant maximum motor force/torque. Then the\nmotor will attempt to keep the joint from moving until the load becomes\ntoo strong.\n\n### Joint Definition\nEach joint type has an associated joint definition. All\njoints are connected between two different bodies. One body may be static.\nJoints between static and/or kinematic bodies are allowed, but have no\neffect and use some processing time.\n\nIf a joint is connected to a disabled body, that joint is effectively disabled.\nWhen the both bodies on a joint become enabled, the joint will automatically\nbe enabled as well. In other words, you do not need to explicitly enable\nor disable a joint.\n\nYou can specify user data for any joint type and you can provide a flag\nto prevent the attached bodies from colliding with each other. This is\nthe default behavior and you must set the `collideConnected`\nBoolean to allow collision between two connected bodies.\n\nMany joint definitions require that you provide some geometric data.\nOften a joint will be defined by anchor points. These are points fixed\nin the attached bodies. Box2D requires these points to be specified in\nlocal coordinates. This way the joint can be specified even when the\ncurrent body transforms violate the joint constraint. Additionally, some joint\ndefinitions need a reference angle between the bodies.\nThis may be necessary to constrain rotation correctly.\n\nThe rest of the joint definition data depends on the joint type. I\ncover these below.\n\n### Joint Lifetime\nJoints are created using creation functions supplied for each joint type. They are destroyed\nwith a shared function. All joint types share a single id type `b2JointId`.\n\nHere's an example of the lifetime of a revolute joint:\n\n```c\nb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\njointDef.bodyIdA = myBodyA;\njointDef.bodyIdB = myBodyB;\njointDef.localAnchorA = (b2Vec2){0.0f, 0.0f};\njointDef.localAnchorB = (b2Vec2){1.0f, 2.0f};\n\nb2JointId myJointId = b2CreateRevoluteJoint(myWorldId, &jointDef);\n\n// ... do stuff ...\n\nb2DestroyJoint(myJointId);\nmyJointId = b2_nullJointId;\n```\n\nIt is always good to nullify your ids after they are destroyed. \n\nJoint lifetime is related to body lifetime. Joints cannot exist detached from a body. \nSo when a body is destroyed, all joints attached to that body are automatically destroyed.\nThis means you need to be careful to avoid using joint ids when the attached body was\ndestroyed. Box2D will assert if you use a dangling joint id.\n\n> **Caution**:\n> Joints are destroyed when an attached body is destroyed.\n\nFortunately you can check if your joint id is valid.\n\n```c\nif (b2Joint_IsValid(myJointId) == false)\n{\n    myJointId = b2_nullJointId;\n}\n```\n\nThis is certainly useful, but should not be overused because if you are creating\nand destroying many joints, this may eventually alias to a different joint. All ids have\na limit of 64k generations.\n\n### Using Joints\nMany simulations create the joints and don't access them again until\nthey are destroyed. However, there is a lot of useful data contained in\njoints that you can use to create a rich simulation.\n\nFirst of all, you can get the type, bodies, anchor points, and user data from\na joint.\n\n```c\nb2JointType jointType = b2Joint_GetType(myJointId);\nb2BodyId bodyIdA = b2Joint_GetBodyA(myJointId);\nb2BodyId bodyIdB = b2Joint_GetBodyB(myJointId);\nb2Vec2 localAnchorA = b2Joint_GetLocalAnchorA(myJointId);\nb2Vec2 localAnchorB = b2Joint_GetLocalAnchorB(myJointId);\nvoid* myUserData = b2Joint_GetUserData(myJointId);\n```\n\nAll joints have a reaction force and torque. Reaction forces are\nrelated to the [free body diagram](https://en.wikipedia.org/wiki/Free_body_diagram).\nThe Box2D convention is that the reaction force\nis applied to body B at the anchor point. You can use reaction forces to\nbreak joints or trigger other game events. These functions may do some\ncomputations, so don't call them if you don't need the result.\n\n```c\nb2Vec2 force = b2Joint_GetConstraintForce(myJointId);\nfloat torque = b2Joint_GetConstraintTorque(myJointId);\n```\n\nSee the sample `BreakableJoint` for more details.\n\n### Distance Joint\nOne of the simplest joints is a distance joint which says that the\ndistance between two points on two bodies must be constant. When you\nspecify a distance joint the two bodies should already be in place. Then\nyou specify the two anchor points in local coordinates. The first anchor\npoint is connected to body A, and the second anchor point is connected\nto body B. These points imply the length of the distance constraint.\n\n![Distance Joint](images/distance_joint.svg)\n\nHere is an example of a distance joint definition. In this case I\ndecided to allow the bodies to collide.\n\n```c\nb2DistanceJointDef jointDef = b2DefaultDistanceJointDef();\njointDef.bodyIdA = myBodyIdA;\njointDef.bodyIdB = myBodyIdB;\njointDef.localAnchorA = (b2Vec2){1.0f, -3.0f};\njointDef.localAnchorB = (b2Vec2){0.0f, 0.5f};\nb2Vec2 anchorA = b2Body_GetWorldPoint(myBodyIdA, jointDef.localAnchorA);\nb2Vec2 anchorB = b2Body_GetWorldPoint(myBodyIdB, jointDef.localAnchorB);\njointDef.length = b2Distance(anchorA, anchorB);\njointDef.collideConnected = true;\n\nb2JointId myJointId = b2CreateDistanceJoint(myWorldId, &jointDef);\n```\n\nThe distance joint can also be made soft, like a spring-damper\nconnection. Softness is achieved by enabling the spring and tuning two values in the definition:\nHertz and damping ratio.\n\n```c\njointDef.enableSpring = true;\njointDef.hertz = 2.0f;\njointDef.dampingRatio = 0.5f;\n```\n\nThe hertz is the frequency of a [harmonic oscillator](https://en.wikipedia.org/wiki/Harmonic_oscillator) (like a\nguitar string). Typically the frequency\nshould be less than a half the frequency of the time step. So if you are using\na 60Hz time step, the frequency of the distance joint should be less than 30Hz.\nThe reason is related to the [Nyquist frequency](https://en.wikipedia.org/wiki/Nyquist_frequency).\n\nThe damping ratio controls how fast the oscillations dissipate. A damping\nratio of one is [critical damping](https://en.wikipedia.org/wiki/Damping) and prevents\noscillation.\n\nIt is also possible to define a minimum and maximum length for the distance joint.\nYou can even motorize the distance joint to adjust its length dynamically.\nSee `b2DistanceJointDef` and the `DistanceJoint` sample for details.\n\n### Revolute Joint\nA revolute joint forces two bodies to share a common anchor point, often\ncalled a hinge point or pivot. The revolute joint has a single degree of freedom:\nthe relative rotation of the two bodies. This is called the joint angle.\n\n![Revolute Joint](images/revolute_joint.svg)\n\nLike all joints, the anchor points are specified in local coordinates.\nHowever, you can use the body utility functions to simplify this.\n\n```c\nb2Vec2 worldPivot = {10.0f, -4.0f};\nb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\njointDef.bodyIdA = myBodyIdA;\njointDef.bodyIdB = myBodyIdB;\njointDef.localAnchorA = b2Body_GetLocalPoint(myBodyIdA, worldPivot);\njointDef.localAnchorB = b2Body_GetLocalPoint(myBodyIdB, worldPivot);\n\nb2JointId myJointId = b2CreateRevoluteJoint(myWorldId, &jointDef);\n```\n\nThe revolute joint angle is positive when bodyB rotates counter-clockwise\nabout the\nanchor point. Like all angles in Box2D, the revolute angle is measured in\nradians. By convention the revolute joint angle is zero when the two bodies\nhave equal angles. You can offset this using `b2RevoluteJointDef::referenceAngle`.\n\nIn some cases you might wish to control the joint angle. For this, the\nrevolute joint can simulate a joint limit and/or a motor.\n\nA joint limit forces the joint angle to remain between a lower and upper\nangle. The limit will apply as much torque as needed to make this\nhappen. The limit range should include zero, otherwise the joint will\nlurch when the simulation begins. The lower and upper limit are relative to\nthe reference angle.\n\nA joint motor allows you to specify the joint speed. The speed can be negative or\npositive. A motor can have infinite force, but this is usually not desirable. Recall the eternal\nquestion:\n\n> *What happens when an irresistible force meets an immovable object?*\n\nI can tell you it's not pretty. So you can provide a maximum torque for\nthe joint motor. The joint motor will maintain the specified speed\nunless the required torque exceeds the specified maximum. When the\nmaximum torque is exceeded, the joint will slow down and can even\nreverse.\n\nYou can use a joint motor to simulate joint friction. Just set the joint\nspeed to zero, and set the maximum torque to some small, but significant\nvalue. The motor will try to prevent the joint from rotating, but will\nyield to a significant load.\n\nHere's a revision of the revolute joint definition above; this time the\njoint has a limit and a motor enabled. The motor is setup to simulate\njoint friction.\n\n```c\nb2Vec2 worldPivot = {10.0f, -4.0f};\nb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\njointDef.bodyIdA = myBodyIdA;\njointDef.bodyIdB = myBodyIdB;\njointDef.localAnchorA = b2Body_GetLocalPoint(myBodyIdA, worldPivot);\njointDef.localAnchorB = b2Body_GetLocalPoint(myBodyIdB, worldPivot);\njointDef.lowerAngle = -0.5f * b2_pi; // -90 degrees\njointDef.upperAngle = 0.25f * b2_pi; // 45 degrees\njointDef.enableLimit = true;\njointDef.maxMotorTorque = 10.0f;\njointDef.motorSpeed = 0.0f;\njointDef.enableMotor = true;\n```\nYou can access a revolute joint's angle, speed, and motor torque.\n\n```c\nfloat angleInRadians = b2RevoluteJoint_GetAngle(myJointId);\nfloat speed = b2RevoluteJoint_GetMotorSpeed(myJointId);\nfloat currentTorque = b2RevoluteJoint_GetMotorTorque(myJointId);\n```\n\nYou also update the motor parameters each step.\n\n```c\nb2RevoluteJoint_SetMotorSpeed(myJointId, 20.0f);\nb2RevoluteJoint_SetMaxMotorTorque(myJointId, 100.0f);\n```\n\nJoint motors have some interesting abilities. You can update the joint\nspeed every time step so you can make the joint move back-and-forth like\na sine-wave or according to whatever function you want.\n\n```c\n// ... Game Loop Begin ...\n\nb2RevoluteJoint_SetMotorSpeed(myJointId, cosf(0.5f * time));\n\n// ... Game Loop End ...\n```\n\nYou can also use joint motors to track a desired joint angle. For example:\n\n```c\n// ... Game Loop Begin ...\n\nfloat angleError = b2RevoluteJoint_GetAngle(myJointId) - angleTarget;\nfloat gain = 0.1f;\nb2RevoluteJoint_SetMotorSpeed(myJointId, -gain * angleError);\n\n// ... Game Loop End ...\n```\n\nGenerally your gain parameter should not be too large. Otherwise your\njoint may become unstable.\n\n### Prismatic Joint\nA prismatic joint allows for relative translation of two bodies along a\nlocal axis. A prismatic joint prevents relative rotation. Therefore,\na prismatic joint has a single degree of freedom.\n\n![Prismatic Joint](images/prismatic_joint.svg)\n\nThe prismatic joint definition is similar to the revolute joint\ndescription; just substitute translation for angle and force for torque.\nUsing this analogy provides an example prismatic joint definition with a\njoint limit and a friction motor:\n\n```c\nb2Vec2 worldPivot = {10.0f, -4.0f};\nb2Vec2 worldAxis = {1.0f, 0.0f};\nb2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();\njointDef.bodyIdA = myBodyIdA;\njointDef.bodyIdB = myBodyIdB;\njointDef.localAnchorA = b2Body_GetLocalPoint(myBodyIdA, worldPivot);\njointDef.localAnchorB = b2Body_GetLocalPoint(myBodyIdB, worldPivot);\njointDef.localAxisA = b2Body_GetLocalVector(myBodyIdA, worldAxis);\njointDef.lowerTranslation = -5.0f;\njointDef.upperTranslation = 2.5f;\njointDef.enableLimit = true;\njointDef.maxMotorForce = 1.0f;\njointDef.motorSpeed = 0.0f;\njointDef.enableMotor = true;\n```\n\nThe revolute joint has an implicit axis coming out of the screen. The\nprismatic joint needs an explicit axis parallel to the screen. This axis\nis fixed in body A.\n\nThe prismatic joint translation is zero when the anchor points overlap. I recommend\nto have the prismatic anchor points close to the center of mass of the two bodies.\nThis will improve joint stiffness.\n\nUsing a prismatic joint is similar to using a revolute joint. Here are\nthe relevant member functions:\n\n```c\nfloat PrismaticJoint::GetJointTranslation() const;\nfloat PrismaticJoint::GetJointSpeed() const;\nfloat PrismaticJoint::GetMotorForce() const;\nvoid PrismaticJoint::SetMotorSpeed(float speed);\nvoid PrismaticJoint::SetMotorForce(float force);\n```\n\n### Mouse Joint\nThe mouse joint is used in the samples to manipulate bodies with the\nmouse. It attempts to drive a point on a body towards the current\nposition of the cursor. There is no restriction on rotation.\n\nThe mouse joint definition has a target point, maximum force, Hertz,\nand damping ratio. The target point initially coincides with the body's\nanchor point. The maximum force is used to prevent violent reactions\nwhen multiple dynamic bodies interact. You can make this as large as you\nlike. The frequency and damping ratio are used to create a spring/damper\neffect similar to the distance joint.\n\n### Weld Joint\nThe weld joint attempts to constrain all relative motion between two\nbodies. See the `Cantilever` sample to see how the weld joint\nbehaves.\n\nIt is tempting to use the weld joint to define breakable structures.\nHowever, the Box2D solver is approximate so the joints can be soft in some\ncases regardless of the joint settings. So chains of bodies connected by weld\njoints may flex.\n\nSee the `ContactEvent` sample for an example of how to merge and split bodies\nwithout using the weld joint.\n\n### Motor Joint\nA motor joint lets you control the motion of a body by specifying target\nposition and rotation offsets. You can set the maximum motor force and\ntorque that will be applied to reach the target position and rotation.\nIf the body is blocked, it will stop and the contact forces will be\nproportional the maximum motor force and torque. See `b2MotorJointDef` and\nthe `MotorJoint` sample for details.\n\n### Wheel Joint\nThe wheel joint restricts a point on bodyB to a line on bodyA. The wheel\njoint also provides a suspension spring and a motor. See the `Driving` sample\nfor details.\n\n![Wheel Joint](images/wheel_joint.svg)\n\nThe wheel joint is designed specifically for vehicles. It provides a translation\nand rotation. The translation has a spring and damper to simulate the vehicle\nsuspension. The rotation allows the wheel to rotate. You can specify an rotational\nmotor to drive the wheel and to apply braking. See `b2WheelJointDef` and the `Drive`\nsample for details.\n\nYou may also use the wheel joint where you want free rotation and translation along\nan axis. See the `ScissorLift` sample for details.\n\n## Spatial Queries {#spatial}\nSpatial queries allow you to inspect the world geometrically. There are overlap queries,\nray-casts, and shape-casts. These allow you to do things like:\n- find a treasure chest near the player\n- shoot a laser beam and destroy all asteroids in the path\n- throw a grenade that is represented as a circle moving along a parabolic path\n\n### Overlap Queries\nSometimes you want to determine all the shapes in a region. The world has a fast\nlog(N) method for this using the broad-phase data structure. Box2D provides these\noverlap tests:\n- axis-aligned bound box (AABB) overlap\n- shape proxy overlap\n\n#### Query Filtering\nA basic understanding of query filtering is needed before considering the specific queries.\nShape versus shape filtering was discussed [here](#filtering). A similar setup is used\nfor queries. This lets your queries only consider certain categories of shapes, it also\nlets your shapes ignore certain queries.\n\nJust like shapes, queries themselves can have a category. For example, you can have a `CAMERA`\nor `PROJECTILE` category.\n\n```c\nenum MyCategories\n{\n    STATIC = 0x00000001,\n    PLAYER = 0x00000002,\n    MONSTER = 0x00000004,\n    WINDOW = 0x00000008,\n    CAMERA = 0x00000010,\n    PROJECTILE = 0x00000020,\n};\n\n// Grenades collide with the static world, monsters, and windows but\n// not players or other projectiles.\nb2QueryFilter grenadeFilter;\ngrenadeFilter.categoryBits = PROJECTILE;\ngrenadeFilter.maskBits = STATIC | MONSTER | WINDOW;\n\n// The view collides with the static world, monsters, and players.\nb2QueryFilter viewFilter;\nviewFilter.categoryBits = CAMERA;\nviewFilter.maskBits = STATIC | PLAYER | MONSTER;\n```\n\nIf you want to query everything you can use `b2DefaultQueryFilter()`;\n\n#### AABB Overlap\nYou provide an AABB in world coordinates and an\nimplementation of `b2OverlapResultFcn()`. The world calls your function with each\nshape whose AABB overlaps the query AABB. Return true to continue the\nquery, otherwise return false. For example, the following code finds all\nthe shapes that potentially intersect a specified AABB and wakes up\nall of the associated bodies.\n\n```c\nbool MyOverlapCallback(b2ShapeId shapeId, void* context)\n{\n    b2BodyId bodyId = b2Shape_GetBody(shapeId);\n    b2Body_SetAwake(bodyId, true);\n\n    // Return true to continue the query.\n    return true;\n}\n\n// Elsewhere ...\nMyOverlapCallback callback;\nb2AABB aabb;\naabb.lowerBound = (b2Vec2){-1.0f, -1.0f};\naabb.upperBound = (b2Vec2){1.0f, 1.0f};\nb2QueryFilter filter = b2DefaultQueryFilter();\nb2World_OverlapAABB(myWorldId, aabb, filter, MyOverlapCallback, &myGame);\n```\n\nDo not make any assumptions about the order of the callback. The order shapes\nare returned to your callback may seem arbitrary.\n\n#### Shape Overlap\nThe AABB overlap is very fast but not very accurate because it only considers\nthe shape bounding box. If you want an accurate overlap test, you can use a shape\noverlap query.\n\nThe overlap function uses a `b2ShapeProxy` which is an abstract shape consisting\nof some points and a radius. You can think of it as a cloud of circles that has been\n_shrink wrapped_. This can represent a point, a circle, a line segment, a capsule, a polygon,\na rounded rectangle, and so on. The helper function `b2MakeProxy` takes an array of points\nand a radius.\n\nIn this example, I'm creating a shape proxy from a circle and then calling `b2World_OverlapShape()`.\nThis takes a `b2OverlapResultFcn()` to receive results and control the search progress.\n\n```c\nb2Circle circle = {b2Vec2_zero, 0.2f};\nb2ShapeProxy proxy = b2MakeProxy(&circle.center, 1, circle.radius);\nb2World_OverlapShape(myWorldId, &proxy, grenadeFilter, MyOverlapCallback, &myGame);\n```\n\n### Ray-casts\nYou can use ray-casts to do line-of-sight checks, fire guns, etc. You\nperform a ray-cast by implementing the `b2CastResultFcn()` callback\nfunction and providing the\norigin point and translation. The world calls your function with each shape\nhit by the ray. Your callback is provided with the shape, the point of\nintersection, the unit normal vector, and the fractional distance along\nthe ray. You cannot make any assumptions about the order of the points\nsent to the callback. The callback may receive points that are further away\nbefore receiving points that are closer.\n\nYou control the continuation of the ray-cast by returning a fraction.\nReturning a fraction of zero indicates the ray-cast should be\nterminated. A fraction of one indicates the ray-cast should continue as\nif no hit occurred. If you return the fraction from the argument list,\nthe ray will be clipped to the current intersection point. So you can\nray-cast any shape, ray-cast all shapes, or ray-cast the closest shape\nby returning the appropriate fraction.\n\nYou may also return of fraction of -1 to filter the shape. Then the\nray-cast will proceed as if the shape does not exist.\n\nHere is an example:\n\n```c\n// This struct captures the closest hit shape\nstruct MyRayCastContext\n{\n    b2ShapeId shapeId;\n    b2Vec2 point;\n    b2Vec2 normal;\n    float fraction;\n};\n\nfloat MyCastCallback(b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context)\n{\n    MyRayCastContext* myContext = context;\n    myContext->shape = shape;\n    myContext->point = point;\n    myContext->normal = normal;\n    myContext->fraction = fraction;\n    return fraction;\n}\n\n// Elsewhere ...\nMyRayCastContext context = {0};\nb2Vec2 origin = {-1.0f, 0.0f};\nb2Vec2 end(3.0f, 1.0f);\nb2Vec2 translation = b2Sub(end, origin);\nb2World_CastRay(myWorldId, origin, translation, viewFilter, MyCastCallback, &context);\n```\n\nRay-cast results may be delivered in an arbitrary order. This doesn't affect the result for closest point ray-casts (except in ties). When you are collecting multiple hits along the ray, you may want to sort them according to the hit fraction. See the `CastWorld` sample for details.\n\n### Shape-casts\nShape-casts are similar to ray-casts. You can view a ray-cast as tracing a point along a line. A shape-cast\nallows you to trace a shape along a line. Like the shape overlap query, the shape cast uses a `b2ShapeProxy`\nto represent an arbitrary shape.\n\n```c\n// This struct captures the closest hit shape\nstruct MyRayCastContext\n{\n    b2ShapeId shapeId;\n    b2Vec2 point;\n    b2Vec2 normal;\n    float fraction;\n};\n\nfloat MyCastCallback(b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context)\n{\n    MyRayCastContext* myContext = context;\n    myContext->shape = shape;\n    myContext->point = point;\n    myContext->normal = normal;\n    myContext->fraction = fraction;\n    return fraction;\n}\n\n// Elsewhere ...\nMyRayCastContext context = {0};\nb2Circle circle = {{-1.0f, 0.0f}, 0.05f};\nb2ShapeProxy proxy = b2MakeProxy(&circle.center, 1, circle.radius);\nb2Vec2 translation = {10.0f, -5.0f};\nb2World_CastCircle(myWorldId, &proxy, translation, grenadeFilter, MyCastCallback, &context);\n```\n\nOtherwise, shape-casts are setup similarly to ray-casts. You can expect shape-casts to generally be slower\nthan ray-casts. So only use a shape-cast if a ray-cast won't do.\n\nJust like ray-casts, shape-casts results may be sent to the callback in any order. If you need multiple sorted results, you will need to write some code to collect and sort the results.\n\n## Simulation Loop\n\n![Simulation Loop](images/simulation_loop.svg)\n\nThe Box2D simulation loop can be useful to understand when you process results.\n\nMultithreading is represented in the diagram.\n- rectangles are parallel-for work\n- rounded rectangles are single-threaded work, but may be in parallel with other work\n\nLet's review each of these stages.\n\n### time step\nThe game starts the simulation by calling `b2World_Step` supplying the time step.\n\n### find pairs\nBox2D maintains a record of every shape that has moved. For each of these shapes the broad-phase\n(BVH) is queried for overlaps. New overlaps are recorded for processing in the next step. I avoid reporting\nexisting overlaps by using a hash table that records all existing shape pairs. This operation is a parallel-for.\n\n### create contacts\nThis takes the pair results and creates the internal contact pair structure (`b2Contact`). This\nstructure is persistent across time steps. It is used for the island graph and holding contact\nmanifolds. This operation is single-threaded but most of the work is done in `find pairs`.\n\n### rebuild BVH\nAfter the new shape pairs are known the BVH is not considered until later in the time step. Therefore the BVH\nfor dynamic and kinematic shapes may be optimized. This involves identifying the part of the collision tree that\nis stale from refitting and then performing a rebuild of that sub-tree. This is a single-single threaded operation\nthat is done concurrently with other work.\n\n### narrow phase\nThis is where the contact manifolds and points are computed. Each active contact pair is confirmed as touching\nor non-touching. If the touching state changes then contact begin and end events are generated. This is a\nparallel-for operation.\n\nNotice that contact points are computed at the beginning of the time step, before bodies have moved and before impulses\nare known. This is necessary for obtaining good simulation results efficiently. If contacts were computed at the end\nof the time step then new contact points would not be known to the constraint solver and shapes would sink into each\nother.\n\nThe `b2PreSolveFcn` is called within the parallel-for so it should be efficient and thread-safe. This is only called for\nshapes that have `enablePreSolveEvents == true`.\n\n### merge islands\nSimulation islands are merged when shapes begin touching. Existing islands that have shapes that stop touching\nare flagged as candidates for splitting. This stage is single-threaded.\n\n### split island\nA split island task may be generated if:\n- there is an island that has shapes that stopped touching\n- this island has a body that is moving slow enough to sleep\nSplitting an island may result in several new islands being created.\nOnly a single island will be split per time step because it is expensive. Delaying the split may delay some\nbodies from sleeping. This is a single-single threaded operation that is done concurrently with other work.\n\n### solve constraints\nThis solves contact and joint constraints and applies restitution. This a parallel-for with multiple internal stages.\n\n### update transforms\nThis stage does several tasks:\n- updates body transforms\n- updates the body sleeping status\n- generates body move events\n- generates island splitting candidates\n- resets forces and torques\n- updates shape bounding boxes\n- performs continuous collision between dynamic and static bodies\nThis stage is parallel-for.\n\nNote that continuous collision does not generate events. Instead they are generated the next time step. However, continuous collision will issue a `b2PreSolveFcn` callback.\n\n### hit events\nActive contacts are scanned for fast approach velocities and added to a buffer. This considers contact points that\nhave an impulse. This includes touching contacts and speculative contacts that generated an impulse (they are confirmed).\nSo you may get hit events for contact points that have a positive separation. This is a single-threaded operation.\n\n### refit BVH\nThis updates the BVH for shapes that have moved significantly. This is done by enlarging the shapes bounding box and all ancestor bounding boxes in the BVH. This is a single-threaded operation.\n\nThis can result in an inefficient BVH. This is the reason for the `rebuild BVH` stage. Refitting is faster than rebuilding the\nBVH but is necessary to ensure the BVH is valid for subsequent queries, such as ray casts.\n\n### bullets\nThis is where bullets are processed. Not that this comes after hit events because continuous collision in Box2D does not\ngenerate events until the next time step.\n\n### island sleep\nWhen an island goes to sleep the simulation data associated with that island is moved to separate stored. This keeps the active simulation data contiguous and cache friendly.\n\n### sensors\nSensor overlaps are checked in the final stage. The overlap state reflects the final body transform. Sensors do not consider sleep so they may react to the user setting a body transform or creating a sleeping body. This is a parallel-for operation. The cost is roughly proportional to the number of sensors.\n\n## Determinism\nBox2D is designed to be deterministic across thread counts and platforms. I believe this is important for debugging and game design.\n\nMultithreaded determinism is achieved by basing simulation order on creation order. This includes bodies, shapes, and joint creation order. Determinism includes results reported to users (events). These events must be in deterministic order.\n\nCross-platform determinism is achieved on 64-bit platforms by using compiler flags and by avoiding non-deterministic library functions.\n- precise math is used on MSVC\n- floating point contraction is disabled on clang and GCC\n- Box2D has custom implementations of atan2, cosine, and sine.\n\nDeterminism is on by default and there is no explicit option to disable it. However, you can break determinism by choosing different compiler flags. Box2D was designed to provide determinism with minimal cost. So there is no advantage to attempting\nto disable determinism.\n\nI maintain a unit test for determinism that is run for every pull request. Determinism is easy to break, so it is important to have regular validation.\n\n> **Caution**:\n> Box2D determinism does not mean your application will be deterministic. Consider using similar strategies to make your\n> application deterministic as I have used for Box2D.\n"
  },
  {
    "path": "extern/glad/include/KHR/khrplatform.h",
    "content": "#ifndef __khrplatform_h_\n#define __khrplatform_h_\n\n/*\n** Copyright (c) 2008-2018 The Khronos Group Inc.\n**\n** Permission is hereby granted, free of charge, to any person obtaining a\n** copy of this software and/or associated documentation files (the\n** \"Materials\"), to deal in the Materials without restriction, including\n** without limitation the rights to use, copy, modify, merge, publish,\n** distribute, sublicense, and/or sell copies of the Materials, and to\n** permit persons to whom the Materials are furnished to do so, subject to\n** the following conditions:\n**\n** The above copyright notice and this permission notice shall be included\n** in all copies or substantial portions of the Materials.\n**\n** THE MATERIALS ARE 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 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.\n*/\n\n/* Khronos platform-specific types and definitions.\n *\n * The master copy of khrplatform.h is maintained in the Khronos EGL\n * Registry repository at https://github.com/KhronosGroup/EGL-Registry\n * The last semantic modification to khrplatform.h was at commit ID:\n *      67a3e0864c2d75ea5287b9f3d2eb74a745936692\n *\n * Adopters may modify this file to suit their platform. Adopters are\n * encouraged to submit platform specific modifications to the Khronos\n * group so that they can be included in future versions of this file.\n * Please submit changes by filing pull requests or issues on\n * the EGL Registry repository linked above.\n *\n *\n * See the Implementer's Guidelines for information about where this file\n * should be located on your system and for more details of its use:\n *    http://www.khronos.org/registry/implementers_guide.pdf\n *\n * This file should be included as\n *        #include <KHR/khrplatform.h>\n * by Khronos client API header files that use its types and defines.\n *\n * The types in khrplatform.h should only be used to define API-specific types.\n *\n * Types defined in khrplatform.h:\n *    khronos_int8_t              signed   8  bit\n *    khronos_uint8_t             unsigned 8  bit\n *    khronos_int16_t             signed   16 bit\n *    khronos_uint16_t            unsigned 16 bit\n *    khronos_int32_t             signed   32 bit\n *    khronos_uint32_t            unsigned 32 bit\n *    khronos_int64_t             signed   64 bit\n *    khronos_uint64_t            unsigned 64 bit\n *    khronos_intptr_t            signed   same number of bits as a pointer\n *    khronos_uintptr_t           unsigned same number of bits as a pointer\n *    khronos_ssize_t             signed   size\n *    khronos_usize_t             unsigned size\n *    khronos_float_t             signed   32 bit floating point\n *    khronos_time_ns_t           unsigned 64 bit time in nanoseconds\n *    khronos_utime_nanoseconds_t unsigned time interval or absolute time in\n *                                         nanoseconds\n *    khronos_stime_nanoseconds_t signed time interval in nanoseconds\n *    khronos_boolean_enum_t      enumerated boolean type. This should\n *      only be used as a base type when a client API's boolean type is\n *      an enum. Client APIs which use an integer or other type for\n *      booleans cannot use this as the base type for their boolean.\n *\n * Tokens defined in khrplatform.h:\n *\n *    KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.\n *\n *    KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.\n *    KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.\n *\n * Calling convention macros defined in this file:\n *    KHRONOS_APICALL\n *    KHRONOS_APIENTRY\n *    KHRONOS_APIATTRIBUTES\n *\n * These may be used in function prototypes as:\n *\n *      KHRONOS_APICALL void KHRONOS_APIENTRY funcname(\n *                                  int arg1,\n *                                  int arg2) KHRONOS_APIATTRIBUTES;\n */\n\n#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)\n#   define KHRONOS_STATIC 1\n#endif\n\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APICALL\n *-------------------------------------------------------------------------\n * This precedes the return type of the function in the function prototype.\n */\n#if defined(KHRONOS_STATIC)\n    /* If the preprocessor constant KHRONOS_STATIC is defined, make the\n     * header compatible with static linking. */\n#   define KHRONOS_APICALL\n#elif defined(_WIN32)\n#   define KHRONOS_APICALL __declspec(dllimport)\n#elif defined (__SYMBIAN32__)\n#   define KHRONOS_APICALL IMPORT_C\n#elif defined(__ANDROID__)\n#   define KHRONOS_APICALL __attribute__((visibility(\"default\")))\n#else\n#   define KHRONOS_APICALL\n#endif\n\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APIENTRY\n *-------------------------------------------------------------------------\n * This follows the return type of the function  and precedes the function\n * name in the function prototype.\n */\n#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)\n    /* Win32 but not WinCE */\n#   define KHRONOS_APIENTRY __stdcall\n#else\n#   define KHRONOS_APIENTRY\n#endif\n\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APIATTRIBUTES\n *-------------------------------------------------------------------------\n * This follows the closing parenthesis of the function prototype arguments.\n */\n#if defined (__ARMCC_2__)\n#define KHRONOS_APIATTRIBUTES __softfp\n#else\n#define KHRONOS_APIATTRIBUTES\n#endif\n\n/*-------------------------------------------------------------------------\n * basic type definitions\n *-----------------------------------------------------------------------*/\n#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)\n\n\n/*\n * Using <stdint.h>\n */\n#include <stdint.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n/*\n * To support platform where unsigned long cannot be used interchangeably with\n * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.\n * Ideally, we could just use (u)intptr_t everywhere, but this could result in\n * ABI breakage if khronos_uintptr_t is changed from unsigned long to\n * unsigned long long or similar (this results in different C++ name mangling).\n * To avoid changes for existing platforms, we restrict usage of intptr_t to\n * platforms where the size of a pointer is larger than the size of long.\n */\n#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)\n#if __SIZEOF_POINTER__ > __SIZEOF_LONG__\n#define KHRONOS_USE_INTPTR_T\n#endif\n#endif\n\n#elif defined(__VMS ) || defined(__sgi)\n\n/*\n * Using <inttypes.h>\n */\n#include <inttypes.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)\n\n/*\n * Win32\n */\ntypedef __int32                 khronos_int32_t;\ntypedef unsigned __int32        khronos_uint32_t;\ntypedef __int64                 khronos_int64_t;\ntypedef unsigned __int64        khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif defined(__sun__) || defined(__digital__)\n\n/*\n * Sun or Digital\n */\ntypedef int                     khronos_int32_t;\ntypedef unsigned int            khronos_uint32_t;\n#if defined(__arch64__) || defined(_LP64)\ntypedef long int                khronos_int64_t;\ntypedef unsigned long int       khronos_uint64_t;\n#else\ntypedef long long int           khronos_int64_t;\ntypedef unsigned long long int  khronos_uint64_t;\n#endif /* __arch64__ */\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif 0\n\n/*\n * Hypothetical platform with no float or int64 support\n */\ntypedef int                     khronos_int32_t;\ntypedef unsigned int            khronos_uint32_t;\n#define KHRONOS_SUPPORT_INT64   0\n#define KHRONOS_SUPPORT_FLOAT   0\n\n#else\n\n/*\n * Generic fallback\n */\n#include <stdint.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#endif\n\n\n/*\n * Types that are (so far) the same on all platforms\n */\ntypedef signed   char          khronos_int8_t;\ntypedef unsigned char          khronos_uint8_t;\ntypedef signed   short int     khronos_int16_t;\ntypedef unsigned short int     khronos_uint16_t;\n\n/*\n * Types that differ between LLP64 and LP64 architectures - in LLP64,\n * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears\n * to be the only LLP64 architecture in current use.\n */\n#ifdef KHRONOS_USE_INTPTR_T\ntypedef intptr_t               khronos_intptr_t;\ntypedef uintptr_t              khronos_uintptr_t;\n#elif defined(_WIN64)\ntypedef signed   long long int khronos_intptr_t;\ntypedef unsigned long long int khronos_uintptr_t;\n#else\ntypedef signed   long  int     khronos_intptr_t;\ntypedef unsigned long  int     khronos_uintptr_t;\n#endif\n\n#if defined(_WIN64)\ntypedef signed   long long int khronos_ssize_t;\ntypedef unsigned long long int khronos_usize_t;\n#else\ntypedef signed   long  int     khronos_ssize_t;\ntypedef unsigned long  int     khronos_usize_t;\n#endif\n\n#if KHRONOS_SUPPORT_FLOAT\n/*\n * Float type\n */\ntypedef          float         khronos_float_t;\n#endif\n\n#if KHRONOS_SUPPORT_INT64\n/* Time types\n *\n * These types can be used to represent a time interval in nanoseconds or\n * an absolute Unadjusted System Time.  Unadjusted System Time is the number\n * of nanoseconds since some arbitrary system event (e.g. since the last\n * time the system booted).  The Unadjusted System Time is an unsigned\n * 64 bit value that wraps back to 0 every 584 years.  Time intervals\n * may be either signed or unsigned.\n */\ntypedef khronos_uint64_t       khronos_utime_nanoseconds_t;\ntypedef khronos_int64_t        khronos_stime_nanoseconds_t;\n#endif\n\n/*\n * Dummy value used to pad enum types to 32 bits.\n */\n#ifndef KHRONOS_MAX_ENUM\n#define KHRONOS_MAX_ENUM 0x7FFFFFFF\n#endif\n\n/*\n * Enumerated boolean type\n *\n * Values other than zero should be considered to be true.  Therefore\n * comparisons should not be made against KHRONOS_TRUE.\n */\ntypedef enum {\n    KHRONOS_FALSE = 0,\n    KHRONOS_TRUE  = 1,\n    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM\n} khronos_boolean_enum_t;\n\n#endif /* __khrplatform_h_ */\n"
  },
  {
    "path": "extern/glad/include/glad/glad.h",
    "content": "/*\n\n    OpenGL loader generated by glad 0.1.34 on Sat Jul  8 18:48:45 2023.\n\n    Language/Generator: C/C++\n    Specification: gl\n    APIs: gl=4.6\n    Profile: compatibility\n    Extensions:\n        \n    Loader: True\n    Local files: False\n    Omit khrplatform: False\n    Reproducible: False\n\n    Commandline:\n        --profile=\"compatibility\" --api=\"gl=4.6\" --generator=\"c\" --spec=\"gl\" --extensions=\"\"\n    Online:\n        https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D4.6\n*/\n\n\n#ifndef __glad_h_\n#define __glad_h_\n\n#ifdef __gl_h_\n#error OpenGL header already included, remove this include, glad already provides it\n#endif\n#define __gl_h_\n\n#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)\n#define APIENTRY __stdcall\n#endif\n\n#ifndef APIENTRY\n#define APIENTRY\n#endif\n#ifndef APIENTRYP\n#define APIENTRYP APIENTRY *\n#endif\n\n#ifndef GLAPIENTRY\n#define GLAPIENTRY APIENTRY\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct gladGLversionStruct {\n    int major;\n    int minor;\n};\n\ntypedef void* (* GLADloadproc)(const char *name);\n\n#ifndef GLAPI\n# if defined(GLAD_GLAPI_EXPORT)\n#  if defined(_WIN32) || defined(__CYGWIN__)\n#   if defined(GLAD_GLAPI_EXPORT_BUILD)\n#    if defined(__GNUC__)\n#     define GLAPI __attribute__ ((dllexport)) extern\n#    else\n#     define GLAPI __declspec(dllexport) extern\n#    endif\n#   else\n#    if defined(__GNUC__)\n#     define GLAPI __attribute__ ((dllimport)) extern\n#    else\n#     define GLAPI __declspec(dllimport) extern\n#    endif\n#   endif\n#  elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)\n#   define GLAPI __attribute__ ((visibility (\"default\"))) extern\n#  else\n#   define GLAPI extern\n#  endif\n# else\n#  define GLAPI extern\n# endif\n#endif\n\nGLAPI struct gladGLversionStruct GLVersion;\n\nGLAPI int gladLoadGL(void);\n\nGLAPI int gladLoadGLLoader(GLADloadproc);\n\n#include <KHR/khrplatform.h>\ntypedef unsigned int GLenum;\ntypedef unsigned char GLboolean;\ntypedef unsigned int GLbitfield;\ntypedef void GLvoid;\ntypedef khronos_int8_t GLbyte;\ntypedef khronos_uint8_t GLubyte;\ntypedef khronos_int16_t GLshort;\ntypedef khronos_uint16_t GLushort;\ntypedef int GLint;\ntypedef unsigned int GLuint;\ntypedef khronos_int32_t GLclampx;\ntypedef int GLsizei;\ntypedef khronos_float_t GLfloat;\ntypedef khronos_float_t GLclampf;\ntypedef double GLdouble;\ntypedef double GLclampd;\ntypedef void *GLeglClientBufferEXT;\ntypedef void *GLeglImageOES;\ntypedef char GLchar;\ntypedef char GLcharARB;\n#ifdef __APPLE__\ntypedef void *GLhandleARB;\n#else\ntypedef unsigned int GLhandleARB;\n#endif\ntypedef khronos_uint16_t GLhalf;\ntypedef khronos_uint16_t GLhalfARB;\ntypedef khronos_int32_t GLfixed;\ntypedef khronos_intptr_t GLintptr;\ntypedef khronos_intptr_t GLintptrARB;\ntypedef khronos_ssize_t GLsizeiptr;\ntypedef khronos_ssize_t GLsizeiptrARB;\ntypedef khronos_int64_t GLint64;\ntypedef khronos_int64_t GLint64EXT;\ntypedef khronos_uint64_t GLuint64;\ntypedef khronos_uint64_t GLuint64EXT;\ntypedef struct __GLsync *GLsync;\nstruct _cl_context;\nstruct _cl_event;\ntypedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);\ntypedef unsigned short GLhalfNV;\ntypedef GLintptr GLvdpauSurfaceNV;\ntypedef void (APIENTRY *GLVULKANPROCNV)(void);\n#define GL_DEPTH_BUFFER_BIT 0x00000100\n#define GL_STENCIL_BUFFER_BIT 0x00000400\n#define GL_COLOR_BUFFER_BIT 0x00004000\n#define GL_FALSE 0\n#define GL_TRUE 1\n#define GL_POINTS 0x0000\n#define GL_LINES 0x0001\n#define GL_LINE_LOOP 0x0002\n#define GL_LINE_STRIP 0x0003\n#define GL_TRIANGLES 0x0004\n#define GL_TRIANGLE_STRIP 0x0005\n#define GL_TRIANGLE_FAN 0x0006\n#define GL_QUADS 0x0007\n#define GL_NEVER 0x0200\n#define GL_LESS 0x0201\n#define GL_EQUAL 0x0202\n#define GL_LEQUAL 0x0203\n#define GL_GREATER 0x0204\n#define GL_NOTEQUAL 0x0205\n#define GL_GEQUAL 0x0206\n#define GL_ALWAYS 0x0207\n#define GL_ZERO 0\n#define GL_ONE 1\n#define GL_SRC_COLOR 0x0300\n#define GL_ONE_MINUS_SRC_COLOR 0x0301\n#define GL_SRC_ALPHA 0x0302\n#define GL_ONE_MINUS_SRC_ALPHA 0x0303\n#define GL_DST_ALPHA 0x0304\n#define GL_ONE_MINUS_DST_ALPHA 0x0305\n#define GL_DST_COLOR 0x0306\n#define GL_ONE_MINUS_DST_COLOR 0x0307\n#define GL_SRC_ALPHA_SATURATE 0x0308\n#define GL_NONE 0\n#define GL_FRONT_LEFT 0x0400\n#define GL_FRONT_RIGHT 0x0401\n#define GL_BACK_LEFT 0x0402\n#define GL_BACK_RIGHT 0x0403\n#define GL_FRONT 0x0404\n#define GL_BACK 0x0405\n#define GL_LEFT 0x0406\n#define GL_RIGHT 0x0407\n#define GL_FRONT_AND_BACK 0x0408\n#define GL_NO_ERROR 0\n#define GL_INVALID_ENUM 0x0500\n#define GL_INVALID_VALUE 0x0501\n#define GL_INVALID_OPERATION 0x0502\n#define GL_OUT_OF_MEMORY 0x0505\n#define GL_CW 0x0900\n#define GL_CCW 0x0901\n#define GL_POINT_SIZE 0x0B11\n#define GL_POINT_SIZE_RANGE 0x0B12\n#define GL_POINT_SIZE_GRANULARITY 0x0B13\n#define GL_LINE_SMOOTH 0x0B20\n#define GL_LINE_WIDTH 0x0B21\n#define GL_LINE_WIDTH_RANGE 0x0B22\n#define GL_LINE_WIDTH_GRANULARITY 0x0B23\n#define GL_POLYGON_MODE 0x0B40\n#define GL_POLYGON_SMOOTH 0x0B41\n#define GL_CULL_FACE 0x0B44\n#define GL_CULL_FACE_MODE 0x0B45\n#define GL_FRONT_FACE 0x0B46\n#define GL_DEPTH_RANGE 0x0B70\n#define GL_DEPTH_TEST 0x0B71\n#define GL_DEPTH_WRITEMASK 0x0B72\n#define GL_DEPTH_CLEAR_VALUE 0x0B73\n#define GL_DEPTH_FUNC 0x0B74\n#define GL_STENCIL_TEST 0x0B90\n#define GL_STENCIL_CLEAR_VALUE 0x0B91\n#define GL_STENCIL_FUNC 0x0B92\n#define GL_STENCIL_VALUE_MASK 0x0B93\n#define GL_STENCIL_FAIL 0x0B94\n#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95\n#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96\n#define GL_STENCIL_REF 0x0B97\n#define GL_STENCIL_WRITEMASK 0x0B98\n#define GL_VIEWPORT 0x0BA2\n#define GL_DITHER 0x0BD0\n#define GL_BLEND_DST 0x0BE0\n#define GL_BLEND_SRC 0x0BE1\n#define GL_BLEND 0x0BE2\n#define GL_LOGIC_OP_MODE 0x0BF0\n#define GL_DRAW_BUFFER 0x0C01\n#define GL_READ_BUFFER 0x0C02\n#define GL_SCISSOR_BOX 0x0C10\n#define GL_SCISSOR_TEST 0x0C11\n#define GL_COLOR_CLEAR_VALUE 0x0C22\n#define GL_COLOR_WRITEMASK 0x0C23\n#define GL_DOUBLEBUFFER 0x0C32\n#define GL_STEREO 0x0C33\n#define GL_LINE_SMOOTH_HINT 0x0C52\n#define GL_POLYGON_SMOOTH_HINT 0x0C53\n#define GL_UNPACK_SWAP_BYTES 0x0CF0\n#define GL_UNPACK_LSB_FIRST 0x0CF1\n#define GL_UNPACK_ROW_LENGTH 0x0CF2\n#define GL_UNPACK_SKIP_ROWS 0x0CF3\n#define GL_UNPACK_SKIP_PIXELS 0x0CF4\n#define GL_UNPACK_ALIGNMENT 0x0CF5\n#define GL_PACK_SWAP_BYTES 0x0D00\n#define GL_PACK_LSB_FIRST 0x0D01\n#define GL_PACK_ROW_LENGTH 0x0D02\n#define GL_PACK_SKIP_ROWS 0x0D03\n#define GL_PACK_SKIP_PIXELS 0x0D04\n#define GL_PACK_ALIGNMENT 0x0D05\n#define GL_MAX_TEXTURE_SIZE 0x0D33\n#define GL_MAX_VIEWPORT_DIMS 0x0D3A\n#define GL_SUBPIXEL_BITS 0x0D50\n#define GL_TEXTURE_1D 0x0DE0\n#define GL_TEXTURE_2D 0x0DE1\n#define GL_TEXTURE_WIDTH 0x1000\n#define GL_TEXTURE_HEIGHT 0x1001\n#define GL_TEXTURE_BORDER_COLOR 0x1004\n#define GL_DONT_CARE 0x1100\n#define GL_FASTEST 0x1101\n#define GL_NICEST 0x1102\n#define GL_BYTE 0x1400\n#define GL_UNSIGNED_BYTE 0x1401\n#define GL_SHORT 0x1402\n#define GL_UNSIGNED_SHORT 0x1403\n#define GL_INT 0x1404\n#define GL_UNSIGNED_INT 0x1405\n#define GL_FLOAT 0x1406\n#define GL_STACK_OVERFLOW 0x0503\n#define GL_STACK_UNDERFLOW 0x0504\n#define GL_CLEAR 0x1500\n#define GL_AND 0x1501\n#define GL_AND_REVERSE 0x1502\n#define GL_COPY 0x1503\n#define GL_AND_INVERTED 0x1504\n#define GL_NOOP 0x1505\n#define GL_XOR 0x1506\n#define GL_OR 0x1507\n#define GL_NOR 0x1508\n#define GL_EQUIV 0x1509\n#define GL_INVERT 0x150A\n#define GL_OR_REVERSE 0x150B\n#define GL_COPY_INVERTED 0x150C\n#define GL_OR_INVERTED 0x150D\n#define GL_NAND 0x150E\n#define GL_SET 0x150F\n#define GL_TEXTURE 0x1702\n#define GL_COLOR 0x1800\n#define GL_DEPTH 0x1801\n#define GL_STENCIL 0x1802\n#define GL_STENCIL_INDEX 0x1901\n#define GL_DEPTH_COMPONENT 0x1902\n#define GL_RED 0x1903\n#define GL_GREEN 0x1904\n#define GL_BLUE 0x1905\n#define GL_ALPHA 0x1906\n#define GL_RGB 0x1907\n#define GL_RGBA 0x1908\n#define GL_POINT 0x1B00\n#define GL_LINE 0x1B01\n#define GL_FILL 0x1B02\n#define GL_KEEP 0x1E00\n#define GL_REPLACE 0x1E01\n#define GL_INCR 0x1E02\n#define GL_DECR 0x1E03\n#define GL_VENDOR 0x1F00\n#define GL_RENDERER 0x1F01\n#define GL_VERSION 0x1F02\n#define GL_EXTENSIONS 0x1F03\n#define GL_NEAREST 0x2600\n#define GL_LINEAR 0x2601\n#define GL_NEAREST_MIPMAP_NEAREST 0x2700\n#define GL_LINEAR_MIPMAP_NEAREST 0x2701\n#define GL_NEAREST_MIPMAP_LINEAR 0x2702\n#define GL_LINEAR_MIPMAP_LINEAR 0x2703\n#define GL_TEXTURE_MAG_FILTER 0x2800\n#define GL_TEXTURE_MIN_FILTER 0x2801\n#define GL_TEXTURE_WRAP_S 0x2802\n#define GL_TEXTURE_WRAP_T 0x2803\n#define GL_REPEAT 0x2901\n#define GL_CURRENT_BIT 0x00000001\n#define GL_POINT_BIT 0x00000002\n#define GL_LINE_BIT 0x00000004\n#define GL_POLYGON_BIT 0x00000008\n#define GL_POLYGON_STIPPLE_BIT 0x00000010\n#define GL_PIXEL_MODE_BIT 0x00000020\n#define GL_LIGHTING_BIT 0x00000040\n#define GL_FOG_BIT 0x00000080\n#define GL_ACCUM_BUFFER_BIT 0x00000200\n#define GL_VIEWPORT_BIT 0x00000800\n#define GL_TRANSFORM_BIT 0x00001000\n#define GL_ENABLE_BIT 0x00002000\n#define GL_HINT_BIT 0x00008000\n#define GL_EVAL_BIT 0x00010000\n#define GL_LIST_BIT 0x00020000\n#define GL_TEXTURE_BIT 0x00040000\n#define GL_SCISSOR_BIT 0x00080000\n#define GL_ALL_ATTRIB_BITS 0xFFFFFFFF\n#define GL_QUAD_STRIP 0x0008\n#define GL_POLYGON 0x0009\n#define GL_ACCUM 0x0100\n#define GL_LOAD 0x0101\n#define GL_RETURN 0x0102\n#define GL_MULT 0x0103\n#define GL_ADD 0x0104\n#define GL_AUX0 0x0409\n#define GL_AUX1 0x040A\n#define GL_AUX2 0x040B\n#define GL_AUX3 0x040C\n#define GL_2D 0x0600\n#define GL_3D 0x0601\n#define GL_3D_COLOR 0x0602\n#define GL_3D_COLOR_TEXTURE 0x0603\n#define GL_4D_COLOR_TEXTURE 0x0604\n#define GL_PASS_THROUGH_TOKEN 0x0700\n#define GL_POINT_TOKEN 0x0701\n#define GL_LINE_TOKEN 0x0702\n#define GL_POLYGON_TOKEN 0x0703\n#define GL_BITMAP_TOKEN 0x0704\n#define GL_DRAW_PIXEL_TOKEN 0x0705\n#define GL_COPY_PIXEL_TOKEN 0x0706\n#define GL_LINE_RESET_TOKEN 0x0707\n#define GL_EXP 0x0800\n#define GL_EXP2 0x0801\n#define GL_COEFF 0x0A00\n#define GL_ORDER 0x0A01\n#define GL_DOMAIN 0x0A02\n#define GL_PIXEL_MAP_I_TO_I 0x0C70\n#define GL_PIXEL_MAP_S_TO_S 0x0C71\n#define GL_PIXEL_MAP_I_TO_R 0x0C72\n#define GL_PIXEL_MAP_I_TO_G 0x0C73\n#define GL_PIXEL_MAP_I_TO_B 0x0C74\n#define GL_PIXEL_MAP_I_TO_A 0x0C75\n#define GL_PIXEL_MAP_R_TO_R 0x0C76\n#define GL_PIXEL_MAP_G_TO_G 0x0C77\n#define GL_PIXEL_MAP_B_TO_B 0x0C78\n#define GL_PIXEL_MAP_A_TO_A 0x0C79\n#define GL_CURRENT_COLOR 0x0B00\n#define GL_CURRENT_INDEX 0x0B01\n#define GL_CURRENT_NORMAL 0x0B02\n#define GL_CURRENT_TEXTURE_COORDS 0x0B03\n#define GL_CURRENT_RASTER_COLOR 0x0B04\n#define GL_CURRENT_RASTER_INDEX 0x0B05\n#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06\n#define GL_CURRENT_RASTER_POSITION 0x0B07\n#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08\n#define GL_CURRENT_RASTER_DISTANCE 0x0B09\n#define GL_POINT_SMOOTH 0x0B10\n#define GL_LINE_STIPPLE 0x0B24\n#define GL_LINE_STIPPLE_PATTERN 0x0B25\n#define GL_LINE_STIPPLE_REPEAT 0x0B26\n#define GL_LIST_MODE 0x0B30\n#define GL_MAX_LIST_NESTING 0x0B31\n#define GL_LIST_BASE 0x0B32\n#define GL_LIST_INDEX 0x0B33\n#define GL_POLYGON_STIPPLE 0x0B42\n#define GL_EDGE_FLAG 0x0B43\n#define GL_LIGHTING 0x0B50\n#define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51\n#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52\n#define GL_LIGHT_MODEL_AMBIENT 0x0B53\n#define GL_SHADE_MODEL 0x0B54\n#define GL_COLOR_MATERIAL_FACE 0x0B55\n#define GL_COLOR_MATERIAL_PARAMETER 0x0B56\n#define GL_COLOR_MATERIAL 0x0B57\n#define GL_FOG 0x0B60\n#define GL_FOG_INDEX 0x0B61\n#define GL_FOG_DENSITY 0x0B62\n#define GL_FOG_START 0x0B63\n#define GL_FOG_END 0x0B64\n#define GL_FOG_MODE 0x0B65\n#define GL_FOG_COLOR 0x0B66\n#define GL_ACCUM_CLEAR_VALUE 0x0B80\n#define GL_MATRIX_MODE 0x0BA0\n#define GL_NORMALIZE 0x0BA1\n#define GL_MODELVIEW_STACK_DEPTH 0x0BA3\n#define GL_PROJECTION_STACK_DEPTH 0x0BA4\n#define GL_TEXTURE_STACK_DEPTH 0x0BA5\n#define GL_MODELVIEW_MATRIX 0x0BA6\n#define GL_PROJECTION_MATRIX 0x0BA7\n#define GL_TEXTURE_MATRIX 0x0BA8\n#define GL_ATTRIB_STACK_DEPTH 0x0BB0\n#define GL_ALPHA_TEST 0x0BC0\n#define GL_ALPHA_TEST_FUNC 0x0BC1\n#define GL_ALPHA_TEST_REF 0x0BC2\n#define GL_LOGIC_OP 0x0BF1\n#define GL_AUX_BUFFERS 0x0C00\n#define GL_INDEX_CLEAR_VALUE 0x0C20\n#define GL_INDEX_WRITEMASK 0x0C21\n#define GL_INDEX_MODE 0x0C30\n#define GL_RGBA_MODE 0x0C31\n#define GL_RENDER_MODE 0x0C40\n#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50\n#define GL_POINT_SMOOTH_HINT 0x0C51\n#define GL_FOG_HINT 0x0C54\n#define GL_TEXTURE_GEN_S 0x0C60\n#define GL_TEXTURE_GEN_T 0x0C61\n#define GL_TEXTURE_GEN_R 0x0C62\n#define GL_TEXTURE_GEN_Q 0x0C63\n#define GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0\n#define GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1\n#define GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2\n#define GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3\n#define GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4\n#define GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5\n#define GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6\n#define GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7\n#define GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8\n#define GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9\n#define GL_MAP_COLOR 0x0D10\n#define GL_MAP_STENCIL 0x0D11\n#define GL_INDEX_SHIFT 0x0D12\n#define GL_INDEX_OFFSET 0x0D13\n#define GL_RED_SCALE 0x0D14\n#define GL_RED_BIAS 0x0D15\n#define GL_ZOOM_X 0x0D16\n#define GL_ZOOM_Y 0x0D17\n#define GL_GREEN_SCALE 0x0D18\n#define GL_GREEN_BIAS 0x0D19\n#define GL_BLUE_SCALE 0x0D1A\n#define GL_BLUE_BIAS 0x0D1B\n#define GL_ALPHA_SCALE 0x0D1C\n#define GL_ALPHA_BIAS 0x0D1D\n#define GL_DEPTH_SCALE 0x0D1E\n#define GL_DEPTH_BIAS 0x0D1F\n#define GL_MAX_EVAL_ORDER 0x0D30\n#define GL_MAX_LIGHTS 0x0D31\n#define GL_MAX_CLIP_PLANES 0x0D32\n#define GL_MAX_PIXEL_MAP_TABLE 0x0D34\n#define GL_MAX_ATTRIB_STACK_DEPTH 0x0D35\n#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36\n#define GL_MAX_NAME_STACK_DEPTH 0x0D37\n#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38\n#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39\n#define GL_INDEX_BITS 0x0D51\n#define GL_RED_BITS 0x0D52\n#define GL_GREEN_BITS 0x0D53\n#define GL_BLUE_BITS 0x0D54\n#define GL_ALPHA_BITS 0x0D55\n#define GL_DEPTH_BITS 0x0D56\n#define GL_STENCIL_BITS 0x0D57\n#define GL_ACCUM_RED_BITS 0x0D58\n#define GL_ACCUM_GREEN_BITS 0x0D59\n#define GL_ACCUM_BLUE_BITS 0x0D5A\n#define GL_ACCUM_ALPHA_BITS 0x0D5B\n#define GL_NAME_STACK_DEPTH 0x0D70\n#define GL_AUTO_NORMAL 0x0D80\n#define GL_MAP1_COLOR_4 0x0D90\n#define GL_MAP1_INDEX 0x0D91\n#define GL_MAP1_NORMAL 0x0D92\n#define GL_MAP1_TEXTURE_COORD_1 0x0D93\n#define GL_MAP1_TEXTURE_COORD_2 0x0D94\n#define GL_MAP1_TEXTURE_COORD_3 0x0D95\n#define GL_MAP1_TEXTURE_COORD_4 0x0D96\n#define GL_MAP1_VERTEX_3 0x0D97\n#define GL_MAP1_VERTEX_4 0x0D98\n#define GL_MAP2_COLOR_4 0x0DB0\n#define GL_MAP2_INDEX 0x0DB1\n#define GL_MAP2_NORMAL 0x0DB2\n#define GL_MAP2_TEXTURE_COORD_1 0x0DB3\n#define GL_MAP2_TEXTURE_COORD_2 0x0DB4\n#define GL_MAP2_TEXTURE_COORD_3 0x0DB5\n#define GL_MAP2_TEXTURE_COORD_4 0x0DB6\n#define GL_MAP2_VERTEX_3 0x0DB7\n#define GL_MAP2_VERTEX_4 0x0DB8\n#define GL_MAP1_GRID_DOMAIN 0x0DD0\n#define GL_MAP1_GRID_SEGMENTS 0x0DD1\n#define GL_MAP2_GRID_DOMAIN 0x0DD2\n#define GL_MAP2_GRID_SEGMENTS 0x0DD3\n#define GL_TEXTURE_COMPONENTS 0x1003\n#define GL_TEXTURE_BORDER 0x1005\n#define GL_AMBIENT 0x1200\n#define GL_DIFFUSE 0x1201\n#define GL_SPECULAR 0x1202\n#define GL_POSITION 0x1203\n#define GL_SPOT_DIRECTION 0x1204\n#define GL_SPOT_EXPONENT 0x1205\n#define GL_SPOT_CUTOFF 0x1206\n#define GL_CONSTANT_ATTENUATION 0x1207\n#define GL_LINEAR_ATTENUATION 0x1208\n#define GL_QUADRATIC_ATTENUATION 0x1209\n#define GL_COMPILE 0x1300\n#define GL_COMPILE_AND_EXECUTE 0x1301\n#define GL_2_BYTES 0x1407\n#define GL_3_BYTES 0x1408\n#define GL_4_BYTES 0x1409\n#define GL_EMISSION 0x1600\n#define GL_SHININESS 0x1601\n#define GL_AMBIENT_AND_DIFFUSE 0x1602\n#define GL_COLOR_INDEXES 0x1603\n#define GL_MODELVIEW 0x1700\n#define GL_PROJECTION 0x1701\n#define GL_COLOR_INDEX 0x1900\n#define GL_LUMINANCE 0x1909\n#define GL_LUMINANCE_ALPHA 0x190A\n#define GL_BITMAP 0x1A00\n#define GL_RENDER 0x1C00\n#define GL_FEEDBACK 0x1C01\n#define GL_SELECT 0x1C02\n#define GL_FLAT 0x1D00\n#define GL_SMOOTH 0x1D01\n#define GL_S 0x2000\n#define GL_T 0x2001\n#define GL_R 0x2002\n#define GL_Q 0x2003\n#define GL_MODULATE 0x2100\n#define GL_DECAL 0x2101\n#define GL_TEXTURE_ENV_MODE 0x2200\n#define GL_TEXTURE_ENV_COLOR 0x2201\n#define GL_TEXTURE_ENV 0x2300\n#define GL_EYE_LINEAR 0x2400\n#define GL_OBJECT_LINEAR 0x2401\n#define GL_SPHERE_MAP 0x2402\n#define GL_TEXTURE_GEN_MODE 0x2500\n#define GL_OBJECT_PLANE 0x2501\n#define GL_EYE_PLANE 0x2502\n#define GL_CLAMP 0x2900\n#define GL_CLIP_PLANE0 0x3000\n#define GL_CLIP_PLANE1 0x3001\n#define GL_CLIP_PLANE2 0x3002\n#define GL_CLIP_PLANE3 0x3003\n#define GL_CLIP_PLANE4 0x3004\n#define GL_CLIP_PLANE5 0x3005\n#define GL_LIGHT0 0x4000\n#define GL_LIGHT1 0x4001\n#define GL_LIGHT2 0x4002\n#define GL_LIGHT3 0x4003\n#define GL_LIGHT4 0x4004\n#define GL_LIGHT5 0x4005\n#define GL_LIGHT6 0x4006\n#define GL_LIGHT7 0x4007\n#define GL_COLOR_LOGIC_OP 0x0BF2\n#define GL_POLYGON_OFFSET_UNITS 0x2A00\n#define GL_POLYGON_OFFSET_POINT 0x2A01\n#define GL_POLYGON_OFFSET_LINE 0x2A02\n#define GL_POLYGON_OFFSET_FILL 0x8037\n#define GL_POLYGON_OFFSET_FACTOR 0x8038\n#define GL_TEXTURE_BINDING_1D 0x8068\n#define GL_TEXTURE_BINDING_2D 0x8069\n#define GL_TEXTURE_INTERNAL_FORMAT 0x1003\n#define GL_TEXTURE_RED_SIZE 0x805C\n#define GL_TEXTURE_GREEN_SIZE 0x805D\n#define GL_TEXTURE_BLUE_SIZE 0x805E\n#define GL_TEXTURE_ALPHA_SIZE 0x805F\n#define GL_DOUBLE 0x140A\n#define GL_PROXY_TEXTURE_1D 0x8063\n#define GL_PROXY_TEXTURE_2D 0x8064\n#define GL_R3_G3_B2 0x2A10\n#define GL_RGB4 0x804F\n#define GL_RGB5 0x8050\n#define GL_RGB8 0x8051\n#define GL_RGB10 0x8052\n#define GL_RGB12 0x8053\n#define GL_RGB16 0x8054\n#define GL_RGBA2 0x8055\n#define GL_RGBA4 0x8056\n#define GL_RGB5_A1 0x8057\n#define GL_RGBA8 0x8058\n#define GL_RGB10_A2 0x8059\n#define GL_RGBA12 0x805A\n#define GL_RGBA16 0x805B\n#define GL_CLIENT_PIXEL_STORE_BIT 0x00000001\n#define GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002\n#define GL_CLIENT_ALL_ATTRIB_BITS 0xFFFFFFFF\n#define GL_VERTEX_ARRAY_POINTER 0x808E\n#define GL_NORMAL_ARRAY_POINTER 0x808F\n#define GL_COLOR_ARRAY_POINTER 0x8090\n#define GL_INDEX_ARRAY_POINTER 0x8091\n#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092\n#define GL_EDGE_FLAG_ARRAY_POINTER 0x8093\n#define GL_FEEDBACK_BUFFER_POINTER 0x0DF0\n#define GL_SELECTION_BUFFER_POINTER 0x0DF3\n#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1\n#define GL_INDEX_LOGIC_OP 0x0BF1\n#define GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B\n#define GL_FEEDBACK_BUFFER_SIZE 0x0DF1\n#define GL_FEEDBACK_BUFFER_TYPE 0x0DF2\n#define GL_SELECTION_BUFFER_SIZE 0x0DF4\n#define GL_VERTEX_ARRAY 0x8074\n#define GL_NORMAL_ARRAY 0x8075\n#define GL_COLOR_ARRAY 0x8076\n#define GL_INDEX_ARRAY 0x8077\n#define GL_TEXTURE_COORD_ARRAY 0x8078\n#define GL_EDGE_FLAG_ARRAY 0x8079\n#define GL_VERTEX_ARRAY_SIZE 0x807A\n#define GL_VERTEX_ARRAY_TYPE 0x807B\n#define GL_VERTEX_ARRAY_STRIDE 0x807C\n#define GL_NORMAL_ARRAY_TYPE 0x807E\n#define GL_NORMAL_ARRAY_STRIDE 0x807F\n#define GL_COLOR_ARRAY_SIZE 0x8081\n#define GL_COLOR_ARRAY_TYPE 0x8082\n#define GL_COLOR_ARRAY_STRIDE 0x8083\n#define GL_INDEX_ARRAY_TYPE 0x8085\n#define GL_INDEX_ARRAY_STRIDE 0x8086\n#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088\n#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089\n#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A\n#define GL_EDGE_FLAG_ARRAY_STRIDE 0x808C\n#define GL_TEXTURE_LUMINANCE_SIZE 0x8060\n#define GL_TEXTURE_INTENSITY_SIZE 0x8061\n#define GL_TEXTURE_PRIORITY 0x8066\n#define GL_TEXTURE_RESIDENT 0x8067\n#define GL_ALPHA4 0x803B\n#define GL_ALPHA8 0x803C\n#define GL_ALPHA12 0x803D\n#define GL_ALPHA16 0x803E\n#define GL_LUMINANCE4 0x803F\n#define GL_LUMINANCE8 0x8040\n#define GL_LUMINANCE12 0x8041\n#define GL_LUMINANCE16 0x8042\n#define GL_LUMINANCE4_ALPHA4 0x8043\n#define GL_LUMINANCE6_ALPHA2 0x8044\n#define GL_LUMINANCE8_ALPHA8 0x8045\n#define GL_LUMINANCE12_ALPHA4 0x8046\n#define GL_LUMINANCE12_ALPHA12 0x8047\n#define GL_LUMINANCE16_ALPHA16 0x8048\n#define GL_INTENSITY 0x8049\n#define GL_INTENSITY4 0x804A\n#define GL_INTENSITY8 0x804B\n#define GL_INTENSITY12 0x804C\n#define GL_INTENSITY16 0x804D\n#define GL_V2F 0x2A20\n#define GL_V3F 0x2A21\n#define GL_C4UB_V2F 0x2A22\n#define GL_C4UB_V3F 0x2A23\n#define GL_C3F_V3F 0x2A24\n#define GL_N3F_V3F 0x2A25\n#define GL_C4F_N3F_V3F 0x2A26\n#define GL_T2F_V3F 0x2A27\n#define GL_T4F_V4F 0x2A28\n#define GL_T2F_C4UB_V3F 0x2A29\n#define GL_T2F_C3F_V3F 0x2A2A\n#define GL_T2F_N3F_V3F 0x2A2B\n#define GL_T2F_C4F_N3F_V3F 0x2A2C\n#define GL_T4F_C4F_N3F_V4F 0x2A2D\n#define GL_UNSIGNED_BYTE_3_3_2 0x8032\n#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033\n#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034\n#define GL_UNSIGNED_INT_8_8_8_8 0x8035\n#define GL_UNSIGNED_INT_10_10_10_2 0x8036\n#define GL_TEXTURE_BINDING_3D 0x806A\n#define GL_PACK_SKIP_IMAGES 0x806B\n#define GL_PACK_IMAGE_HEIGHT 0x806C\n#define GL_UNPACK_SKIP_IMAGES 0x806D\n#define GL_UNPACK_IMAGE_HEIGHT 0x806E\n#define GL_TEXTURE_3D 0x806F\n#define GL_PROXY_TEXTURE_3D 0x8070\n#define GL_TEXTURE_DEPTH 0x8071\n#define GL_TEXTURE_WRAP_R 0x8072\n#define GL_MAX_3D_TEXTURE_SIZE 0x8073\n#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362\n#define GL_UNSIGNED_SHORT_5_6_5 0x8363\n#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364\n#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365\n#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366\n#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367\n#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368\n#define GL_BGR 0x80E0\n#define GL_BGRA 0x80E1\n#define GL_MAX_ELEMENTS_VERTICES 0x80E8\n#define GL_MAX_ELEMENTS_INDICES 0x80E9\n#define GL_CLAMP_TO_EDGE 0x812F\n#define GL_TEXTURE_MIN_LOD 0x813A\n#define GL_TEXTURE_MAX_LOD 0x813B\n#define GL_TEXTURE_BASE_LEVEL 0x813C\n#define GL_TEXTURE_MAX_LEVEL 0x813D\n#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12\n#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13\n#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22\n#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23\n#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E\n#define GL_RESCALE_NORMAL 0x803A\n#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8\n#define GL_SINGLE_COLOR 0x81F9\n#define GL_SEPARATE_SPECULAR_COLOR 0x81FA\n#define GL_ALIASED_POINT_SIZE_RANGE 0x846D\n#define GL_TEXTURE0 0x84C0\n#define GL_TEXTURE1 0x84C1\n#define GL_TEXTURE2 0x84C2\n#define GL_TEXTURE3 0x84C3\n#define GL_TEXTURE4 0x84C4\n#define GL_TEXTURE5 0x84C5\n#define GL_TEXTURE6 0x84C6\n#define GL_TEXTURE7 0x84C7\n#define GL_TEXTURE8 0x84C8\n#define GL_TEXTURE9 0x84C9\n#define GL_TEXTURE10 0x84CA\n#define GL_TEXTURE11 0x84CB\n#define GL_TEXTURE12 0x84CC\n#define GL_TEXTURE13 0x84CD\n#define GL_TEXTURE14 0x84CE\n#define GL_TEXTURE15 0x84CF\n#define GL_TEXTURE16 0x84D0\n#define GL_TEXTURE17 0x84D1\n#define GL_TEXTURE18 0x84D2\n#define GL_TEXTURE19 0x84D3\n#define GL_TEXTURE20 0x84D4\n#define GL_TEXTURE21 0x84D5\n#define GL_TEXTURE22 0x84D6\n#define GL_TEXTURE23 0x84D7\n#define GL_TEXTURE24 0x84D8\n#define GL_TEXTURE25 0x84D9\n#define GL_TEXTURE26 0x84DA\n#define GL_TEXTURE27 0x84DB\n#define GL_TEXTURE28 0x84DC\n#define GL_TEXTURE29 0x84DD\n#define GL_TEXTURE30 0x84DE\n#define GL_TEXTURE31 0x84DF\n#define GL_ACTIVE_TEXTURE 0x84E0\n#define GL_MULTISAMPLE 0x809D\n#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E\n#define GL_SAMPLE_ALPHA_TO_ONE 0x809F\n#define GL_SAMPLE_COVERAGE 0x80A0\n#define GL_SAMPLE_BUFFERS 0x80A8\n#define GL_SAMPLES 0x80A9\n#define GL_SAMPLE_COVERAGE_VALUE 0x80AA\n#define GL_SAMPLE_COVERAGE_INVERT 0x80AB\n#define GL_TEXTURE_CUBE_MAP 0x8513\n#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A\n#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B\n#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C\n#define GL_COMPRESSED_RGB 0x84ED\n#define GL_COMPRESSED_RGBA 0x84EE\n#define GL_TEXTURE_COMPRESSION_HINT 0x84EF\n#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0\n#define GL_TEXTURE_COMPRESSED 0x86A1\n#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2\n#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3\n#define GL_CLAMP_TO_BORDER 0x812D\n#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1\n#define GL_MAX_TEXTURE_UNITS 0x84E2\n#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3\n#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4\n#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5\n#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6\n#define GL_MULTISAMPLE_BIT 0x20000000\n#define GL_NORMAL_MAP 0x8511\n#define GL_REFLECTION_MAP 0x8512\n#define GL_COMPRESSED_ALPHA 0x84E9\n#define GL_COMPRESSED_LUMINANCE 0x84EA\n#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB\n#define GL_COMPRESSED_INTENSITY 0x84EC\n#define GL_COMBINE 0x8570\n#define GL_COMBINE_RGB 0x8571\n#define GL_COMBINE_ALPHA 0x8572\n#define GL_SOURCE0_RGB 0x8580\n#define GL_SOURCE1_RGB 0x8581\n#define GL_SOURCE2_RGB 0x8582\n#define GL_SOURCE0_ALPHA 0x8588\n#define GL_SOURCE1_ALPHA 0x8589\n#define GL_SOURCE2_ALPHA 0x858A\n#define GL_OPERAND0_RGB 0x8590\n#define GL_OPERAND1_RGB 0x8591\n#define GL_OPERAND2_RGB 0x8592\n#define GL_OPERAND0_ALPHA 0x8598\n#define GL_OPERAND1_ALPHA 0x8599\n#define GL_OPERAND2_ALPHA 0x859A\n#define GL_RGB_SCALE 0x8573\n#define GL_ADD_SIGNED 0x8574\n#define GL_INTERPOLATE 0x8575\n#define GL_SUBTRACT 0x84E7\n#define GL_CONSTANT 0x8576\n#define GL_PRIMARY_COLOR 0x8577\n#define GL_PREVIOUS 0x8578\n#define GL_DOT3_RGB 0x86AE\n#define GL_DOT3_RGBA 0x86AF\n#define GL_BLEND_DST_RGB 0x80C8\n#define GL_BLEND_SRC_RGB 0x80C9\n#define GL_BLEND_DST_ALPHA 0x80CA\n#define GL_BLEND_SRC_ALPHA 0x80CB\n#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128\n#define GL_DEPTH_COMPONENT16 0x81A5\n#define GL_DEPTH_COMPONENT24 0x81A6\n#define GL_DEPTH_COMPONENT32 0x81A7\n#define GL_MIRRORED_REPEAT 0x8370\n#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD\n#define GL_TEXTURE_LOD_BIAS 0x8501\n#define GL_INCR_WRAP 0x8507\n#define GL_DECR_WRAP 0x8508\n#define GL_TEXTURE_DEPTH_SIZE 0x884A\n#define GL_TEXTURE_COMPARE_MODE 0x884C\n#define GL_TEXTURE_COMPARE_FUNC 0x884D\n#define GL_POINT_SIZE_MIN 0x8126\n#define GL_POINT_SIZE_MAX 0x8127\n#define GL_POINT_DISTANCE_ATTENUATION 0x8129\n#define GL_GENERATE_MIPMAP 0x8191\n#define GL_GENERATE_MIPMAP_HINT 0x8192\n#define GL_FOG_COORDINATE_SOURCE 0x8450\n#define GL_FOG_COORDINATE 0x8451\n#define GL_FRAGMENT_DEPTH 0x8452\n#define GL_CURRENT_FOG_COORDINATE 0x8453\n#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454\n#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455\n#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456\n#define GL_FOG_COORDINATE_ARRAY 0x8457\n#define GL_COLOR_SUM 0x8458\n#define GL_CURRENT_SECONDARY_COLOR 0x8459\n#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A\n#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B\n#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C\n#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D\n#define GL_SECONDARY_COLOR_ARRAY 0x845E\n#define GL_TEXTURE_FILTER_CONTROL 0x8500\n#define GL_DEPTH_TEXTURE_MODE 0x884B\n#define GL_COMPARE_R_TO_TEXTURE 0x884E\n#define GL_BLEND_COLOR 0x8005\n#define GL_BLEND_EQUATION 0x8009\n#define GL_CONSTANT_COLOR 0x8001\n#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002\n#define GL_CONSTANT_ALPHA 0x8003\n#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004\n#define GL_FUNC_ADD 0x8006\n#define GL_FUNC_REVERSE_SUBTRACT 0x800B\n#define GL_FUNC_SUBTRACT 0x800A\n#define GL_MIN 0x8007\n#define GL_MAX 0x8008\n#define GL_BUFFER_SIZE 0x8764\n#define GL_BUFFER_USAGE 0x8765\n#define GL_QUERY_COUNTER_BITS 0x8864\n#define GL_CURRENT_QUERY 0x8865\n#define GL_QUERY_RESULT 0x8866\n#define GL_QUERY_RESULT_AVAILABLE 0x8867\n#define GL_ARRAY_BUFFER 0x8892\n#define GL_ELEMENT_ARRAY_BUFFER 0x8893\n#define GL_ARRAY_BUFFER_BINDING 0x8894\n#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895\n#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F\n#define GL_READ_ONLY 0x88B8\n#define GL_WRITE_ONLY 0x88B9\n#define GL_READ_WRITE 0x88BA\n#define GL_BUFFER_ACCESS 0x88BB\n#define GL_BUFFER_MAPPED 0x88BC\n#define GL_BUFFER_MAP_POINTER 0x88BD\n#define GL_STREAM_DRAW 0x88E0\n#define GL_STREAM_READ 0x88E1\n#define GL_STREAM_COPY 0x88E2\n#define GL_STATIC_DRAW 0x88E4\n#define GL_STATIC_READ 0x88E5\n#define GL_STATIC_COPY 0x88E6\n#define GL_DYNAMIC_DRAW 0x88E8\n#define GL_DYNAMIC_READ 0x88E9\n#define GL_DYNAMIC_COPY 0x88EA\n#define GL_SAMPLES_PASSED 0x8914\n#define GL_SRC1_ALPHA 0x8589\n#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896\n#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897\n#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898\n#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899\n#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A\n#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B\n#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C\n#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D\n#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E\n#define GL_FOG_COORD_SRC 0x8450\n#define GL_FOG_COORD 0x8451\n#define GL_CURRENT_FOG_COORD 0x8453\n#define GL_FOG_COORD_ARRAY_TYPE 0x8454\n#define GL_FOG_COORD_ARRAY_STRIDE 0x8455\n#define GL_FOG_COORD_ARRAY_POINTER 0x8456\n#define GL_FOG_COORD_ARRAY 0x8457\n#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D\n#define GL_SRC0_RGB 0x8580\n#define GL_SRC1_RGB 0x8581\n#define GL_SRC2_RGB 0x8582\n#define GL_SRC0_ALPHA 0x8588\n#define GL_SRC2_ALPHA 0x858A\n#define GL_BLEND_EQUATION_RGB 0x8009\n#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622\n#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623\n#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624\n#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625\n#define GL_CURRENT_VERTEX_ATTRIB 0x8626\n#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642\n#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645\n#define GL_STENCIL_BACK_FUNC 0x8800\n#define GL_STENCIL_BACK_FAIL 0x8801\n#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802\n#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803\n#define GL_MAX_DRAW_BUFFERS 0x8824\n#define GL_DRAW_BUFFER0 0x8825\n#define GL_DRAW_BUFFER1 0x8826\n#define GL_DRAW_BUFFER2 0x8827\n#define GL_DRAW_BUFFER3 0x8828\n#define GL_DRAW_BUFFER4 0x8829\n#define GL_DRAW_BUFFER5 0x882A\n#define GL_DRAW_BUFFER6 0x882B\n#define GL_DRAW_BUFFER7 0x882C\n#define GL_DRAW_BUFFER8 0x882D\n#define GL_DRAW_BUFFER9 0x882E\n#define GL_DRAW_BUFFER10 0x882F\n#define GL_DRAW_BUFFER11 0x8830\n#define GL_DRAW_BUFFER12 0x8831\n#define GL_DRAW_BUFFER13 0x8832\n#define GL_DRAW_BUFFER14 0x8833\n#define GL_DRAW_BUFFER15 0x8834\n#define GL_BLEND_EQUATION_ALPHA 0x883D\n#define GL_MAX_VERTEX_ATTRIBS 0x8869\n#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A\n#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872\n#define GL_FRAGMENT_SHADER 0x8B30\n#define GL_VERTEX_SHADER 0x8B31\n#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49\n#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A\n#define GL_MAX_VARYING_FLOATS 0x8B4B\n#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C\n#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D\n#define GL_SHADER_TYPE 0x8B4F\n#define GL_FLOAT_VEC2 0x8B50\n#define GL_FLOAT_VEC3 0x8B51\n#define GL_FLOAT_VEC4 0x8B52\n#define GL_INT_VEC2 0x8B53\n#define GL_INT_VEC3 0x8B54\n#define GL_INT_VEC4 0x8B55\n#define GL_BOOL 0x8B56\n#define GL_BOOL_VEC2 0x8B57\n#define GL_BOOL_VEC3 0x8B58\n#define GL_BOOL_VEC4 0x8B59\n#define GL_FLOAT_MAT2 0x8B5A\n#define GL_FLOAT_MAT3 0x8B5B\n#define GL_FLOAT_MAT4 0x8B5C\n#define GL_SAMPLER_1D 0x8B5D\n#define GL_SAMPLER_2D 0x8B5E\n#define GL_SAMPLER_3D 0x8B5F\n#define GL_SAMPLER_CUBE 0x8B60\n#define GL_SAMPLER_1D_SHADOW 0x8B61\n#define GL_SAMPLER_2D_SHADOW 0x8B62\n#define GL_DELETE_STATUS 0x8B80\n#define GL_COMPILE_STATUS 0x8B81\n#define GL_LINK_STATUS 0x8B82\n#define GL_VALIDATE_STATUS 0x8B83\n#define GL_INFO_LOG_LENGTH 0x8B84\n#define GL_ATTACHED_SHADERS 0x8B85\n#define GL_ACTIVE_UNIFORMS 0x8B86\n#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87\n#define GL_SHADER_SOURCE_LENGTH 0x8B88\n#define GL_ACTIVE_ATTRIBUTES 0x8B89\n#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A\n#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B\n#define GL_SHADING_LANGUAGE_VERSION 0x8B8C\n#define GL_CURRENT_PROGRAM 0x8B8D\n#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0\n#define GL_LOWER_LEFT 0x8CA1\n#define GL_UPPER_LEFT 0x8CA2\n#define GL_STENCIL_BACK_REF 0x8CA3\n#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4\n#define GL_STENCIL_BACK_WRITEMASK 0x8CA5\n#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643\n#define GL_POINT_SPRITE 0x8861\n#define GL_COORD_REPLACE 0x8862\n#define GL_MAX_TEXTURE_COORDS 0x8871\n#define GL_PIXEL_PACK_BUFFER 0x88EB\n#define GL_PIXEL_UNPACK_BUFFER 0x88EC\n#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED\n#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF\n#define GL_FLOAT_MAT2x3 0x8B65\n#define GL_FLOAT_MAT2x4 0x8B66\n#define GL_FLOAT_MAT3x2 0x8B67\n#define GL_FLOAT_MAT3x4 0x8B68\n#define GL_FLOAT_MAT4x2 0x8B69\n#define GL_FLOAT_MAT4x3 0x8B6A\n#define GL_SRGB 0x8C40\n#define GL_SRGB8 0x8C41\n#define GL_SRGB_ALPHA 0x8C42\n#define GL_SRGB8_ALPHA8 0x8C43\n#define GL_COMPRESSED_SRGB 0x8C48\n#define GL_COMPRESSED_SRGB_ALPHA 0x8C49\n#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F\n#define GL_SLUMINANCE_ALPHA 0x8C44\n#define GL_SLUMINANCE8_ALPHA8 0x8C45\n#define GL_SLUMINANCE 0x8C46\n#define GL_SLUMINANCE8 0x8C47\n#define GL_COMPRESSED_SLUMINANCE 0x8C4A\n#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B\n#define GL_COMPARE_REF_TO_TEXTURE 0x884E\n#define GL_CLIP_DISTANCE0 0x3000\n#define GL_CLIP_DISTANCE1 0x3001\n#define GL_CLIP_DISTANCE2 0x3002\n#define GL_CLIP_DISTANCE3 0x3003\n#define GL_CLIP_DISTANCE4 0x3004\n#define GL_CLIP_DISTANCE5 0x3005\n#define GL_CLIP_DISTANCE6 0x3006\n#define GL_CLIP_DISTANCE7 0x3007\n#define GL_MAX_CLIP_DISTANCES 0x0D32\n#define GL_MAJOR_VERSION 0x821B\n#define GL_MINOR_VERSION 0x821C\n#define GL_NUM_EXTENSIONS 0x821D\n#define GL_CONTEXT_FLAGS 0x821E\n#define GL_COMPRESSED_RED 0x8225\n#define GL_COMPRESSED_RG 0x8226\n#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001\n#define GL_RGBA32F 0x8814\n#define GL_RGB32F 0x8815\n#define GL_RGBA16F 0x881A\n#define GL_RGB16F 0x881B\n#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD\n#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF\n#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904\n#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905\n#define GL_CLAMP_READ_COLOR 0x891C\n#define GL_FIXED_ONLY 0x891D\n#define GL_MAX_VARYING_COMPONENTS 0x8B4B\n#define GL_TEXTURE_1D_ARRAY 0x8C18\n#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19\n#define GL_TEXTURE_2D_ARRAY 0x8C1A\n#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B\n#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C\n#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D\n#define GL_R11F_G11F_B10F 0x8C3A\n#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B\n#define GL_RGB9_E5 0x8C3D\n#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E\n#define GL_TEXTURE_SHARED_SIZE 0x8C3F\n#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76\n#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80\n#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83\n#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84\n#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85\n#define GL_PRIMITIVES_GENERATED 0x8C87\n#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88\n#define GL_RASTERIZER_DISCARD 0x8C89\n#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B\n#define GL_INTERLEAVED_ATTRIBS 0x8C8C\n#define GL_SEPARATE_ATTRIBS 0x8C8D\n#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E\n#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F\n#define GL_RGBA32UI 0x8D70\n#define GL_RGB32UI 0x8D71\n#define GL_RGBA16UI 0x8D76\n#define GL_RGB16UI 0x8D77\n#define GL_RGBA8UI 0x8D7C\n#define GL_RGB8UI 0x8D7D\n#define GL_RGBA32I 0x8D82\n#define GL_RGB32I 0x8D83\n#define GL_RGBA16I 0x8D88\n#define GL_RGB16I 0x8D89\n#define GL_RGBA8I 0x8D8E\n#define GL_RGB8I 0x8D8F\n#define GL_RED_INTEGER 0x8D94\n#define GL_GREEN_INTEGER 0x8D95\n#define GL_BLUE_INTEGER 0x8D96\n#define GL_RGB_INTEGER 0x8D98\n#define GL_RGBA_INTEGER 0x8D99\n#define GL_BGR_INTEGER 0x8D9A\n#define GL_BGRA_INTEGER 0x8D9B\n#define GL_SAMPLER_1D_ARRAY 0x8DC0\n#define GL_SAMPLER_2D_ARRAY 0x8DC1\n#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3\n#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4\n#define GL_SAMPLER_CUBE_SHADOW 0x8DC5\n#define GL_UNSIGNED_INT_VEC2 0x8DC6\n#define GL_UNSIGNED_INT_VEC3 0x8DC7\n#define GL_UNSIGNED_INT_VEC4 0x8DC8\n#define GL_INT_SAMPLER_1D 0x8DC9\n#define GL_INT_SAMPLER_2D 0x8DCA\n#define GL_INT_SAMPLER_3D 0x8DCB\n#define GL_INT_SAMPLER_CUBE 0x8DCC\n#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE\n#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF\n#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1\n#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2\n#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3\n#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4\n#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6\n#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7\n#define GL_QUERY_WAIT 0x8E13\n#define GL_QUERY_NO_WAIT 0x8E14\n#define GL_QUERY_BY_REGION_WAIT 0x8E15\n#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16\n#define GL_BUFFER_ACCESS_FLAGS 0x911F\n#define GL_BUFFER_MAP_LENGTH 0x9120\n#define GL_BUFFER_MAP_OFFSET 0x9121\n#define GL_DEPTH_COMPONENT32F 0x8CAC\n#define GL_DEPTH32F_STENCIL8 0x8CAD\n#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD\n#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506\n#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210\n#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211\n#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212\n#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213\n#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214\n#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215\n#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216\n#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217\n#define GL_FRAMEBUFFER_DEFAULT 0x8218\n#define GL_FRAMEBUFFER_UNDEFINED 0x8219\n#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A\n#define GL_MAX_RENDERBUFFER_SIZE 0x84E8\n#define GL_DEPTH_STENCIL 0x84F9\n#define GL_UNSIGNED_INT_24_8 0x84FA\n#define GL_DEPTH24_STENCIL8 0x88F0\n#define GL_TEXTURE_STENCIL_SIZE 0x88F1\n#define GL_TEXTURE_RED_TYPE 0x8C10\n#define GL_TEXTURE_GREEN_TYPE 0x8C11\n#define GL_TEXTURE_BLUE_TYPE 0x8C12\n#define GL_TEXTURE_ALPHA_TYPE 0x8C13\n#define GL_TEXTURE_DEPTH_TYPE 0x8C16\n#define GL_UNSIGNED_NORMALIZED 0x8C17\n#define GL_FRAMEBUFFER_BINDING 0x8CA6\n#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6\n#define GL_RENDERBUFFER_BINDING 0x8CA7\n#define GL_READ_FRAMEBUFFER 0x8CA8\n#define GL_DRAW_FRAMEBUFFER 0x8CA9\n#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA\n#define GL_RENDERBUFFER_SAMPLES 0x8CAB\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4\n#define GL_FRAMEBUFFER_COMPLETE 0x8CD5\n#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6\n#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7\n#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB\n#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC\n#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD\n#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF\n#define GL_COLOR_ATTACHMENT0 0x8CE0\n#define GL_COLOR_ATTACHMENT1 0x8CE1\n#define GL_COLOR_ATTACHMENT2 0x8CE2\n#define GL_COLOR_ATTACHMENT3 0x8CE3\n#define GL_COLOR_ATTACHMENT4 0x8CE4\n#define GL_COLOR_ATTACHMENT5 0x8CE5\n#define GL_COLOR_ATTACHMENT6 0x8CE6\n#define GL_COLOR_ATTACHMENT7 0x8CE7\n#define GL_COLOR_ATTACHMENT8 0x8CE8\n#define GL_COLOR_ATTACHMENT9 0x8CE9\n#define GL_COLOR_ATTACHMENT10 0x8CEA\n#define GL_COLOR_ATTACHMENT11 0x8CEB\n#define GL_COLOR_ATTACHMENT12 0x8CEC\n#define GL_COLOR_ATTACHMENT13 0x8CED\n#define GL_COLOR_ATTACHMENT14 0x8CEE\n#define GL_COLOR_ATTACHMENT15 0x8CEF\n#define GL_COLOR_ATTACHMENT16 0x8CF0\n#define GL_COLOR_ATTACHMENT17 0x8CF1\n#define GL_COLOR_ATTACHMENT18 0x8CF2\n#define GL_COLOR_ATTACHMENT19 0x8CF3\n#define GL_COLOR_ATTACHMENT20 0x8CF4\n#define GL_COLOR_ATTACHMENT21 0x8CF5\n#define GL_COLOR_ATTACHMENT22 0x8CF6\n#define GL_COLOR_ATTACHMENT23 0x8CF7\n#define GL_COLOR_ATTACHMENT24 0x8CF8\n#define GL_COLOR_ATTACHMENT25 0x8CF9\n#define GL_COLOR_ATTACHMENT26 0x8CFA\n#define GL_COLOR_ATTACHMENT27 0x8CFB\n#define GL_COLOR_ATTACHMENT28 0x8CFC\n#define GL_COLOR_ATTACHMENT29 0x8CFD\n#define GL_COLOR_ATTACHMENT30 0x8CFE\n#define GL_COLOR_ATTACHMENT31 0x8CFF\n#define GL_DEPTH_ATTACHMENT 0x8D00\n#define GL_STENCIL_ATTACHMENT 0x8D20\n#define GL_FRAMEBUFFER 0x8D40\n#define GL_RENDERBUFFER 0x8D41\n#define GL_RENDERBUFFER_WIDTH 0x8D42\n#define GL_RENDERBUFFER_HEIGHT 0x8D43\n#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44\n#define GL_STENCIL_INDEX1 0x8D46\n#define GL_STENCIL_INDEX4 0x8D47\n#define GL_STENCIL_INDEX8 0x8D48\n#define GL_STENCIL_INDEX16 0x8D49\n#define GL_RENDERBUFFER_RED_SIZE 0x8D50\n#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51\n#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52\n#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53\n#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54\n#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55\n#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56\n#define GL_MAX_SAMPLES 0x8D57\n#define GL_INDEX 0x8222\n#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14\n#define GL_TEXTURE_INTENSITY_TYPE 0x8C15\n#define GL_FRAMEBUFFER_SRGB 0x8DB9\n#define GL_HALF_FLOAT 0x140B\n#define GL_MAP_READ_BIT 0x0001\n#define GL_MAP_WRITE_BIT 0x0002\n#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004\n#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008\n#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010\n#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020\n#define GL_COMPRESSED_RED_RGTC1 0x8DBB\n#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC\n#define GL_COMPRESSED_RG_RGTC2 0x8DBD\n#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE\n#define GL_RG 0x8227\n#define GL_RG_INTEGER 0x8228\n#define GL_R8 0x8229\n#define GL_R16 0x822A\n#define GL_RG8 0x822B\n#define GL_RG16 0x822C\n#define GL_R16F 0x822D\n#define GL_R32F 0x822E\n#define GL_RG16F 0x822F\n#define GL_RG32F 0x8230\n#define GL_R8I 0x8231\n#define GL_R8UI 0x8232\n#define GL_R16I 0x8233\n#define GL_R16UI 0x8234\n#define GL_R32I 0x8235\n#define GL_R32UI 0x8236\n#define GL_RG8I 0x8237\n#define GL_RG8UI 0x8238\n#define GL_RG16I 0x8239\n#define GL_RG16UI 0x823A\n#define GL_RG32I 0x823B\n#define GL_RG32UI 0x823C\n#define GL_VERTEX_ARRAY_BINDING 0x85B5\n#define GL_CLAMP_VERTEX_COLOR 0x891A\n#define GL_CLAMP_FRAGMENT_COLOR 0x891B\n#define GL_ALPHA_INTEGER 0x8D97\n#define GL_SAMPLER_2D_RECT 0x8B63\n#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64\n#define GL_SAMPLER_BUFFER 0x8DC2\n#define GL_INT_SAMPLER_2D_RECT 0x8DCD\n#define GL_INT_SAMPLER_BUFFER 0x8DD0\n#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5\n#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8\n#define GL_TEXTURE_BUFFER 0x8C2A\n#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B\n#define GL_TEXTURE_BINDING_BUFFER 0x8C2C\n#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D\n#define GL_TEXTURE_RECTANGLE 0x84F5\n#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6\n#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7\n#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8\n#define GL_R8_SNORM 0x8F94\n#define GL_RG8_SNORM 0x8F95\n#define GL_RGB8_SNORM 0x8F96\n#define GL_RGBA8_SNORM 0x8F97\n#define GL_R16_SNORM 0x8F98\n#define GL_RG16_SNORM 0x8F99\n#define GL_RGB16_SNORM 0x8F9A\n#define GL_RGBA16_SNORM 0x8F9B\n#define GL_SIGNED_NORMALIZED 0x8F9C\n#define GL_PRIMITIVE_RESTART 0x8F9D\n#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E\n#define GL_COPY_READ_BUFFER 0x8F36\n#define GL_COPY_WRITE_BUFFER 0x8F37\n#define GL_UNIFORM_BUFFER 0x8A11\n#define GL_UNIFORM_BUFFER_BINDING 0x8A28\n#define GL_UNIFORM_BUFFER_START 0x8A29\n#define GL_UNIFORM_BUFFER_SIZE 0x8A2A\n#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B\n#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C\n#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D\n#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E\n#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F\n#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30\n#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31\n#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32\n#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33\n#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34\n#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35\n#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36\n#define GL_UNIFORM_TYPE 0x8A37\n#define GL_UNIFORM_SIZE 0x8A38\n#define GL_UNIFORM_NAME_LENGTH 0x8A39\n#define GL_UNIFORM_BLOCK_INDEX 0x8A3A\n#define GL_UNIFORM_OFFSET 0x8A3B\n#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C\n#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D\n#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E\n#define GL_UNIFORM_BLOCK_BINDING 0x8A3F\n#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40\n#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41\n#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42\n#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46\n#define GL_INVALID_INDEX 0xFFFFFFFF\n#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001\n#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002\n#define GL_LINES_ADJACENCY 0x000A\n#define GL_LINE_STRIP_ADJACENCY 0x000B\n#define GL_TRIANGLES_ADJACENCY 0x000C\n#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D\n#define GL_PROGRAM_POINT_SIZE 0x8642\n#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29\n#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7\n#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8\n#define GL_GEOMETRY_SHADER 0x8DD9\n#define GL_GEOMETRY_VERTICES_OUT 0x8916\n#define GL_GEOMETRY_INPUT_TYPE 0x8917\n#define GL_GEOMETRY_OUTPUT_TYPE 0x8918\n#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF\n#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0\n#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1\n#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122\n#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123\n#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124\n#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125\n#define GL_CONTEXT_PROFILE_MASK 0x9126\n#define GL_DEPTH_CLAMP 0x864F\n#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C\n#define GL_FIRST_VERTEX_CONVENTION 0x8E4D\n#define GL_LAST_VERTEX_CONVENTION 0x8E4E\n#define GL_PROVOKING_VERTEX 0x8E4F\n#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F\n#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111\n#define GL_OBJECT_TYPE 0x9112\n#define GL_SYNC_CONDITION 0x9113\n#define GL_SYNC_STATUS 0x9114\n#define GL_SYNC_FLAGS 0x9115\n#define GL_SYNC_FENCE 0x9116\n#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117\n#define GL_UNSIGNALED 0x9118\n#define GL_SIGNALED 0x9119\n#define GL_ALREADY_SIGNALED 0x911A\n#define GL_TIMEOUT_EXPIRED 0x911B\n#define GL_CONDITION_SATISFIED 0x911C\n#define GL_WAIT_FAILED 0x911D\n#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF\n#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001\n#define GL_SAMPLE_POSITION 0x8E50\n#define GL_SAMPLE_MASK 0x8E51\n#define GL_SAMPLE_MASK_VALUE 0x8E52\n#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59\n#define GL_TEXTURE_2D_MULTISAMPLE 0x9100\n#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101\n#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102\n#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103\n#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104\n#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105\n#define GL_TEXTURE_SAMPLES 0x9106\n#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107\n#define GL_SAMPLER_2D_MULTISAMPLE 0x9108\n#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109\n#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A\n#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B\n#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C\n#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D\n#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E\n#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F\n#define GL_MAX_INTEGER_SAMPLES 0x9110\n#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE\n#define GL_SRC1_COLOR 0x88F9\n#define GL_ONE_MINUS_SRC1_COLOR 0x88FA\n#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB\n#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC\n#define GL_ANY_SAMPLES_PASSED 0x8C2F\n#define GL_SAMPLER_BINDING 0x8919\n#define GL_RGB10_A2UI 0x906F\n#define GL_TEXTURE_SWIZZLE_R 0x8E42\n#define GL_TEXTURE_SWIZZLE_G 0x8E43\n#define GL_TEXTURE_SWIZZLE_B 0x8E44\n#define GL_TEXTURE_SWIZZLE_A 0x8E45\n#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46\n#define GL_TIME_ELAPSED 0x88BF\n#define GL_TIMESTAMP 0x8E28\n#define GL_INT_2_10_10_10_REV 0x8D9F\n#define GL_SAMPLE_SHADING 0x8C36\n#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37\n#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E\n#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F\n#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009\n#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A\n#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B\n#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C\n#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D\n#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E\n#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F\n#define GL_DRAW_INDIRECT_BUFFER 0x8F3F\n#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43\n#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F\n#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A\n#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B\n#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C\n#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D\n#define GL_MAX_VERTEX_STREAMS 0x8E71\n#define GL_DOUBLE_VEC2 0x8FFC\n#define GL_DOUBLE_VEC3 0x8FFD\n#define GL_DOUBLE_VEC4 0x8FFE\n#define GL_DOUBLE_MAT2 0x8F46\n#define GL_DOUBLE_MAT3 0x8F47\n#define GL_DOUBLE_MAT4 0x8F48\n#define GL_DOUBLE_MAT2x3 0x8F49\n#define GL_DOUBLE_MAT2x4 0x8F4A\n#define GL_DOUBLE_MAT3x2 0x8F4B\n#define GL_DOUBLE_MAT3x4 0x8F4C\n#define GL_DOUBLE_MAT4x2 0x8F4D\n#define GL_DOUBLE_MAT4x3 0x8F4E\n#define GL_ACTIVE_SUBROUTINES 0x8DE5\n#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6\n#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47\n#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48\n#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49\n#define GL_MAX_SUBROUTINES 0x8DE7\n#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8\n#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A\n#define GL_COMPATIBLE_SUBROUTINES 0x8E4B\n#define GL_PATCHES 0x000E\n#define GL_PATCH_VERTICES 0x8E72\n#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73\n#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74\n#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75\n#define GL_TESS_GEN_MODE 0x8E76\n#define GL_TESS_GEN_SPACING 0x8E77\n#define GL_TESS_GEN_VERTEX_ORDER 0x8E78\n#define GL_TESS_GEN_POINT_MODE 0x8E79\n#define GL_ISOLINES 0x8E7A\n#define GL_FRACTIONAL_ODD 0x8E7B\n#define GL_FRACTIONAL_EVEN 0x8E7C\n#define GL_MAX_PATCH_VERTICES 0x8E7D\n#define GL_MAX_TESS_GEN_LEVEL 0x8E7E\n#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F\n#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80\n#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81\n#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82\n#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83\n#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84\n#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85\n#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86\n#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89\n#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A\n#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C\n#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D\n#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E\n#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1\n#define GL_TESS_EVALUATION_SHADER 0x8E87\n#define GL_TESS_CONTROL_SHADER 0x8E88\n#define GL_TRANSFORM_FEEDBACK 0x8E22\n#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23\n#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24\n#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25\n#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70\n#define GL_FIXED 0x140C\n#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A\n#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B\n#define GL_LOW_FLOAT 0x8DF0\n#define GL_MEDIUM_FLOAT 0x8DF1\n#define GL_HIGH_FLOAT 0x8DF2\n#define GL_LOW_INT 0x8DF3\n#define GL_MEDIUM_INT 0x8DF4\n#define GL_HIGH_INT 0x8DF5\n#define GL_SHADER_COMPILER 0x8DFA\n#define GL_SHADER_BINARY_FORMATS 0x8DF8\n#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9\n#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB\n#define GL_MAX_VARYING_VECTORS 0x8DFC\n#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD\n#define GL_RGB565 0x8D62\n#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257\n#define GL_PROGRAM_BINARY_LENGTH 0x8741\n#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE\n#define GL_PROGRAM_BINARY_FORMATS 0x87FF\n#define GL_VERTEX_SHADER_BIT 0x00000001\n#define GL_FRAGMENT_SHADER_BIT 0x00000002\n#define GL_GEOMETRY_SHADER_BIT 0x00000004\n#define GL_TESS_CONTROL_SHADER_BIT 0x00000008\n#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010\n#define GL_ALL_SHADER_BITS 0xFFFFFFFF\n#define GL_PROGRAM_SEPARABLE 0x8258\n#define GL_ACTIVE_PROGRAM 0x8259\n#define GL_PROGRAM_PIPELINE_BINDING 0x825A\n#define GL_MAX_VIEWPORTS 0x825B\n#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C\n#define GL_VIEWPORT_BOUNDS_RANGE 0x825D\n#define GL_LAYER_PROVOKING_VERTEX 0x825E\n#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F\n#define GL_UNDEFINED_VERTEX 0x8260\n#define GL_COPY_READ_BUFFER_BINDING 0x8F36\n#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37\n#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24\n#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23\n#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127\n#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128\n#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129\n#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A\n#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B\n#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C\n#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D\n#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E\n#define GL_NUM_SAMPLE_COUNTS 0x9380\n#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC\n#define GL_ATOMIC_COUNTER_BUFFER 0x92C0\n#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1\n#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2\n#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3\n#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4\n#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5\n#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB\n#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC\n#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD\n#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE\n#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF\n#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0\n#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1\n#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2\n#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3\n#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4\n#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5\n#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6\n#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7\n#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8\n#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC\n#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9\n#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA\n#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB\n#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001\n#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002\n#define GL_UNIFORM_BARRIER_BIT 0x00000004\n#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008\n#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020\n#define GL_COMMAND_BARRIER_BIT 0x00000040\n#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080\n#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100\n#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200\n#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400\n#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800\n#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000\n#define GL_ALL_BARRIER_BITS 0xFFFFFFFF\n#define GL_MAX_IMAGE_UNITS 0x8F38\n#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39\n#define GL_IMAGE_BINDING_NAME 0x8F3A\n#define GL_IMAGE_BINDING_LEVEL 0x8F3B\n#define GL_IMAGE_BINDING_LAYERED 0x8F3C\n#define GL_IMAGE_BINDING_LAYER 0x8F3D\n#define GL_IMAGE_BINDING_ACCESS 0x8F3E\n#define GL_IMAGE_1D 0x904C\n#define GL_IMAGE_2D 0x904D\n#define GL_IMAGE_3D 0x904E\n#define GL_IMAGE_2D_RECT 0x904F\n#define GL_IMAGE_CUBE 0x9050\n#define GL_IMAGE_BUFFER 0x9051\n#define GL_IMAGE_1D_ARRAY 0x9052\n#define GL_IMAGE_2D_ARRAY 0x9053\n#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054\n#define GL_IMAGE_2D_MULTISAMPLE 0x9055\n#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056\n#define GL_INT_IMAGE_1D 0x9057\n#define GL_INT_IMAGE_2D 0x9058\n#define GL_INT_IMAGE_3D 0x9059\n#define GL_INT_IMAGE_2D_RECT 0x905A\n#define GL_INT_IMAGE_CUBE 0x905B\n#define GL_INT_IMAGE_BUFFER 0x905C\n#define GL_INT_IMAGE_1D_ARRAY 0x905D\n#define GL_INT_IMAGE_2D_ARRAY 0x905E\n#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F\n#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060\n#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061\n#define GL_UNSIGNED_INT_IMAGE_1D 0x9062\n#define GL_UNSIGNED_INT_IMAGE_2D 0x9063\n#define GL_UNSIGNED_INT_IMAGE_3D 0x9064\n#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065\n#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066\n#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067\n#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068\n#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069\n#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A\n#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B\n#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C\n#define GL_MAX_IMAGE_SAMPLES 0x906D\n#define GL_IMAGE_BINDING_FORMAT 0x906E\n#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7\n#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8\n#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9\n#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA\n#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB\n#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC\n#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD\n#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE\n#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF\n#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C\n#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D\n#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E\n#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F\n#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F\n#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9\n#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E\n#define GL_COMPRESSED_RGB8_ETC2 0x9274\n#define GL_COMPRESSED_SRGB8_ETC2 0x9275\n#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276\n#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277\n#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278\n#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279\n#define GL_COMPRESSED_R11_EAC 0x9270\n#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271\n#define GL_COMPRESSED_RG11_EAC 0x9272\n#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273\n#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69\n#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A\n#define GL_MAX_ELEMENT_INDEX 0x8D6B\n#define GL_COMPUTE_SHADER 0x91B9\n#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB\n#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC\n#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD\n#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262\n#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263\n#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264\n#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265\n#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266\n#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB\n#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE\n#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF\n#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED\n#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE\n#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF\n#define GL_COMPUTE_SHADER_BIT 0x00000020\n#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242\n#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243\n#define GL_DEBUG_CALLBACK_FUNCTION 0x8244\n#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245\n#define GL_DEBUG_SOURCE_API 0x8246\n#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247\n#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248\n#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249\n#define GL_DEBUG_SOURCE_APPLICATION 0x824A\n#define GL_DEBUG_SOURCE_OTHER 0x824B\n#define GL_DEBUG_TYPE_ERROR 0x824C\n#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D\n#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E\n#define GL_DEBUG_TYPE_PORTABILITY 0x824F\n#define GL_DEBUG_TYPE_PERFORMANCE 0x8250\n#define GL_DEBUG_TYPE_OTHER 0x8251\n#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143\n#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144\n#define GL_DEBUG_LOGGED_MESSAGES 0x9145\n#define GL_DEBUG_SEVERITY_HIGH 0x9146\n#define GL_DEBUG_SEVERITY_MEDIUM 0x9147\n#define GL_DEBUG_SEVERITY_LOW 0x9148\n#define GL_DEBUG_TYPE_MARKER 0x8268\n#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269\n#define GL_DEBUG_TYPE_POP_GROUP 0x826A\n#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B\n#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C\n#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D\n#define GL_BUFFER 0x82E0\n#define GL_SHADER 0x82E1\n#define GL_PROGRAM 0x82E2\n#define GL_QUERY 0x82E3\n#define GL_PROGRAM_PIPELINE 0x82E4\n#define GL_SAMPLER 0x82E6\n#define GL_MAX_LABEL_LENGTH 0x82E8\n#define GL_DEBUG_OUTPUT 0x92E0\n#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002\n#define GL_MAX_UNIFORM_LOCATIONS 0x826E\n#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310\n#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311\n#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312\n#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313\n#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314\n#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315\n#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316\n#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317\n#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318\n#define GL_INTERNALFORMAT_SUPPORTED 0x826F\n#define GL_INTERNALFORMAT_PREFERRED 0x8270\n#define GL_INTERNALFORMAT_RED_SIZE 0x8271\n#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272\n#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273\n#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274\n#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275\n#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276\n#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277\n#define GL_INTERNALFORMAT_RED_TYPE 0x8278\n#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279\n#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A\n#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B\n#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C\n#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D\n#define GL_MAX_WIDTH 0x827E\n#define GL_MAX_HEIGHT 0x827F\n#define GL_MAX_DEPTH 0x8280\n#define GL_MAX_LAYERS 0x8281\n#define GL_MAX_COMBINED_DIMENSIONS 0x8282\n#define GL_COLOR_COMPONENTS 0x8283\n#define GL_DEPTH_COMPONENTS 0x8284\n#define GL_STENCIL_COMPONENTS 0x8285\n#define GL_COLOR_RENDERABLE 0x8286\n#define GL_DEPTH_RENDERABLE 0x8287\n#define GL_STENCIL_RENDERABLE 0x8288\n#define GL_FRAMEBUFFER_RENDERABLE 0x8289\n#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A\n#define GL_FRAMEBUFFER_BLEND 0x828B\n#define GL_READ_PIXELS 0x828C\n#define GL_READ_PIXELS_FORMAT 0x828D\n#define GL_READ_PIXELS_TYPE 0x828E\n#define GL_TEXTURE_IMAGE_FORMAT 0x828F\n#define GL_TEXTURE_IMAGE_TYPE 0x8290\n#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291\n#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292\n#define GL_MIPMAP 0x8293\n#define GL_MANUAL_GENERATE_MIPMAP 0x8294\n#define GL_AUTO_GENERATE_MIPMAP 0x8295\n#define GL_COLOR_ENCODING 0x8296\n#define GL_SRGB_READ 0x8297\n#define GL_SRGB_WRITE 0x8298\n#define GL_FILTER 0x829A\n#define GL_VERTEX_TEXTURE 0x829B\n#define GL_TESS_CONTROL_TEXTURE 0x829C\n#define GL_TESS_EVALUATION_TEXTURE 0x829D\n#define GL_GEOMETRY_TEXTURE 0x829E\n#define GL_FRAGMENT_TEXTURE 0x829F\n#define GL_COMPUTE_TEXTURE 0x82A0\n#define GL_TEXTURE_SHADOW 0x82A1\n#define GL_TEXTURE_GATHER 0x82A2\n#define GL_TEXTURE_GATHER_SHADOW 0x82A3\n#define GL_SHADER_IMAGE_LOAD 0x82A4\n#define GL_SHADER_IMAGE_STORE 0x82A5\n#define GL_SHADER_IMAGE_ATOMIC 0x82A6\n#define GL_IMAGE_TEXEL_SIZE 0x82A7\n#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8\n#define GL_IMAGE_PIXEL_FORMAT 0x82A9\n#define GL_IMAGE_PIXEL_TYPE 0x82AA\n#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC\n#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD\n#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE\n#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF\n#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1\n#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2\n#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3\n#define GL_CLEAR_BUFFER 0x82B4\n#define GL_TEXTURE_VIEW 0x82B5\n#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6\n#define GL_FULL_SUPPORT 0x82B7\n#define GL_CAVEAT_SUPPORT 0x82B8\n#define GL_IMAGE_CLASS_4_X_32 0x82B9\n#define GL_IMAGE_CLASS_2_X_32 0x82BA\n#define GL_IMAGE_CLASS_1_X_32 0x82BB\n#define GL_IMAGE_CLASS_4_X_16 0x82BC\n#define GL_IMAGE_CLASS_2_X_16 0x82BD\n#define GL_IMAGE_CLASS_1_X_16 0x82BE\n#define GL_IMAGE_CLASS_4_X_8 0x82BF\n#define GL_IMAGE_CLASS_2_X_8 0x82C0\n#define GL_IMAGE_CLASS_1_X_8 0x82C1\n#define GL_IMAGE_CLASS_11_11_10 0x82C2\n#define GL_IMAGE_CLASS_10_10_10_2 0x82C3\n#define GL_VIEW_CLASS_128_BITS 0x82C4\n#define GL_VIEW_CLASS_96_BITS 0x82C5\n#define GL_VIEW_CLASS_64_BITS 0x82C6\n#define GL_VIEW_CLASS_48_BITS 0x82C7\n#define GL_VIEW_CLASS_32_BITS 0x82C8\n#define GL_VIEW_CLASS_24_BITS 0x82C9\n#define GL_VIEW_CLASS_16_BITS 0x82CA\n#define GL_VIEW_CLASS_8_BITS 0x82CB\n#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC\n#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD\n#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE\n#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF\n#define GL_VIEW_CLASS_RGTC1_RED 0x82D0\n#define GL_VIEW_CLASS_RGTC2_RG 0x82D1\n#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2\n#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3\n#define GL_UNIFORM 0x92E1\n#define GL_UNIFORM_BLOCK 0x92E2\n#define GL_PROGRAM_INPUT 0x92E3\n#define GL_PROGRAM_OUTPUT 0x92E4\n#define GL_BUFFER_VARIABLE 0x92E5\n#define GL_SHADER_STORAGE_BLOCK 0x92E6\n#define GL_VERTEX_SUBROUTINE 0x92E8\n#define GL_TESS_CONTROL_SUBROUTINE 0x92E9\n#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA\n#define GL_GEOMETRY_SUBROUTINE 0x92EB\n#define GL_FRAGMENT_SUBROUTINE 0x92EC\n#define GL_COMPUTE_SUBROUTINE 0x92ED\n#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE\n#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF\n#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0\n#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1\n#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2\n#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3\n#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4\n#define GL_ACTIVE_RESOURCES 0x92F5\n#define GL_MAX_NAME_LENGTH 0x92F6\n#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7\n#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8\n#define GL_NAME_LENGTH 0x92F9\n#define GL_TYPE 0x92FA\n#define GL_ARRAY_SIZE 0x92FB\n#define GL_OFFSET 0x92FC\n#define GL_BLOCK_INDEX 0x92FD\n#define GL_ARRAY_STRIDE 0x92FE\n#define GL_MATRIX_STRIDE 0x92FF\n#define GL_IS_ROW_MAJOR 0x9300\n#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301\n#define GL_BUFFER_BINDING 0x9302\n#define GL_BUFFER_DATA_SIZE 0x9303\n#define GL_NUM_ACTIVE_VARIABLES 0x9304\n#define GL_ACTIVE_VARIABLES 0x9305\n#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306\n#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307\n#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308\n#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309\n#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A\n#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B\n#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C\n#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D\n#define GL_LOCATION 0x930E\n#define GL_LOCATION_INDEX 0x930F\n#define GL_IS_PER_PATCH 0x92E7\n#define GL_SHADER_STORAGE_BUFFER 0x90D2\n#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3\n#define GL_SHADER_STORAGE_BUFFER_START 0x90D4\n#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5\n#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6\n#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7\n#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8\n#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9\n#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA\n#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB\n#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC\n#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD\n#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE\n#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF\n#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000\n#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39\n#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA\n#define GL_TEXTURE_BUFFER_OFFSET 0x919D\n#define GL_TEXTURE_BUFFER_SIZE 0x919E\n#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F\n#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB\n#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC\n#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD\n#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE\n#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF\n#define GL_VERTEX_ATTRIB_BINDING 0x82D4\n#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5\n#define GL_VERTEX_BINDING_DIVISOR 0x82D6\n#define GL_VERTEX_BINDING_OFFSET 0x82D7\n#define GL_VERTEX_BINDING_STRIDE 0x82D8\n#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9\n#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA\n#define GL_VERTEX_BINDING_BUFFER 0x8F4F\n#define GL_DISPLAY_LIST 0x82E7\n#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5\n#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221\n#define GL_TEXTURE_BUFFER_BINDING 0x8C2A\n#define GL_MAP_PERSISTENT_BIT 0x0040\n#define GL_MAP_COHERENT_BIT 0x0080\n#define GL_DYNAMIC_STORAGE_BIT 0x0100\n#define GL_CLIENT_STORAGE_BIT 0x0200\n#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000\n#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F\n#define GL_BUFFER_STORAGE_FLAGS 0x8220\n#define GL_CLEAR_TEXTURE 0x9365\n#define GL_LOCATION_COMPONENT 0x934A\n#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B\n#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C\n#define GL_QUERY_BUFFER 0x9192\n#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000\n#define GL_QUERY_BUFFER_BINDING 0x9193\n#define GL_QUERY_RESULT_NO_WAIT 0x9194\n#define GL_MIRROR_CLAMP_TO_EDGE 0x8743\n#define GL_CONTEXT_LOST 0x0507\n#define GL_NEGATIVE_ONE_TO_ONE 0x935E\n#define GL_ZERO_TO_ONE 0x935F\n#define GL_CLIP_ORIGIN 0x935C\n#define GL_CLIP_DEPTH_MODE 0x935D\n#define GL_QUERY_WAIT_INVERTED 0x8E17\n#define GL_QUERY_NO_WAIT_INVERTED 0x8E18\n#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19\n#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A\n#define GL_MAX_CULL_DISTANCES 0x82F9\n#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA\n#define GL_TEXTURE_TARGET 0x1006\n#define GL_QUERY_TARGET 0x82EA\n#define GL_GUILTY_CONTEXT_RESET 0x8253\n#define GL_INNOCENT_CONTEXT_RESET 0x8254\n#define GL_UNKNOWN_CONTEXT_RESET 0x8255\n#define GL_RESET_NOTIFICATION_STRATEGY 0x8256\n#define GL_LOSE_CONTEXT_ON_RESET 0x8252\n#define GL_NO_RESET_NOTIFICATION 0x8261\n#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004\n#define GL_COLOR_TABLE 0x80D0\n#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1\n#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2\n#define GL_PROXY_COLOR_TABLE 0x80D3\n#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4\n#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5\n#define GL_CONVOLUTION_1D 0x8010\n#define GL_CONVOLUTION_2D 0x8011\n#define GL_SEPARABLE_2D 0x8012\n#define GL_HISTOGRAM 0x8024\n#define GL_PROXY_HISTOGRAM 0x8025\n#define GL_MINMAX 0x802E\n#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB\n#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC\n#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551\n#define GL_SPIR_V_BINARY 0x9552\n#define GL_PARAMETER_BUFFER 0x80EE\n#define GL_PARAMETER_BUFFER_BINDING 0x80EF\n#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008\n#define GL_VERTICES_SUBMITTED 0x82EE\n#define GL_PRIMITIVES_SUBMITTED 0x82EF\n#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0\n#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1\n#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2\n#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3\n#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4\n#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5\n#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6\n#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7\n#define GL_POLYGON_OFFSET_CLAMP 0x8E1B\n#define GL_SPIR_V_EXTENSIONS 0x9553\n#define GL_NUM_SPIR_V_EXTENSIONS 0x9554\n#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE\n#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF\n#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC\n#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED\n#ifndef GL_VERSION_1_0\n#define GL_VERSION_1_0 1\nGLAPI int GLAD_GL_VERSION_1_0;\ntypedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum mode);\nGLAPI PFNGLCULLFACEPROC glad_glCullFace;\n#define glCullFace glad_glCullFace\ntypedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum mode);\nGLAPI PFNGLFRONTFACEPROC glad_glFrontFace;\n#define glFrontFace glad_glFrontFace\ntypedef void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode);\nGLAPI PFNGLHINTPROC glad_glHint;\n#define glHint glad_glHint\ntypedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width);\nGLAPI PFNGLLINEWIDTHPROC glad_glLineWidth;\n#define glLineWidth glad_glLineWidth\ntypedef void (APIENTRYP PFNGLPOINTSIZEPROC)(GLfloat size);\nGLAPI PFNGLPOINTSIZEPROC glad_glPointSize;\n#define glPointSize glad_glPointSize\ntypedef void (APIENTRYP PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode);\nGLAPI PFNGLPOLYGONMODEPROC glad_glPolygonMode;\n#define glPolygonMode glad_glPolygonMode\ntypedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLSCISSORPROC glad_glScissor;\n#define glScissor glad_glScissor\ntypedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param);\nGLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf;\n#define glTexParameterf glad_glTexParameterf\ntypedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params);\nGLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;\n#define glTexParameterfv glad_glTexParameterfv\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param);\nGLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri;\n#define glTexParameteri glad_glTexParameteri\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params);\nGLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;\n#define glTexParameteriv glad_glTexParameteriv\ntypedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXIMAGE1DPROC glad_glTexImage1D;\n#define glTexImage1D glad_glTexImage1D\ntypedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D;\n#define glTexImage2D glad_glTexImage2D\ntypedef void (APIENTRYP PFNGLDRAWBUFFERPROC)(GLenum buf);\nGLAPI PFNGLDRAWBUFFERPROC glad_glDrawBuffer;\n#define glDrawBuffer glad_glDrawBuffer\ntypedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield mask);\nGLAPI PFNGLCLEARPROC glad_glClear;\n#define glClear glad_glClear\ntypedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nGLAPI PFNGLCLEARCOLORPROC glad_glClearColor;\n#define glClearColor glad_glClearColor\ntypedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s);\nGLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil;\n#define glClearStencil glad_glClearStencil\ntypedef void (APIENTRYP PFNGLCLEARDEPTHPROC)(GLdouble depth);\nGLAPI PFNGLCLEARDEPTHPROC glad_glClearDepth;\n#define glClearDepth glad_glClearDepth\ntypedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask);\nGLAPI PFNGLSTENCILMASKPROC glad_glStencilMask;\n#define glStencilMask glad_glStencilMask\ntypedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);\nGLAPI PFNGLCOLORMASKPROC glad_glColorMask;\n#define glColorMask glad_glColorMask\ntypedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag);\nGLAPI PFNGLDEPTHMASKPROC glad_glDepthMask;\n#define glDepthMask glad_glDepthMask\ntypedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap);\nGLAPI PFNGLDISABLEPROC glad_glDisable;\n#define glDisable glad_glDisable\ntypedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap);\nGLAPI PFNGLENABLEPROC glad_glEnable;\n#define glEnable glad_glEnable\ntypedef void (APIENTRYP PFNGLFINISHPROC)(void);\nGLAPI PFNGLFINISHPROC glad_glFinish;\n#define glFinish glad_glFinish\ntypedef void (APIENTRYP PFNGLFLUSHPROC)(void);\nGLAPI PFNGLFLUSHPROC glad_glFlush;\n#define glFlush glad_glFlush\ntypedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor);\nGLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc;\n#define glBlendFunc glad_glBlendFunc\ntypedef void (APIENTRYP PFNGLLOGICOPPROC)(GLenum opcode);\nGLAPI PFNGLLOGICOPPROC glad_glLogicOp;\n#define glLogicOp glad_glLogicOp\ntypedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask);\nGLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc;\n#define glStencilFunc glad_glStencilFunc\ntypedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass);\nGLAPI PFNGLSTENCILOPPROC glad_glStencilOp;\n#define glStencilOp glad_glStencilOp\ntypedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func);\nGLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc;\n#define glDepthFunc glad_glDepthFunc\ntypedef void (APIENTRYP PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param);\nGLAPI PFNGLPIXELSTOREFPROC glad_glPixelStoref;\n#define glPixelStoref glad_glPixelStoref\ntypedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param);\nGLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei;\n#define glPixelStorei glad_glPixelStorei\ntypedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum src);\nGLAPI PFNGLREADBUFFERPROC glad_glReadBuffer;\n#define glReadBuffer glad_glReadBuffer\ntypedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);\nGLAPI PFNGLREADPIXELSPROC glad_glReadPixels;\n#define glReadPixels glad_glReadPixels\ntypedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data);\nGLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv;\n#define glGetBooleanv glad_glGetBooleanv\ntypedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble *data);\nGLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev;\n#define glGetDoublev glad_glGetDoublev\ntypedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void);\nGLAPI PFNGLGETERRORPROC glad_glGetError;\n#define glGetError glad_glGetError\ntypedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data);\nGLAPI PFNGLGETFLOATVPROC glad_glGetFloatv;\n#define glGetFloatv glad_glGetFloatv\ntypedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data);\nGLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv;\n#define glGetIntegerv glad_glGetIntegerv\ntypedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name);\nGLAPI PFNGLGETSTRINGPROC glad_glGetString;\n#define glGetString glad_glGetString\ntypedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void *pixels);\nGLAPI PFNGLGETTEXIMAGEPROC glad_glGetTexImage;\n#define glGetTexImage glad_glGetTexImage\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;\n#define glGetTexParameterfv glad_glGetTexParameterfv\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;\n#define glGetTexParameteriv glad_glGetTexParameteriv\ntypedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv;\n#define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv\ntypedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint *params);\nGLAPI PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv;\n#define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv\ntypedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap);\nGLAPI PFNGLISENABLEDPROC glad_glIsEnabled;\n#define glIsEnabled glad_glIsEnabled\ntypedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f);\nGLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange;\n#define glDepthRange glad_glDepthRange\ntypedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLVIEWPORTPROC glad_glViewport;\n#define glViewport glad_glViewport\ntypedef void (APIENTRYP PFNGLNEWLISTPROC)(GLuint list, GLenum mode);\nGLAPI PFNGLNEWLISTPROC glad_glNewList;\n#define glNewList glad_glNewList\ntypedef void (APIENTRYP PFNGLENDLISTPROC)(void);\nGLAPI PFNGLENDLISTPROC glad_glEndList;\n#define glEndList glad_glEndList\ntypedef void (APIENTRYP PFNGLCALLLISTPROC)(GLuint list);\nGLAPI PFNGLCALLLISTPROC glad_glCallList;\n#define glCallList glad_glCallList\ntypedef void (APIENTRYP PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void *lists);\nGLAPI PFNGLCALLLISTSPROC glad_glCallLists;\n#define glCallLists glad_glCallLists\ntypedef void (APIENTRYP PFNGLDELETELISTSPROC)(GLuint list, GLsizei range);\nGLAPI PFNGLDELETELISTSPROC glad_glDeleteLists;\n#define glDeleteLists glad_glDeleteLists\ntypedef GLuint (APIENTRYP PFNGLGENLISTSPROC)(GLsizei range);\nGLAPI PFNGLGENLISTSPROC glad_glGenLists;\n#define glGenLists glad_glGenLists\ntypedef void (APIENTRYP PFNGLLISTBASEPROC)(GLuint base);\nGLAPI PFNGLLISTBASEPROC glad_glListBase;\n#define glListBase glad_glListBase\ntypedef void (APIENTRYP PFNGLBEGINPROC)(GLenum mode);\nGLAPI PFNGLBEGINPROC glad_glBegin;\n#define glBegin glad_glBegin\ntypedef void (APIENTRYP PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap);\nGLAPI PFNGLBITMAPPROC glad_glBitmap;\n#define glBitmap glad_glBitmap\ntypedef void (APIENTRYP PFNGLCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue);\nGLAPI PFNGLCOLOR3BPROC glad_glColor3b;\n#define glColor3b glad_glColor3b\ntypedef void (APIENTRYP PFNGLCOLOR3BVPROC)(const GLbyte *v);\nGLAPI PFNGLCOLOR3BVPROC glad_glColor3bv;\n#define glColor3bv glad_glColor3bv\ntypedef void (APIENTRYP PFNGLCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue);\nGLAPI PFNGLCOLOR3DPROC glad_glColor3d;\n#define glColor3d glad_glColor3d\ntypedef void (APIENTRYP PFNGLCOLOR3DVPROC)(const GLdouble *v);\nGLAPI PFNGLCOLOR3DVPROC glad_glColor3dv;\n#define glColor3dv glad_glColor3dv\ntypedef void (APIENTRYP PFNGLCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue);\nGLAPI PFNGLCOLOR3FPROC glad_glColor3f;\n#define glColor3f glad_glColor3f\ntypedef void (APIENTRYP PFNGLCOLOR3FVPROC)(const GLfloat *v);\nGLAPI PFNGLCOLOR3FVPROC glad_glColor3fv;\n#define glColor3fv glad_glColor3fv\ntypedef void (APIENTRYP PFNGLCOLOR3IPROC)(GLint red, GLint green, GLint blue);\nGLAPI PFNGLCOLOR3IPROC glad_glColor3i;\n#define glColor3i glad_glColor3i\ntypedef void (APIENTRYP PFNGLCOLOR3IVPROC)(const GLint *v);\nGLAPI PFNGLCOLOR3IVPROC glad_glColor3iv;\n#define glColor3iv glad_glColor3iv\ntypedef void (APIENTRYP PFNGLCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue);\nGLAPI PFNGLCOLOR3SPROC glad_glColor3s;\n#define glColor3s glad_glColor3s\ntypedef void (APIENTRYP PFNGLCOLOR3SVPROC)(const GLshort *v);\nGLAPI PFNGLCOLOR3SVPROC glad_glColor3sv;\n#define glColor3sv glad_glColor3sv\ntypedef void (APIENTRYP PFNGLCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue);\nGLAPI PFNGLCOLOR3UBPROC glad_glColor3ub;\n#define glColor3ub glad_glColor3ub\ntypedef void (APIENTRYP PFNGLCOLOR3UBVPROC)(const GLubyte *v);\nGLAPI PFNGLCOLOR3UBVPROC glad_glColor3ubv;\n#define glColor3ubv glad_glColor3ubv\ntypedef void (APIENTRYP PFNGLCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue);\nGLAPI PFNGLCOLOR3UIPROC glad_glColor3ui;\n#define glColor3ui glad_glColor3ui\ntypedef void (APIENTRYP PFNGLCOLOR3UIVPROC)(const GLuint *v);\nGLAPI PFNGLCOLOR3UIVPROC glad_glColor3uiv;\n#define glColor3uiv glad_glColor3uiv\ntypedef void (APIENTRYP PFNGLCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue);\nGLAPI PFNGLCOLOR3USPROC glad_glColor3us;\n#define glColor3us glad_glColor3us\ntypedef void (APIENTRYP PFNGLCOLOR3USVPROC)(const GLushort *v);\nGLAPI PFNGLCOLOR3USVPROC glad_glColor3usv;\n#define glColor3usv glad_glColor3usv\ntypedef void (APIENTRYP PFNGLCOLOR4BPROC)(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha);\nGLAPI PFNGLCOLOR4BPROC glad_glColor4b;\n#define glColor4b glad_glColor4b\ntypedef void (APIENTRYP PFNGLCOLOR4BVPROC)(const GLbyte *v);\nGLAPI PFNGLCOLOR4BVPROC glad_glColor4bv;\n#define glColor4bv glad_glColor4bv\ntypedef void (APIENTRYP PFNGLCOLOR4DPROC)(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha);\nGLAPI PFNGLCOLOR4DPROC glad_glColor4d;\n#define glColor4d glad_glColor4d\ntypedef void (APIENTRYP PFNGLCOLOR4DVPROC)(const GLdouble *v);\nGLAPI PFNGLCOLOR4DVPROC glad_glColor4dv;\n#define glColor4dv glad_glColor4dv\ntypedef void (APIENTRYP PFNGLCOLOR4FPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nGLAPI PFNGLCOLOR4FPROC glad_glColor4f;\n#define glColor4f glad_glColor4f\ntypedef void (APIENTRYP PFNGLCOLOR4FVPROC)(const GLfloat *v);\nGLAPI PFNGLCOLOR4FVPROC glad_glColor4fv;\n#define glColor4fv glad_glColor4fv\ntypedef void (APIENTRYP PFNGLCOLOR4IPROC)(GLint red, GLint green, GLint blue, GLint alpha);\nGLAPI PFNGLCOLOR4IPROC glad_glColor4i;\n#define glColor4i glad_glColor4i\ntypedef void (APIENTRYP PFNGLCOLOR4IVPROC)(const GLint *v);\nGLAPI PFNGLCOLOR4IVPROC glad_glColor4iv;\n#define glColor4iv glad_glColor4iv\ntypedef void (APIENTRYP PFNGLCOLOR4SPROC)(GLshort red, GLshort green, GLshort blue, GLshort alpha);\nGLAPI PFNGLCOLOR4SPROC glad_glColor4s;\n#define glColor4s glad_glColor4s\ntypedef void (APIENTRYP PFNGLCOLOR4SVPROC)(const GLshort *v);\nGLAPI PFNGLCOLOR4SVPROC glad_glColor4sv;\n#define glColor4sv glad_glColor4sv\ntypedef void (APIENTRYP PFNGLCOLOR4UBPROC)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);\nGLAPI PFNGLCOLOR4UBPROC glad_glColor4ub;\n#define glColor4ub glad_glColor4ub\ntypedef void (APIENTRYP PFNGLCOLOR4UBVPROC)(const GLubyte *v);\nGLAPI PFNGLCOLOR4UBVPROC glad_glColor4ubv;\n#define glColor4ubv glad_glColor4ubv\ntypedef void (APIENTRYP PFNGLCOLOR4UIPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha);\nGLAPI PFNGLCOLOR4UIPROC glad_glColor4ui;\n#define glColor4ui glad_glColor4ui\ntypedef void (APIENTRYP PFNGLCOLOR4UIVPROC)(const GLuint *v);\nGLAPI PFNGLCOLOR4UIVPROC glad_glColor4uiv;\n#define glColor4uiv glad_glColor4uiv\ntypedef void (APIENTRYP PFNGLCOLOR4USPROC)(GLushort red, GLushort green, GLushort blue, GLushort alpha);\nGLAPI PFNGLCOLOR4USPROC glad_glColor4us;\n#define glColor4us glad_glColor4us\ntypedef void (APIENTRYP PFNGLCOLOR4USVPROC)(const GLushort *v);\nGLAPI PFNGLCOLOR4USVPROC glad_glColor4usv;\n#define glColor4usv glad_glColor4usv\ntypedef void (APIENTRYP PFNGLEDGEFLAGPROC)(GLboolean flag);\nGLAPI PFNGLEDGEFLAGPROC glad_glEdgeFlag;\n#define glEdgeFlag glad_glEdgeFlag\ntypedef void (APIENTRYP PFNGLEDGEFLAGVPROC)(const GLboolean *flag);\nGLAPI PFNGLEDGEFLAGVPROC glad_glEdgeFlagv;\n#define glEdgeFlagv glad_glEdgeFlagv\ntypedef void (APIENTRYP PFNGLENDPROC)(void);\nGLAPI PFNGLENDPROC glad_glEnd;\n#define glEnd glad_glEnd\ntypedef void (APIENTRYP PFNGLINDEXDPROC)(GLdouble c);\nGLAPI PFNGLINDEXDPROC glad_glIndexd;\n#define glIndexd glad_glIndexd\ntypedef void (APIENTRYP PFNGLINDEXDVPROC)(const GLdouble *c);\nGLAPI PFNGLINDEXDVPROC glad_glIndexdv;\n#define glIndexdv glad_glIndexdv\ntypedef void (APIENTRYP PFNGLINDEXFPROC)(GLfloat c);\nGLAPI PFNGLINDEXFPROC glad_glIndexf;\n#define glIndexf glad_glIndexf\ntypedef void (APIENTRYP PFNGLINDEXFVPROC)(const GLfloat *c);\nGLAPI PFNGLINDEXFVPROC glad_glIndexfv;\n#define glIndexfv glad_glIndexfv\ntypedef void (APIENTRYP PFNGLINDEXIPROC)(GLint c);\nGLAPI PFNGLINDEXIPROC glad_glIndexi;\n#define glIndexi glad_glIndexi\ntypedef void (APIENTRYP PFNGLINDEXIVPROC)(const GLint *c);\nGLAPI PFNGLINDEXIVPROC glad_glIndexiv;\n#define glIndexiv glad_glIndexiv\ntypedef void (APIENTRYP PFNGLINDEXSPROC)(GLshort c);\nGLAPI PFNGLINDEXSPROC glad_glIndexs;\n#define glIndexs glad_glIndexs\ntypedef void (APIENTRYP PFNGLINDEXSVPROC)(const GLshort *c);\nGLAPI PFNGLINDEXSVPROC glad_glIndexsv;\n#define glIndexsv glad_glIndexsv\ntypedef void (APIENTRYP PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz);\nGLAPI PFNGLNORMAL3BPROC glad_glNormal3b;\n#define glNormal3b glad_glNormal3b\ntypedef void (APIENTRYP PFNGLNORMAL3BVPROC)(const GLbyte *v);\nGLAPI PFNGLNORMAL3BVPROC glad_glNormal3bv;\n#define glNormal3bv glad_glNormal3bv\ntypedef void (APIENTRYP PFNGLNORMAL3DPROC)(GLdouble nx, GLdouble ny, GLdouble nz);\nGLAPI PFNGLNORMAL3DPROC glad_glNormal3d;\n#define glNormal3d glad_glNormal3d\ntypedef void (APIENTRYP PFNGLNORMAL3DVPROC)(const GLdouble *v);\nGLAPI PFNGLNORMAL3DVPROC glad_glNormal3dv;\n#define glNormal3dv glad_glNormal3dv\ntypedef void (APIENTRYP PFNGLNORMAL3FPROC)(GLfloat nx, GLfloat ny, GLfloat nz);\nGLAPI PFNGLNORMAL3FPROC glad_glNormal3f;\n#define glNormal3f glad_glNormal3f\ntypedef void (APIENTRYP PFNGLNORMAL3FVPROC)(const GLfloat *v);\nGLAPI PFNGLNORMAL3FVPROC glad_glNormal3fv;\n#define glNormal3fv glad_glNormal3fv\ntypedef void (APIENTRYP PFNGLNORMAL3IPROC)(GLint nx, GLint ny, GLint nz);\nGLAPI PFNGLNORMAL3IPROC glad_glNormal3i;\n#define glNormal3i glad_glNormal3i\ntypedef void (APIENTRYP PFNGLNORMAL3IVPROC)(const GLint *v);\nGLAPI PFNGLNORMAL3IVPROC glad_glNormal3iv;\n#define glNormal3iv glad_glNormal3iv\ntypedef void (APIENTRYP PFNGLNORMAL3SPROC)(GLshort nx, GLshort ny, GLshort nz);\nGLAPI PFNGLNORMAL3SPROC glad_glNormal3s;\n#define glNormal3s glad_glNormal3s\ntypedef void (APIENTRYP PFNGLNORMAL3SVPROC)(const GLshort *v);\nGLAPI PFNGLNORMAL3SVPROC glad_glNormal3sv;\n#define glNormal3sv glad_glNormal3sv\ntypedef void (APIENTRYP PFNGLRASTERPOS2DPROC)(GLdouble x, GLdouble y);\nGLAPI PFNGLRASTERPOS2DPROC glad_glRasterPos2d;\n#define glRasterPos2d glad_glRasterPos2d\ntypedef void (APIENTRYP PFNGLRASTERPOS2DVPROC)(const GLdouble *v);\nGLAPI PFNGLRASTERPOS2DVPROC glad_glRasterPos2dv;\n#define glRasterPos2dv glad_glRasterPos2dv\ntypedef void (APIENTRYP PFNGLRASTERPOS2FPROC)(GLfloat x, GLfloat y);\nGLAPI PFNGLRASTERPOS2FPROC glad_glRasterPos2f;\n#define glRasterPos2f glad_glRasterPos2f\ntypedef void (APIENTRYP PFNGLRASTERPOS2FVPROC)(const GLfloat *v);\nGLAPI PFNGLRASTERPOS2FVPROC glad_glRasterPos2fv;\n#define glRasterPos2fv glad_glRasterPos2fv\ntypedef void (APIENTRYP PFNGLRASTERPOS2IPROC)(GLint x, GLint y);\nGLAPI PFNGLRASTERPOS2IPROC glad_glRasterPos2i;\n#define glRasterPos2i glad_glRasterPos2i\ntypedef void (APIENTRYP PFNGLRASTERPOS2IVPROC)(const GLint *v);\nGLAPI PFNGLRASTERPOS2IVPROC glad_glRasterPos2iv;\n#define glRasterPos2iv glad_glRasterPos2iv\ntypedef void (APIENTRYP PFNGLRASTERPOS2SPROC)(GLshort x, GLshort y);\nGLAPI PFNGLRASTERPOS2SPROC glad_glRasterPos2s;\n#define glRasterPos2s glad_glRasterPos2s\ntypedef void (APIENTRYP PFNGLRASTERPOS2SVPROC)(const GLshort *v);\nGLAPI PFNGLRASTERPOS2SVPROC glad_glRasterPos2sv;\n#define glRasterPos2sv glad_glRasterPos2sv\ntypedef void (APIENTRYP PFNGLRASTERPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z);\nGLAPI PFNGLRASTERPOS3DPROC glad_glRasterPos3d;\n#define glRasterPos3d glad_glRasterPos3d\ntypedef void (APIENTRYP PFNGLRASTERPOS3DVPROC)(const GLdouble *v);\nGLAPI PFNGLRASTERPOS3DVPROC glad_glRasterPos3dv;\n#define glRasterPos3dv glad_glRasterPos3dv\ntypedef void (APIENTRYP PFNGLRASTERPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z);\nGLAPI PFNGLRASTERPOS3FPROC glad_glRasterPos3f;\n#define glRasterPos3f glad_glRasterPos3f\ntypedef void (APIENTRYP PFNGLRASTERPOS3FVPROC)(const GLfloat *v);\nGLAPI PFNGLRASTERPOS3FVPROC glad_glRasterPos3fv;\n#define glRasterPos3fv glad_glRasterPos3fv\ntypedef void (APIENTRYP PFNGLRASTERPOS3IPROC)(GLint x, GLint y, GLint z);\nGLAPI PFNGLRASTERPOS3IPROC glad_glRasterPos3i;\n#define glRasterPos3i glad_glRasterPos3i\ntypedef void (APIENTRYP PFNGLRASTERPOS3IVPROC)(const GLint *v);\nGLAPI PFNGLRASTERPOS3IVPROC glad_glRasterPos3iv;\n#define glRasterPos3iv glad_glRasterPos3iv\ntypedef void (APIENTRYP PFNGLRASTERPOS3SPROC)(GLshort x, GLshort y, GLshort z);\nGLAPI PFNGLRASTERPOS3SPROC glad_glRasterPos3s;\n#define glRasterPos3s glad_glRasterPos3s\ntypedef void (APIENTRYP PFNGLRASTERPOS3SVPROC)(const GLshort *v);\nGLAPI PFNGLRASTERPOS3SVPROC glad_glRasterPos3sv;\n#define glRasterPos3sv glad_glRasterPos3sv\ntypedef void (APIENTRYP PFNGLRASTERPOS4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nGLAPI PFNGLRASTERPOS4DPROC glad_glRasterPos4d;\n#define glRasterPos4d glad_glRasterPos4d\ntypedef void (APIENTRYP PFNGLRASTERPOS4DVPROC)(const GLdouble *v);\nGLAPI PFNGLRASTERPOS4DVPROC glad_glRasterPos4dv;\n#define glRasterPos4dv glad_glRasterPos4dv\ntypedef void (APIENTRYP PFNGLRASTERPOS4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w);\nGLAPI PFNGLRASTERPOS4FPROC glad_glRasterPos4f;\n#define glRasterPos4f glad_glRasterPos4f\ntypedef void (APIENTRYP PFNGLRASTERPOS4FVPROC)(const GLfloat *v);\nGLAPI PFNGLRASTERPOS4FVPROC glad_glRasterPos4fv;\n#define glRasterPos4fv glad_glRasterPos4fv\ntypedef void (APIENTRYP PFNGLRASTERPOS4IPROC)(GLint x, GLint y, GLint z, GLint w);\nGLAPI PFNGLRASTERPOS4IPROC glad_glRasterPos4i;\n#define glRasterPos4i glad_glRasterPos4i\ntypedef void (APIENTRYP PFNGLRASTERPOS4IVPROC)(const GLint *v);\nGLAPI PFNGLRASTERPOS4IVPROC glad_glRasterPos4iv;\n#define glRasterPos4iv glad_glRasterPos4iv\ntypedef void (APIENTRYP PFNGLRASTERPOS4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w);\nGLAPI PFNGLRASTERPOS4SPROC glad_glRasterPos4s;\n#define glRasterPos4s glad_glRasterPos4s\ntypedef void (APIENTRYP PFNGLRASTERPOS4SVPROC)(const GLshort *v);\nGLAPI PFNGLRASTERPOS4SVPROC glad_glRasterPos4sv;\n#define glRasterPos4sv glad_glRasterPos4sv\ntypedef void (APIENTRYP PFNGLRECTDPROC)(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2);\nGLAPI PFNGLRECTDPROC glad_glRectd;\n#define glRectd glad_glRectd\ntypedef void (APIENTRYP PFNGLRECTDVPROC)(const GLdouble *v1, const GLdouble *v2);\nGLAPI PFNGLRECTDVPROC glad_glRectdv;\n#define glRectdv glad_glRectdv\ntypedef void (APIENTRYP PFNGLRECTFPROC)(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);\nGLAPI PFNGLRECTFPROC glad_glRectf;\n#define glRectf glad_glRectf\ntypedef void (APIENTRYP PFNGLRECTFVPROC)(const GLfloat *v1, const GLfloat *v2);\nGLAPI PFNGLRECTFVPROC glad_glRectfv;\n#define glRectfv glad_glRectfv\ntypedef void (APIENTRYP PFNGLRECTIPROC)(GLint x1, GLint y1, GLint x2, GLint y2);\nGLAPI PFNGLRECTIPROC glad_glRecti;\n#define glRecti glad_glRecti\ntypedef void (APIENTRYP PFNGLRECTIVPROC)(const GLint *v1, const GLint *v2);\nGLAPI PFNGLRECTIVPROC glad_glRectiv;\n#define glRectiv glad_glRectiv\ntypedef void (APIENTRYP PFNGLRECTSPROC)(GLshort x1, GLshort y1, GLshort x2, GLshort y2);\nGLAPI PFNGLRECTSPROC glad_glRects;\n#define glRects glad_glRects\ntypedef void (APIENTRYP PFNGLRECTSVPROC)(const GLshort *v1, const GLshort *v2);\nGLAPI PFNGLRECTSVPROC glad_glRectsv;\n#define glRectsv glad_glRectsv\ntypedef void (APIENTRYP PFNGLTEXCOORD1DPROC)(GLdouble s);\nGLAPI PFNGLTEXCOORD1DPROC glad_glTexCoord1d;\n#define glTexCoord1d glad_glTexCoord1d\ntypedef void (APIENTRYP PFNGLTEXCOORD1DVPROC)(const GLdouble *v);\nGLAPI PFNGLTEXCOORD1DVPROC glad_glTexCoord1dv;\n#define glTexCoord1dv glad_glTexCoord1dv\ntypedef void (APIENTRYP PFNGLTEXCOORD1FPROC)(GLfloat s);\nGLAPI PFNGLTEXCOORD1FPROC glad_glTexCoord1f;\n#define glTexCoord1f glad_glTexCoord1f\ntypedef void (APIENTRYP PFNGLTEXCOORD1FVPROC)(const GLfloat *v);\nGLAPI PFNGLTEXCOORD1FVPROC glad_glTexCoord1fv;\n#define glTexCoord1fv glad_glTexCoord1fv\ntypedef void (APIENTRYP PFNGLTEXCOORD1IPROC)(GLint s);\nGLAPI PFNGLTEXCOORD1IPROC glad_glTexCoord1i;\n#define glTexCoord1i glad_glTexCoord1i\ntypedef void (APIENTRYP PFNGLTEXCOORD1IVPROC)(const GLint *v);\nGLAPI PFNGLTEXCOORD1IVPROC glad_glTexCoord1iv;\n#define glTexCoord1iv glad_glTexCoord1iv\ntypedef void (APIENTRYP PFNGLTEXCOORD1SPROC)(GLshort s);\nGLAPI PFNGLTEXCOORD1SPROC glad_glTexCoord1s;\n#define glTexCoord1s glad_glTexCoord1s\ntypedef void (APIENTRYP PFNGLTEXCOORD1SVPROC)(const GLshort *v);\nGLAPI PFNGLTEXCOORD1SVPROC glad_glTexCoord1sv;\n#define glTexCoord1sv glad_glTexCoord1sv\ntypedef void (APIENTRYP PFNGLTEXCOORD2DPROC)(GLdouble s, GLdouble t);\nGLAPI PFNGLTEXCOORD2DPROC glad_glTexCoord2d;\n#define glTexCoord2d glad_glTexCoord2d\ntypedef void (APIENTRYP PFNGLTEXCOORD2DVPROC)(const GLdouble *v);\nGLAPI PFNGLTEXCOORD2DVPROC glad_glTexCoord2dv;\n#define glTexCoord2dv glad_glTexCoord2dv\ntypedef void (APIENTRYP PFNGLTEXCOORD2FPROC)(GLfloat s, GLfloat t);\nGLAPI PFNGLTEXCOORD2FPROC glad_glTexCoord2f;\n#define glTexCoord2f glad_glTexCoord2f\ntypedef void (APIENTRYP PFNGLTEXCOORD2FVPROC)(const GLfloat *v);\nGLAPI PFNGLTEXCOORD2FVPROC glad_glTexCoord2fv;\n#define glTexCoord2fv glad_glTexCoord2fv\ntypedef void (APIENTRYP PFNGLTEXCOORD2IPROC)(GLint s, GLint t);\nGLAPI PFNGLTEXCOORD2IPROC glad_glTexCoord2i;\n#define glTexCoord2i glad_glTexCoord2i\ntypedef void (APIENTRYP PFNGLTEXCOORD2IVPROC)(const GLint *v);\nGLAPI PFNGLTEXCOORD2IVPROC glad_glTexCoord2iv;\n#define glTexCoord2iv glad_glTexCoord2iv\ntypedef void (APIENTRYP PFNGLTEXCOORD2SPROC)(GLshort s, GLshort t);\nGLAPI PFNGLTEXCOORD2SPROC glad_glTexCoord2s;\n#define glTexCoord2s glad_glTexCoord2s\ntypedef void (APIENTRYP PFNGLTEXCOORD2SVPROC)(const GLshort *v);\nGLAPI PFNGLTEXCOORD2SVPROC glad_glTexCoord2sv;\n#define glTexCoord2sv glad_glTexCoord2sv\ntypedef void (APIENTRYP PFNGLTEXCOORD3DPROC)(GLdouble s, GLdouble t, GLdouble r);\nGLAPI PFNGLTEXCOORD3DPROC glad_glTexCoord3d;\n#define glTexCoord3d glad_glTexCoord3d\ntypedef void (APIENTRYP PFNGLTEXCOORD3DVPROC)(const GLdouble *v);\nGLAPI PFNGLTEXCOORD3DVPROC glad_glTexCoord3dv;\n#define glTexCoord3dv glad_glTexCoord3dv\ntypedef void (APIENTRYP PFNGLTEXCOORD3FPROC)(GLfloat s, GLfloat t, GLfloat r);\nGLAPI PFNGLTEXCOORD3FPROC glad_glTexCoord3f;\n#define glTexCoord3f glad_glTexCoord3f\ntypedef void (APIENTRYP PFNGLTEXCOORD3FVPROC)(const GLfloat *v);\nGLAPI PFNGLTEXCOORD3FVPROC glad_glTexCoord3fv;\n#define glTexCoord3fv glad_glTexCoord3fv\ntypedef void (APIENTRYP PFNGLTEXCOORD3IPROC)(GLint s, GLint t, GLint r);\nGLAPI PFNGLTEXCOORD3IPROC glad_glTexCoord3i;\n#define glTexCoord3i glad_glTexCoord3i\ntypedef void (APIENTRYP PFNGLTEXCOORD3IVPROC)(const GLint *v);\nGLAPI PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv;\n#define glTexCoord3iv glad_glTexCoord3iv\ntypedef void (APIENTRYP PFNGLTEXCOORD3SPROC)(GLshort s, GLshort t, GLshort r);\nGLAPI PFNGLTEXCOORD3SPROC glad_glTexCoord3s;\n#define glTexCoord3s glad_glTexCoord3s\ntypedef void (APIENTRYP PFNGLTEXCOORD3SVPROC)(const GLshort *v);\nGLAPI PFNGLTEXCOORD3SVPROC glad_glTexCoord3sv;\n#define glTexCoord3sv glad_glTexCoord3sv\ntypedef void (APIENTRYP PFNGLTEXCOORD4DPROC)(GLdouble s, GLdouble t, GLdouble r, GLdouble q);\nGLAPI PFNGLTEXCOORD4DPROC glad_glTexCoord4d;\n#define glTexCoord4d glad_glTexCoord4d\ntypedef void (APIENTRYP PFNGLTEXCOORD4DVPROC)(const GLdouble *v);\nGLAPI PFNGLTEXCOORD4DVPROC glad_glTexCoord4dv;\n#define glTexCoord4dv glad_glTexCoord4dv\ntypedef void (APIENTRYP PFNGLTEXCOORD4FPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat q);\nGLAPI PFNGLTEXCOORD4FPROC glad_glTexCoord4f;\n#define glTexCoord4f glad_glTexCoord4f\ntypedef void (APIENTRYP PFNGLTEXCOORD4FVPROC)(const GLfloat *v);\nGLAPI PFNGLTEXCOORD4FVPROC glad_glTexCoord4fv;\n#define glTexCoord4fv glad_glTexCoord4fv\ntypedef void (APIENTRYP PFNGLTEXCOORD4IPROC)(GLint s, GLint t, GLint r, GLint q);\nGLAPI PFNGLTEXCOORD4IPROC glad_glTexCoord4i;\n#define glTexCoord4i glad_glTexCoord4i\ntypedef void (APIENTRYP PFNGLTEXCOORD4IVPROC)(const GLint *v);\nGLAPI PFNGLTEXCOORD4IVPROC glad_glTexCoord4iv;\n#define glTexCoord4iv glad_glTexCoord4iv\ntypedef void (APIENTRYP PFNGLTEXCOORD4SPROC)(GLshort s, GLshort t, GLshort r, GLshort q);\nGLAPI PFNGLTEXCOORD4SPROC glad_glTexCoord4s;\n#define glTexCoord4s glad_glTexCoord4s\ntypedef void (APIENTRYP PFNGLTEXCOORD4SVPROC)(const GLshort *v);\nGLAPI PFNGLTEXCOORD4SVPROC glad_glTexCoord4sv;\n#define glTexCoord4sv glad_glTexCoord4sv\ntypedef void (APIENTRYP PFNGLVERTEX2DPROC)(GLdouble x, GLdouble y);\nGLAPI PFNGLVERTEX2DPROC glad_glVertex2d;\n#define glVertex2d glad_glVertex2d\ntypedef void (APIENTRYP PFNGLVERTEX2DVPROC)(const GLdouble *v);\nGLAPI PFNGLVERTEX2DVPROC glad_glVertex2dv;\n#define glVertex2dv glad_glVertex2dv\ntypedef void (APIENTRYP PFNGLVERTEX2FPROC)(GLfloat x, GLfloat y);\nGLAPI PFNGLVERTEX2FPROC glad_glVertex2f;\n#define glVertex2f glad_glVertex2f\ntypedef void (APIENTRYP PFNGLVERTEX2FVPROC)(const GLfloat *v);\nGLAPI PFNGLVERTEX2FVPROC glad_glVertex2fv;\n#define glVertex2fv glad_glVertex2fv\ntypedef void (APIENTRYP PFNGLVERTEX2IPROC)(GLint x, GLint y);\nGLAPI PFNGLVERTEX2IPROC glad_glVertex2i;\n#define glVertex2i glad_glVertex2i\ntypedef void (APIENTRYP PFNGLVERTEX2IVPROC)(const GLint *v);\nGLAPI PFNGLVERTEX2IVPROC glad_glVertex2iv;\n#define glVertex2iv glad_glVertex2iv\ntypedef void (APIENTRYP PFNGLVERTEX2SPROC)(GLshort x, GLshort y);\nGLAPI PFNGLVERTEX2SPROC glad_glVertex2s;\n#define glVertex2s glad_glVertex2s\ntypedef void (APIENTRYP PFNGLVERTEX2SVPROC)(const GLshort *v);\nGLAPI PFNGLVERTEX2SVPROC glad_glVertex2sv;\n#define glVertex2sv glad_glVertex2sv\ntypedef void (APIENTRYP PFNGLVERTEX3DPROC)(GLdouble x, GLdouble y, GLdouble z);\nGLAPI PFNGLVERTEX3DPROC glad_glVertex3d;\n#define glVertex3d glad_glVertex3d\ntypedef void (APIENTRYP PFNGLVERTEX3DVPROC)(const GLdouble *v);\nGLAPI PFNGLVERTEX3DVPROC glad_glVertex3dv;\n#define glVertex3dv glad_glVertex3dv\ntypedef void (APIENTRYP PFNGLVERTEX3FPROC)(GLfloat x, GLfloat y, GLfloat z);\nGLAPI PFNGLVERTEX3FPROC glad_glVertex3f;\n#define glVertex3f glad_glVertex3f\ntypedef void (APIENTRYP PFNGLVERTEX3FVPROC)(const GLfloat *v);\nGLAPI PFNGLVERTEX3FVPROC glad_glVertex3fv;\n#define glVertex3fv glad_glVertex3fv\ntypedef void (APIENTRYP PFNGLVERTEX3IPROC)(GLint x, GLint y, GLint z);\nGLAPI PFNGLVERTEX3IPROC glad_glVertex3i;\n#define glVertex3i glad_glVertex3i\ntypedef void (APIENTRYP PFNGLVERTEX3IVPROC)(const GLint *v);\nGLAPI PFNGLVERTEX3IVPROC glad_glVertex3iv;\n#define glVertex3iv glad_glVertex3iv\ntypedef void (APIENTRYP PFNGLVERTEX3SPROC)(GLshort x, GLshort y, GLshort z);\nGLAPI PFNGLVERTEX3SPROC glad_glVertex3s;\n#define glVertex3s glad_glVertex3s\ntypedef void (APIENTRYP PFNGLVERTEX3SVPROC)(const GLshort *v);\nGLAPI PFNGLVERTEX3SVPROC glad_glVertex3sv;\n#define glVertex3sv glad_glVertex3sv\ntypedef void (APIENTRYP PFNGLVERTEX4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nGLAPI PFNGLVERTEX4DPROC glad_glVertex4d;\n#define glVertex4d glad_glVertex4d\ntypedef void (APIENTRYP PFNGLVERTEX4DVPROC)(const GLdouble *v);\nGLAPI PFNGLVERTEX4DVPROC glad_glVertex4dv;\n#define glVertex4dv glad_glVertex4dv\ntypedef void (APIENTRYP PFNGLVERTEX4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w);\nGLAPI PFNGLVERTEX4FPROC glad_glVertex4f;\n#define glVertex4f glad_glVertex4f\ntypedef void (APIENTRYP PFNGLVERTEX4FVPROC)(const GLfloat *v);\nGLAPI PFNGLVERTEX4FVPROC glad_glVertex4fv;\n#define glVertex4fv glad_glVertex4fv\ntypedef void (APIENTRYP PFNGLVERTEX4IPROC)(GLint x, GLint y, GLint z, GLint w);\nGLAPI PFNGLVERTEX4IPROC glad_glVertex4i;\n#define glVertex4i glad_glVertex4i\ntypedef void (APIENTRYP PFNGLVERTEX4IVPROC)(const GLint *v);\nGLAPI PFNGLVERTEX4IVPROC glad_glVertex4iv;\n#define glVertex4iv glad_glVertex4iv\ntypedef void (APIENTRYP PFNGLVERTEX4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w);\nGLAPI PFNGLVERTEX4SPROC glad_glVertex4s;\n#define glVertex4s glad_glVertex4s\ntypedef void (APIENTRYP PFNGLVERTEX4SVPROC)(const GLshort *v);\nGLAPI PFNGLVERTEX4SVPROC glad_glVertex4sv;\n#define glVertex4sv glad_glVertex4sv\ntypedef void (APIENTRYP PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble *equation);\nGLAPI PFNGLCLIPPLANEPROC glad_glClipPlane;\n#define glClipPlane glad_glClipPlane\ntypedef void (APIENTRYP PFNGLCOLORMATERIALPROC)(GLenum face, GLenum mode);\nGLAPI PFNGLCOLORMATERIALPROC glad_glColorMaterial;\n#define glColorMaterial glad_glColorMaterial\ntypedef void (APIENTRYP PFNGLFOGFPROC)(GLenum pname, GLfloat param);\nGLAPI PFNGLFOGFPROC glad_glFogf;\n#define glFogf glad_glFogf\ntypedef void (APIENTRYP PFNGLFOGFVPROC)(GLenum pname, const GLfloat *params);\nGLAPI PFNGLFOGFVPROC glad_glFogfv;\n#define glFogfv glad_glFogfv\ntypedef void (APIENTRYP PFNGLFOGIPROC)(GLenum pname, GLint param);\nGLAPI PFNGLFOGIPROC glad_glFogi;\n#define glFogi glad_glFogi\ntypedef void (APIENTRYP PFNGLFOGIVPROC)(GLenum pname, const GLint *params);\nGLAPI PFNGLFOGIVPROC glad_glFogiv;\n#define glFogiv glad_glFogiv\ntypedef void (APIENTRYP PFNGLLIGHTFPROC)(GLenum light, GLenum pname, GLfloat param);\nGLAPI PFNGLLIGHTFPROC glad_glLightf;\n#define glLightf glad_glLightf\ntypedef void (APIENTRYP PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat *params);\nGLAPI PFNGLLIGHTFVPROC glad_glLightfv;\n#define glLightfv glad_glLightfv\ntypedef void (APIENTRYP PFNGLLIGHTIPROC)(GLenum light, GLenum pname, GLint param);\nGLAPI PFNGLLIGHTIPROC glad_glLighti;\n#define glLighti glad_glLighti\ntypedef void (APIENTRYP PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint *params);\nGLAPI PFNGLLIGHTIVPROC glad_glLightiv;\n#define glLightiv glad_glLightiv\ntypedef void (APIENTRYP PFNGLLIGHTMODELFPROC)(GLenum pname, GLfloat param);\nGLAPI PFNGLLIGHTMODELFPROC glad_glLightModelf;\n#define glLightModelf glad_glLightModelf\ntypedef void (APIENTRYP PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat *params);\nGLAPI PFNGLLIGHTMODELFVPROC glad_glLightModelfv;\n#define glLightModelfv glad_glLightModelfv\ntypedef void (APIENTRYP PFNGLLIGHTMODELIPROC)(GLenum pname, GLint param);\nGLAPI PFNGLLIGHTMODELIPROC glad_glLightModeli;\n#define glLightModeli glad_glLightModeli\ntypedef void (APIENTRYP PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint *params);\nGLAPI PFNGLLIGHTMODELIVPROC glad_glLightModeliv;\n#define glLightModeliv glad_glLightModeliv\ntypedef void (APIENTRYP PFNGLLINESTIPPLEPROC)(GLint factor, GLushort pattern);\nGLAPI PFNGLLINESTIPPLEPROC glad_glLineStipple;\n#define glLineStipple glad_glLineStipple\ntypedef void (APIENTRYP PFNGLMATERIALFPROC)(GLenum face, GLenum pname, GLfloat param);\nGLAPI PFNGLMATERIALFPROC glad_glMaterialf;\n#define glMaterialf glad_glMaterialf\ntypedef void (APIENTRYP PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat *params);\nGLAPI PFNGLMATERIALFVPROC glad_glMaterialfv;\n#define glMaterialfv glad_glMaterialfv\ntypedef void (APIENTRYP PFNGLMATERIALIPROC)(GLenum face, GLenum pname, GLint param);\nGLAPI PFNGLMATERIALIPROC glad_glMateriali;\n#define glMateriali glad_glMateriali\ntypedef void (APIENTRYP PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint *params);\nGLAPI PFNGLMATERIALIVPROC glad_glMaterialiv;\n#define glMaterialiv glad_glMaterialiv\ntypedef void (APIENTRYP PFNGLPOLYGONSTIPPLEPROC)(const GLubyte *mask);\nGLAPI PFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple;\n#define glPolygonStipple glad_glPolygonStipple\ntypedef void (APIENTRYP PFNGLSHADEMODELPROC)(GLenum mode);\nGLAPI PFNGLSHADEMODELPROC glad_glShadeModel;\n#define glShadeModel glad_glShadeModel\ntypedef void (APIENTRYP PFNGLTEXENVFPROC)(GLenum target, GLenum pname, GLfloat param);\nGLAPI PFNGLTEXENVFPROC glad_glTexEnvf;\n#define glTexEnvf glad_glTexEnvf\ntypedef void (APIENTRYP PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat *params);\nGLAPI PFNGLTEXENVFVPROC glad_glTexEnvfv;\n#define glTexEnvfv glad_glTexEnvfv\ntypedef void (APIENTRYP PFNGLTEXENVIPROC)(GLenum target, GLenum pname, GLint param);\nGLAPI PFNGLTEXENVIPROC glad_glTexEnvi;\n#define glTexEnvi glad_glTexEnvi\ntypedef void (APIENTRYP PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint *params);\nGLAPI PFNGLTEXENVIVPROC glad_glTexEnviv;\n#define glTexEnviv glad_glTexEnviv\ntypedef void (APIENTRYP PFNGLTEXGENDPROC)(GLenum coord, GLenum pname, GLdouble param);\nGLAPI PFNGLTEXGENDPROC glad_glTexGend;\n#define glTexGend glad_glTexGend\ntypedef void (APIENTRYP PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble *params);\nGLAPI PFNGLTEXGENDVPROC glad_glTexGendv;\n#define glTexGendv glad_glTexGendv\ntypedef void (APIENTRYP PFNGLTEXGENFPROC)(GLenum coord, GLenum pname, GLfloat param);\nGLAPI PFNGLTEXGENFPROC glad_glTexGenf;\n#define glTexGenf glad_glTexGenf\ntypedef void (APIENTRYP PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat *params);\nGLAPI PFNGLTEXGENFVPROC glad_glTexGenfv;\n#define glTexGenfv glad_glTexGenfv\ntypedef void (APIENTRYP PFNGLTEXGENIPROC)(GLenum coord, GLenum pname, GLint param);\nGLAPI PFNGLTEXGENIPROC glad_glTexGeni;\n#define glTexGeni glad_glTexGeni\ntypedef void (APIENTRYP PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint *params);\nGLAPI PFNGLTEXGENIVPROC glad_glTexGeniv;\n#define glTexGeniv glad_glTexGeniv\ntypedef void (APIENTRYP PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat *buffer);\nGLAPI PFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer;\n#define glFeedbackBuffer glad_glFeedbackBuffer\ntypedef void (APIENTRYP PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint *buffer);\nGLAPI PFNGLSELECTBUFFERPROC glad_glSelectBuffer;\n#define glSelectBuffer glad_glSelectBuffer\ntypedef GLint (APIENTRYP PFNGLRENDERMODEPROC)(GLenum mode);\nGLAPI PFNGLRENDERMODEPROC glad_glRenderMode;\n#define glRenderMode glad_glRenderMode\ntypedef void (APIENTRYP PFNGLINITNAMESPROC)(void);\nGLAPI PFNGLINITNAMESPROC glad_glInitNames;\n#define glInitNames glad_glInitNames\ntypedef void (APIENTRYP PFNGLLOADNAMEPROC)(GLuint name);\nGLAPI PFNGLLOADNAMEPROC glad_glLoadName;\n#define glLoadName glad_glLoadName\ntypedef void (APIENTRYP PFNGLPASSTHROUGHPROC)(GLfloat token);\nGLAPI PFNGLPASSTHROUGHPROC glad_glPassThrough;\n#define glPassThrough glad_glPassThrough\ntypedef void (APIENTRYP PFNGLPOPNAMEPROC)(void);\nGLAPI PFNGLPOPNAMEPROC glad_glPopName;\n#define glPopName glad_glPopName\ntypedef void (APIENTRYP PFNGLPUSHNAMEPROC)(GLuint name);\nGLAPI PFNGLPUSHNAMEPROC glad_glPushName;\n#define glPushName glad_glPushName\ntypedef void (APIENTRYP PFNGLCLEARACCUMPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nGLAPI PFNGLCLEARACCUMPROC glad_glClearAccum;\n#define glClearAccum glad_glClearAccum\ntypedef void (APIENTRYP PFNGLCLEARINDEXPROC)(GLfloat c);\nGLAPI PFNGLCLEARINDEXPROC glad_glClearIndex;\n#define glClearIndex glad_glClearIndex\ntypedef void (APIENTRYP PFNGLINDEXMASKPROC)(GLuint mask);\nGLAPI PFNGLINDEXMASKPROC glad_glIndexMask;\n#define glIndexMask glad_glIndexMask\ntypedef void (APIENTRYP PFNGLACCUMPROC)(GLenum op, GLfloat value);\nGLAPI PFNGLACCUMPROC glad_glAccum;\n#define glAccum glad_glAccum\ntypedef void (APIENTRYP PFNGLPOPATTRIBPROC)(void);\nGLAPI PFNGLPOPATTRIBPROC glad_glPopAttrib;\n#define glPopAttrib glad_glPopAttrib\ntypedef void (APIENTRYP PFNGLPUSHATTRIBPROC)(GLbitfield mask);\nGLAPI PFNGLPUSHATTRIBPROC glad_glPushAttrib;\n#define glPushAttrib glad_glPushAttrib\ntypedef void (APIENTRYP PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points);\nGLAPI PFNGLMAP1DPROC glad_glMap1d;\n#define glMap1d glad_glMap1d\ntypedef void (APIENTRYP PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points);\nGLAPI PFNGLMAP1FPROC glad_glMap1f;\n#define glMap1f glad_glMap1f\ntypedef void (APIENTRYP PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points);\nGLAPI PFNGLMAP2DPROC glad_glMap2d;\n#define glMap2d glad_glMap2d\ntypedef void (APIENTRYP PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points);\nGLAPI PFNGLMAP2FPROC glad_glMap2f;\n#define glMap2f glad_glMap2f\ntypedef void (APIENTRYP PFNGLMAPGRID1DPROC)(GLint un, GLdouble u1, GLdouble u2);\nGLAPI PFNGLMAPGRID1DPROC glad_glMapGrid1d;\n#define glMapGrid1d glad_glMapGrid1d\ntypedef void (APIENTRYP PFNGLMAPGRID1FPROC)(GLint un, GLfloat u1, GLfloat u2);\nGLAPI PFNGLMAPGRID1FPROC glad_glMapGrid1f;\n#define glMapGrid1f glad_glMapGrid1f\ntypedef void (APIENTRYP PFNGLMAPGRID2DPROC)(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2);\nGLAPI PFNGLMAPGRID2DPROC glad_glMapGrid2d;\n#define glMapGrid2d glad_glMapGrid2d\ntypedef void (APIENTRYP PFNGLMAPGRID2FPROC)(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2);\nGLAPI PFNGLMAPGRID2FPROC glad_glMapGrid2f;\n#define glMapGrid2f glad_glMapGrid2f\ntypedef void (APIENTRYP PFNGLEVALCOORD1DPROC)(GLdouble u);\nGLAPI PFNGLEVALCOORD1DPROC glad_glEvalCoord1d;\n#define glEvalCoord1d glad_glEvalCoord1d\ntypedef void (APIENTRYP PFNGLEVALCOORD1DVPROC)(const GLdouble *u);\nGLAPI PFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv;\n#define glEvalCoord1dv glad_glEvalCoord1dv\ntypedef void (APIENTRYP PFNGLEVALCOORD1FPROC)(GLfloat u);\nGLAPI PFNGLEVALCOORD1FPROC glad_glEvalCoord1f;\n#define glEvalCoord1f glad_glEvalCoord1f\ntypedef void (APIENTRYP PFNGLEVALCOORD1FVPROC)(const GLfloat *u);\nGLAPI PFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv;\n#define glEvalCoord1fv glad_glEvalCoord1fv\ntypedef void (APIENTRYP PFNGLEVALCOORD2DPROC)(GLdouble u, GLdouble v);\nGLAPI PFNGLEVALCOORD2DPROC glad_glEvalCoord2d;\n#define glEvalCoord2d glad_glEvalCoord2d\ntypedef void (APIENTRYP PFNGLEVALCOORD2DVPROC)(const GLdouble *u);\nGLAPI PFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv;\n#define glEvalCoord2dv glad_glEvalCoord2dv\ntypedef void (APIENTRYP PFNGLEVALCOORD2FPROC)(GLfloat u, GLfloat v);\nGLAPI PFNGLEVALCOORD2FPROC glad_glEvalCoord2f;\n#define glEvalCoord2f glad_glEvalCoord2f\ntypedef void (APIENTRYP PFNGLEVALCOORD2FVPROC)(const GLfloat *u);\nGLAPI PFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv;\n#define glEvalCoord2fv glad_glEvalCoord2fv\ntypedef void (APIENTRYP PFNGLEVALMESH1PROC)(GLenum mode, GLint i1, GLint i2);\nGLAPI PFNGLEVALMESH1PROC glad_glEvalMesh1;\n#define glEvalMesh1 glad_glEvalMesh1\ntypedef void (APIENTRYP PFNGLEVALPOINT1PROC)(GLint i);\nGLAPI PFNGLEVALPOINT1PROC glad_glEvalPoint1;\n#define glEvalPoint1 glad_glEvalPoint1\ntypedef void (APIENTRYP PFNGLEVALMESH2PROC)(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2);\nGLAPI PFNGLEVALMESH2PROC glad_glEvalMesh2;\n#define glEvalMesh2 glad_glEvalMesh2\ntypedef void (APIENTRYP PFNGLEVALPOINT2PROC)(GLint i, GLint j);\nGLAPI PFNGLEVALPOINT2PROC glad_glEvalPoint2;\n#define glEvalPoint2 glad_glEvalPoint2\ntypedef void (APIENTRYP PFNGLALPHAFUNCPROC)(GLenum func, GLfloat ref);\nGLAPI PFNGLALPHAFUNCPROC glad_glAlphaFunc;\n#define glAlphaFunc glad_glAlphaFunc\ntypedef void (APIENTRYP PFNGLPIXELZOOMPROC)(GLfloat xfactor, GLfloat yfactor);\nGLAPI PFNGLPIXELZOOMPROC glad_glPixelZoom;\n#define glPixelZoom glad_glPixelZoom\ntypedef void (APIENTRYP PFNGLPIXELTRANSFERFPROC)(GLenum pname, GLfloat param);\nGLAPI PFNGLPIXELTRANSFERFPROC glad_glPixelTransferf;\n#define glPixelTransferf glad_glPixelTransferf\ntypedef void (APIENTRYP PFNGLPIXELTRANSFERIPROC)(GLenum pname, GLint param);\nGLAPI PFNGLPIXELTRANSFERIPROC glad_glPixelTransferi;\n#define glPixelTransferi glad_glPixelTransferi\ntypedef void (APIENTRYP PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat *values);\nGLAPI PFNGLPIXELMAPFVPROC glad_glPixelMapfv;\n#define glPixelMapfv glad_glPixelMapfv\ntypedef void (APIENTRYP PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint *values);\nGLAPI PFNGLPIXELMAPUIVPROC glad_glPixelMapuiv;\n#define glPixelMapuiv glad_glPixelMapuiv\ntypedef void (APIENTRYP PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort *values);\nGLAPI PFNGLPIXELMAPUSVPROC glad_glPixelMapusv;\n#define glPixelMapusv glad_glPixelMapusv\ntypedef void (APIENTRYP PFNGLCOPYPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type);\nGLAPI PFNGLCOPYPIXELSPROC glad_glCopyPixels;\n#define glCopyPixels glad_glCopyPixels\ntypedef void (APIENTRYP PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLDRAWPIXELSPROC glad_glDrawPixels;\n#define glDrawPixels glad_glDrawPixels\ntypedef void (APIENTRYP PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble *equation);\nGLAPI PFNGLGETCLIPPLANEPROC glad_glGetClipPlane;\n#define glGetClipPlane glad_glGetClipPlane\ntypedef void (APIENTRYP PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETLIGHTFVPROC glad_glGetLightfv;\n#define glGetLightfv glad_glGetLightfv\ntypedef void (APIENTRYP PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint *params);\nGLAPI PFNGLGETLIGHTIVPROC glad_glGetLightiv;\n#define glGetLightiv glad_glGetLightiv\ntypedef void (APIENTRYP PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble *v);\nGLAPI PFNGLGETMAPDVPROC glad_glGetMapdv;\n#define glGetMapdv glad_glGetMapdv\ntypedef void (APIENTRYP PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat *v);\nGLAPI PFNGLGETMAPFVPROC glad_glGetMapfv;\n#define glGetMapfv glad_glGetMapfv\ntypedef void (APIENTRYP PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint *v);\nGLAPI PFNGLGETMAPIVPROC glad_glGetMapiv;\n#define glGetMapiv glad_glGetMapiv\ntypedef void (APIENTRYP PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETMATERIALFVPROC glad_glGetMaterialfv;\n#define glGetMaterialfv glad_glGetMaterialfv\ntypedef void (APIENTRYP PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint *params);\nGLAPI PFNGLGETMATERIALIVPROC glad_glGetMaterialiv;\n#define glGetMaterialiv glad_glGetMaterialiv\ntypedef void (APIENTRYP PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat *values);\nGLAPI PFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv;\n#define glGetPixelMapfv glad_glGetPixelMapfv\ntypedef void (APIENTRYP PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint *values);\nGLAPI PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv;\n#define glGetPixelMapuiv glad_glGetPixelMapuiv\ntypedef void (APIENTRYP PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort *values);\nGLAPI PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv;\n#define glGetPixelMapusv glad_glGetPixelMapusv\ntypedef void (APIENTRYP PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte *mask);\nGLAPI PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple;\n#define glGetPolygonStipple glad_glGetPolygonStipple\ntypedef void (APIENTRYP PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETTEXENVFVPROC glad_glGetTexEnvfv;\n#define glGetTexEnvfv glad_glGetTexEnvfv\ntypedef void (APIENTRYP PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETTEXENVIVPROC glad_glGetTexEnviv;\n#define glGetTexEnviv glad_glGetTexEnviv\ntypedef void (APIENTRYP PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble *params);\nGLAPI PFNGLGETTEXGENDVPROC glad_glGetTexGendv;\n#define glGetTexGendv glad_glGetTexGendv\ntypedef void (APIENTRYP PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETTEXGENFVPROC glad_glGetTexGenfv;\n#define glGetTexGenfv glad_glGetTexGenfv\ntypedef void (APIENTRYP PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint *params);\nGLAPI PFNGLGETTEXGENIVPROC glad_glGetTexGeniv;\n#define glGetTexGeniv glad_glGetTexGeniv\ntypedef GLboolean (APIENTRYP PFNGLISLISTPROC)(GLuint list);\nGLAPI PFNGLISLISTPROC glad_glIsList;\n#define glIsList glad_glIsList\ntypedef void (APIENTRYP PFNGLFRUSTUMPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\nGLAPI PFNGLFRUSTUMPROC glad_glFrustum;\n#define glFrustum glad_glFrustum\ntypedef void (APIENTRYP PFNGLLOADIDENTITYPROC)(void);\nGLAPI PFNGLLOADIDENTITYPROC glad_glLoadIdentity;\n#define glLoadIdentity glad_glLoadIdentity\ntypedef void (APIENTRYP PFNGLLOADMATRIXFPROC)(const GLfloat *m);\nGLAPI PFNGLLOADMATRIXFPROC glad_glLoadMatrixf;\n#define glLoadMatrixf glad_glLoadMatrixf\ntypedef void (APIENTRYP PFNGLLOADMATRIXDPROC)(const GLdouble *m);\nGLAPI PFNGLLOADMATRIXDPROC glad_glLoadMatrixd;\n#define glLoadMatrixd glad_glLoadMatrixd\ntypedef void (APIENTRYP PFNGLMATRIXMODEPROC)(GLenum mode);\nGLAPI PFNGLMATRIXMODEPROC glad_glMatrixMode;\n#define glMatrixMode glad_glMatrixMode\ntypedef void (APIENTRYP PFNGLMULTMATRIXFPROC)(const GLfloat *m);\nGLAPI PFNGLMULTMATRIXFPROC glad_glMultMatrixf;\n#define glMultMatrixf glad_glMultMatrixf\ntypedef void (APIENTRYP PFNGLMULTMATRIXDPROC)(const GLdouble *m);\nGLAPI PFNGLMULTMATRIXDPROC glad_glMultMatrixd;\n#define glMultMatrixd glad_glMultMatrixd\ntypedef void (APIENTRYP PFNGLORTHOPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\nGLAPI PFNGLORTHOPROC glad_glOrtho;\n#define glOrtho glad_glOrtho\ntypedef void (APIENTRYP PFNGLPOPMATRIXPROC)(void);\nGLAPI PFNGLPOPMATRIXPROC glad_glPopMatrix;\n#define glPopMatrix glad_glPopMatrix\ntypedef void (APIENTRYP PFNGLPUSHMATRIXPROC)(void);\nGLAPI PFNGLPUSHMATRIXPROC glad_glPushMatrix;\n#define glPushMatrix glad_glPushMatrix\ntypedef void (APIENTRYP PFNGLROTATEDPROC)(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);\nGLAPI PFNGLROTATEDPROC glad_glRotated;\n#define glRotated glad_glRotated\ntypedef void (APIENTRYP PFNGLROTATEFPROC)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);\nGLAPI PFNGLROTATEFPROC glad_glRotatef;\n#define glRotatef glad_glRotatef\ntypedef void (APIENTRYP PFNGLSCALEDPROC)(GLdouble x, GLdouble y, GLdouble z);\nGLAPI PFNGLSCALEDPROC glad_glScaled;\n#define glScaled glad_glScaled\ntypedef void (APIENTRYP PFNGLSCALEFPROC)(GLfloat x, GLfloat y, GLfloat z);\nGLAPI PFNGLSCALEFPROC glad_glScalef;\n#define glScalef glad_glScalef\ntypedef void (APIENTRYP PFNGLTRANSLATEDPROC)(GLdouble x, GLdouble y, GLdouble z);\nGLAPI PFNGLTRANSLATEDPROC glad_glTranslated;\n#define glTranslated glad_glTranslated\ntypedef void (APIENTRYP PFNGLTRANSLATEFPROC)(GLfloat x, GLfloat y, GLfloat z);\nGLAPI PFNGLTRANSLATEFPROC glad_glTranslatef;\n#define glTranslatef glad_glTranslatef\n#endif\n#ifndef GL_VERSION_1_1\n#define GL_VERSION_1_1 1\nGLAPI int GLAD_GL_VERSION_1_1;\ntypedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count);\nGLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays;\n#define glDrawArrays glad_glDrawArrays\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices);\nGLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements;\n#define glDrawElements glad_glDrawElements\ntypedef void (APIENTRYP PFNGLGETPOINTERVPROC)(GLenum pname, void **params);\nGLAPI PFNGLGETPOINTERVPROC glad_glGetPointerv;\n#define glGetPointerv glad_glGetPointerv\ntypedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units);\nGLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;\n#define glPolygonOffset glad_glPolygonOffset\ntypedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);\nGLAPI PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D;\n#define glCopyTexImage1D glad_glCopyTexImage1D\ntypedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);\nGLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;\n#define glCopyTexImage2D glad_glCopyTexImage2D\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\nGLAPI PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D;\n#define glCopyTexSubImage1D glad_glCopyTexSubImage1D\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;\n#define glCopyTexSubImage2D glad_glCopyTexSubImage2D\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D;\n#define glTexSubImage1D glad_glTexSubImage1D\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;\n#define glTexSubImage2D glad_glTexSubImage2D\ntypedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture);\nGLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture;\n#define glBindTexture glad_glBindTexture\ntypedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures);\nGLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures;\n#define glDeleteTextures glad_glDeleteTextures\ntypedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures);\nGLAPI PFNGLGENTEXTURESPROC glad_glGenTextures;\n#define glGenTextures glad_glGenTextures\ntypedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture);\nGLAPI PFNGLISTEXTUREPROC glad_glIsTexture;\n#define glIsTexture glad_glIsTexture\ntypedef void (APIENTRYP PFNGLARRAYELEMENTPROC)(GLint i);\nGLAPI PFNGLARRAYELEMENTPROC glad_glArrayElement;\n#define glArrayElement glad_glArrayElement\ntypedef void (APIENTRYP PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer);\nGLAPI PFNGLCOLORPOINTERPROC glad_glColorPointer;\n#define glColorPointer glad_glColorPointer\ntypedef void (APIENTRYP PFNGLDISABLECLIENTSTATEPROC)(GLenum array);\nGLAPI PFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState;\n#define glDisableClientState glad_glDisableClientState\ntypedef void (APIENTRYP PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void *pointer);\nGLAPI PFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer;\n#define glEdgeFlagPointer glad_glEdgeFlagPointer\ntypedef void (APIENTRYP PFNGLENABLECLIENTSTATEPROC)(GLenum array);\nGLAPI PFNGLENABLECLIENTSTATEPROC glad_glEnableClientState;\n#define glEnableClientState glad_glEnableClientState\ntypedef void (APIENTRYP PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer);\nGLAPI PFNGLINDEXPOINTERPROC glad_glIndexPointer;\n#define glIndexPointer glad_glIndexPointer\ntypedef void (APIENTRYP PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void *pointer);\nGLAPI PFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays;\n#define glInterleavedArrays glad_glInterleavedArrays\ntypedef void (APIENTRYP PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer);\nGLAPI PFNGLNORMALPOINTERPROC glad_glNormalPointer;\n#define glNormalPointer glad_glNormalPointer\ntypedef void (APIENTRYP PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer);\nGLAPI PFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer;\n#define glTexCoordPointer glad_glTexCoordPointer\ntypedef void (APIENTRYP PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer);\nGLAPI PFNGLVERTEXPOINTERPROC glad_glVertexPointer;\n#define glVertexPointer glad_glVertexPointer\ntypedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint *textures, GLboolean *residences);\nGLAPI PFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident;\n#define glAreTexturesResident glad_glAreTexturesResident\ntypedef void (APIENTRYP PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint *textures, const GLfloat *priorities);\nGLAPI PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures;\n#define glPrioritizeTextures glad_glPrioritizeTextures\ntypedef void (APIENTRYP PFNGLINDEXUBPROC)(GLubyte c);\nGLAPI PFNGLINDEXUBPROC glad_glIndexub;\n#define glIndexub glad_glIndexub\ntypedef void (APIENTRYP PFNGLINDEXUBVPROC)(const GLubyte *c);\nGLAPI PFNGLINDEXUBVPROC glad_glIndexubv;\n#define glIndexubv glad_glIndexubv\ntypedef void (APIENTRYP PFNGLPOPCLIENTATTRIBPROC)(void);\nGLAPI PFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib;\n#define glPopClientAttrib glad_glPopClientAttrib\ntypedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBPROC)(GLbitfield mask);\nGLAPI PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib;\n#define glPushClientAttrib glad_glPushClientAttrib\n#endif\n#ifndef GL_VERSION_1_2\n#define GL_VERSION_1_2 1\nGLAPI int GLAD_GL_VERSION_1_2;\ntypedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices);\nGLAPI PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements;\n#define glDrawRangeElements glad_glDrawRangeElements\ntypedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXIMAGE3DPROC glad_glTexImage3D;\n#define glTexImage3D glad_glTexImage3D\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D;\n#define glTexSubImage3D glad_glTexSubImage3D\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D;\n#define glCopyTexSubImage3D glad_glCopyTexSubImage3D\n#endif\n#ifndef GL_VERSION_1_3\n#define GL_VERSION_1_3 1\nGLAPI int GLAD_GL_VERSION_1_3;\ntypedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture);\nGLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture;\n#define glActiveTexture glad_glActiveTexture\ntypedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert);\nGLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;\n#define glSampleCoverage glad_glSampleCoverage\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D;\n#define glCompressedTexImage3D glad_glCompressedTexImage3D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;\n#define glCompressedTexImage2D glad_glCompressedTexImage2D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D;\n#define glCompressedTexImage1D glad_glCompressedTexImage1D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D;\n#define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;\n#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D;\n#define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void *img);\nGLAPI PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage;\n#define glGetCompressedTexImage glad_glGetCompressedTexImage\ntypedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC)(GLenum texture);\nGLAPI PFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture;\n#define glClientActiveTexture glad_glClientActiveTexture\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC)(GLenum target, GLdouble s);\nGLAPI PFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d;\n#define glMultiTexCoord1d glad_glMultiTexCoord1d\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble *v);\nGLAPI PFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv;\n#define glMultiTexCoord1dv glad_glMultiTexCoord1dv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC)(GLenum target, GLfloat s);\nGLAPI PFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f;\n#define glMultiTexCoord1f glad_glMultiTexCoord1f\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat *v);\nGLAPI PFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv;\n#define glMultiTexCoord1fv glad_glMultiTexCoord1fv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC)(GLenum target, GLint s);\nGLAPI PFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i;\n#define glMultiTexCoord1i glad_glMultiTexCoord1i\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint *v);\nGLAPI PFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv;\n#define glMultiTexCoord1iv glad_glMultiTexCoord1iv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC)(GLenum target, GLshort s);\nGLAPI PFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s;\n#define glMultiTexCoord1s glad_glMultiTexCoord1s\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort *v);\nGLAPI PFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv;\n#define glMultiTexCoord1sv glad_glMultiTexCoord1sv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC)(GLenum target, GLdouble s, GLdouble t);\nGLAPI PFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d;\n#define glMultiTexCoord2d glad_glMultiTexCoord2d\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble *v);\nGLAPI PFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv;\n#define glMultiTexCoord2dv glad_glMultiTexCoord2dv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t);\nGLAPI PFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f;\n#define glMultiTexCoord2f glad_glMultiTexCoord2f\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat *v);\nGLAPI PFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv;\n#define glMultiTexCoord2fv glad_glMultiTexCoord2fv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC)(GLenum target, GLint s, GLint t);\nGLAPI PFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i;\n#define glMultiTexCoord2i glad_glMultiTexCoord2i\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint *v);\nGLAPI PFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv;\n#define glMultiTexCoord2iv glad_glMultiTexCoord2iv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC)(GLenum target, GLshort s, GLshort t);\nGLAPI PFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s;\n#define glMultiTexCoord2s glad_glMultiTexCoord2s\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort *v);\nGLAPI PFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv;\n#define glMultiTexCoord2sv glad_glMultiTexCoord2sv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r);\nGLAPI PFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d;\n#define glMultiTexCoord3d glad_glMultiTexCoord3d\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble *v);\nGLAPI PFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv;\n#define glMultiTexCoord3dv glad_glMultiTexCoord3dv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r);\nGLAPI PFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f;\n#define glMultiTexCoord3f glad_glMultiTexCoord3f\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat *v);\nGLAPI PFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv;\n#define glMultiTexCoord3fv glad_glMultiTexCoord3fv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC)(GLenum target, GLint s, GLint t, GLint r);\nGLAPI PFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i;\n#define glMultiTexCoord3i glad_glMultiTexCoord3i\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint *v);\nGLAPI PFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv;\n#define glMultiTexCoord3iv glad_glMultiTexCoord3iv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC)(GLenum target, GLshort s, GLshort t, GLshort r);\nGLAPI PFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s;\n#define glMultiTexCoord3s glad_glMultiTexCoord3s\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort *v);\nGLAPI PFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv;\n#define glMultiTexCoord3sv glad_glMultiTexCoord3sv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q);\nGLAPI PFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d;\n#define glMultiTexCoord4d glad_glMultiTexCoord4d\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble *v);\nGLAPI PFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv;\n#define glMultiTexCoord4dv glad_glMultiTexCoord4dv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q);\nGLAPI PFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f;\n#define glMultiTexCoord4f glad_glMultiTexCoord4f\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat *v);\nGLAPI PFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv;\n#define glMultiTexCoord4fv glad_glMultiTexCoord4fv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q);\nGLAPI PFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i;\n#define glMultiTexCoord4i glad_glMultiTexCoord4i\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint *v);\nGLAPI PFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv;\n#define glMultiTexCoord4iv glad_glMultiTexCoord4iv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q);\nGLAPI PFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s;\n#define glMultiTexCoord4s glad_glMultiTexCoord4s\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort *v);\nGLAPI PFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv;\n#define glMultiTexCoord4sv glad_glMultiTexCoord4sv\ntypedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat *m);\nGLAPI PFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf;\n#define glLoadTransposeMatrixf glad_glLoadTransposeMatrixf\ntypedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble *m);\nGLAPI PFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd;\n#define glLoadTransposeMatrixd glad_glLoadTransposeMatrixd\ntypedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat *m);\nGLAPI PFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf;\n#define glMultTransposeMatrixf glad_glMultTransposeMatrixf\ntypedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble *m);\nGLAPI PFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd;\n#define glMultTransposeMatrixd glad_glMultTransposeMatrixd\n#endif\n#ifndef GL_VERSION_1_4\n#define GL_VERSION_1_4 1\nGLAPI int GLAD_GL_VERSION_1_4;\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);\nGLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;\n#define glBlendFuncSeparate glad_glBlendFuncSeparate\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount);\nGLAPI PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays;\n#define glMultiDrawArrays glad_glMultiDrawArrays\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount);\nGLAPI PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements;\n#define glMultiDrawElements glad_glMultiDrawElements\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param);\nGLAPI PFNGLPOINTPARAMETERFPROC glad_glPointParameterf;\n#define glPointParameterf glad_glPointParameterf\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat *params);\nGLAPI PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv;\n#define glPointParameterfv glad_glPointParameterfv\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param);\nGLAPI PFNGLPOINTPARAMETERIPROC glad_glPointParameteri;\n#define glPointParameteri glad_glPointParameteri\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint *params);\nGLAPI PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv;\n#define glPointParameteriv glad_glPointParameteriv\ntypedef void (APIENTRYP PFNGLFOGCOORDFPROC)(GLfloat coord);\nGLAPI PFNGLFOGCOORDFPROC glad_glFogCoordf;\n#define glFogCoordf glad_glFogCoordf\ntypedef void (APIENTRYP PFNGLFOGCOORDFVPROC)(const GLfloat *coord);\nGLAPI PFNGLFOGCOORDFVPROC glad_glFogCoordfv;\n#define glFogCoordfv glad_glFogCoordfv\ntypedef void (APIENTRYP PFNGLFOGCOORDDPROC)(GLdouble coord);\nGLAPI PFNGLFOGCOORDDPROC glad_glFogCoordd;\n#define glFogCoordd glad_glFogCoordd\ntypedef void (APIENTRYP PFNGLFOGCOORDDVPROC)(const GLdouble *coord);\nGLAPI PFNGLFOGCOORDDVPROC glad_glFogCoorddv;\n#define glFogCoorddv glad_glFogCoorddv\ntypedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer);\nGLAPI PFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer;\n#define glFogCoordPointer glad_glFogCoordPointer\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue);\nGLAPI PFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b;\n#define glSecondaryColor3b glad_glSecondaryColor3b\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte *v);\nGLAPI PFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv;\n#define glSecondaryColor3bv glad_glSecondaryColor3bv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue);\nGLAPI PFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d;\n#define glSecondaryColor3d glad_glSecondaryColor3d\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble *v);\nGLAPI PFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv;\n#define glSecondaryColor3dv glad_glSecondaryColor3dv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue);\nGLAPI PFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f;\n#define glSecondaryColor3f glad_glSecondaryColor3f\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat *v);\nGLAPI PFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv;\n#define glSecondaryColor3fv glad_glSecondaryColor3fv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC)(GLint red, GLint green, GLint blue);\nGLAPI PFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i;\n#define glSecondaryColor3i glad_glSecondaryColor3i\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC)(const GLint *v);\nGLAPI PFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv;\n#define glSecondaryColor3iv glad_glSecondaryColor3iv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue);\nGLAPI PFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s;\n#define glSecondaryColor3s glad_glSecondaryColor3s\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC)(const GLshort *v);\nGLAPI PFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv;\n#define glSecondaryColor3sv glad_glSecondaryColor3sv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue);\nGLAPI PFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub;\n#define glSecondaryColor3ub glad_glSecondaryColor3ub\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte *v);\nGLAPI PFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv;\n#define glSecondaryColor3ubv glad_glSecondaryColor3ubv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue);\nGLAPI PFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui;\n#define glSecondaryColor3ui glad_glSecondaryColor3ui\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint *v);\nGLAPI PFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv;\n#define glSecondaryColor3uiv glad_glSecondaryColor3uiv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue);\nGLAPI PFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us;\n#define glSecondaryColor3us glad_glSecondaryColor3us\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC)(const GLushort *v);\nGLAPI PFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv;\n#define glSecondaryColor3usv glad_glSecondaryColor3usv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer);\nGLAPI PFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer;\n#define glSecondaryColorPointer glad_glSecondaryColorPointer\ntypedef void (APIENTRYP PFNGLWINDOWPOS2DPROC)(GLdouble x, GLdouble y);\nGLAPI PFNGLWINDOWPOS2DPROC glad_glWindowPos2d;\n#define glWindowPos2d glad_glWindowPos2d\ntypedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC)(const GLdouble *v);\nGLAPI PFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv;\n#define glWindowPos2dv glad_glWindowPos2dv\ntypedef void (APIENTRYP PFNGLWINDOWPOS2FPROC)(GLfloat x, GLfloat y);\nGLAPI PFNGLWINDOWPOS2FPROC glad_glWindowPos2f;\n#define glWindowPos2f glad_glWindowPos2f\ntypedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC)(const GLfloat *v);\nGLAPI PFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv;\n#define glWindowPos2fv glad_glWindowPos2fv\ntypedef void (APIENTRYP PFNGLWINDOWPOS2IPROC)(GLint x, GLint y);\nGLAPI PFNGLWINDOWPOS2IPROC glad_glWindowPos2i;\n#define glWindowPos2i glad_glWindowPos2i\ntypedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC)(const GLint *v);\nGLAPI PFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv;\n#define glWindowPos2iv glad_glWindowPos2iv\ntypedef void (APIENTRYP PFNGLWINDOWPOS2SPROC)(GLshort x, GLshort y);\nGLAPI PFNGLWINDOWPOS2SPROC glad_glWindowPos2s;\n#define glWindowPos2s glad_glWindowPos2s\ntypedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC)(const GLshort *v);\nGLAPI PFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv;\n#define glWindowPos2sv glad_glWindowPos2sv\ntypedef void (APIENTRYP PFNGLWINDOWPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z);\nGLAPI PFNGLWINDOWPOS3DPROC glad_glWindowPos3d;\n#define glWindowPos3d glad_glWindowPos3d\ntypedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC)(const GLdouble *v);\nGLAPI PFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv;\n#define glWindowPos3dv glad_glWindowPos3dv\ntypedef void (APIENTRYP PFNGLWINDOWPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z);\nGLAPI PFNGLWINDOWPOS3FPROC glad_glWindowPos3f;\n#define glWindowPos3f glad_glWindowPos3f\ntypedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC)(const GLfloat *v);\nGLAPI PFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv;\n#define glWindowPos3fv glad_glWindowPos3fv\ntypedef void (APIENTRYP PFNGLWINDOWPOS3IPROC)(GLint x, GLint y, GLint z);\nGLAPI PFNGLWINDOWPOS3IPROC glad_glWindowPos3i;\n#define glWindowPos3i glad_glWindowPos3i\ntypedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC)(const GLint *v);\nGLAPI PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv;\n#define glWindowPos3iv glad_glWindowPos3iv\ntypedef void (APIENTRYP PFNGLWINDOWPOS3SPROC)(GLshort x, GLshort y, GLshort z);\nGLAPI PFNGLWINDOWPOS3SPROC glad_glWindowPos3s;\n#define glWindowPos3s glad_glWindowPos3s\ntypedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC)(const GLshort *v);\nGLAPI PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv;\n#define glWindowPos3sv glad_glWindowPos3sv\ntypedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nGLAPI PFNGLBLENDCOLORPROC glad_glBlendColor;\n#define glBlendColor glad_glBlendColor\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum mode);\nGLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation;\n#define glBlendEquation glad_glBlendEquation\n#endif\n#ifndef GL_VERSION_1_5\n#define GL_VERSION_1_5 1\nGLAPI int GLAD_GL_VERSION_1_5;\ntypedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei n, GLuint *ids);\nGLAPI PFNGLGENQUERIESPROC glad_glGenQueries;\n#define glGenQueries glad_glGenQueries\ntypedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint *ids);\nGLAPI PFNGLDELETEQUERIESPROC glad_glDeleteQueries;\n#define glDeleteQueries glad_glDeleteQueries\ntypedef GLboolean (APIENTRYP PFNGLISQUERYPROC)(GLuint id);\nGLAPI PFNGLISQUERYPROC glad_glIsQuery;\n#define glIsQuery glad_glIsQuery\ntypedef void (APIENTRYP PFNGLBEGINQUERYPROC)(GLenum target, GLuint id);\nGLAPI PFNGLBEGINQUERYPROC glad_glBeginQuery;\n#define glBeginQuery glad_glBeginQuery\ntypedef void (APIENTRYP PFNGLENDQUERYPROC)(GLenum target);\nGLAPI PFNGLENDQUERYPROC glad_glEndQuery;\n#define glEndQuery glad_glEndQuery\ntypedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETQUERYIVPROC glad_glGetQueryiv;\n#define glGetQueryiv glad_glGetQueryiv\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint *params);\nGLAPI PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv;\n#define glGetQueryObjectiv glad_glGetQueryObjectiv\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint *params);\nGLAPI PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv;\n#define glGetQueryObjectuiv glad_glGetQueryObjectuiv\ntypedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);\nGLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer;\n#define glBindBuffer glad_glBindBuffer\ntypedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers);\nGLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;\n#define glDeleteBuffers glad_glDeleteBuffers\ntypedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers);\nGLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers;\n#define glGenBuffers glad_glGenBuffers\ntypedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer);\nGLAPI PFNGLISBUFFERPROC glad_glIsBuffer;\n#define glIsBuffer glad_glIsBuffer\ntypedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage);\nGLAPI PFNGLBUFFERDATAPROC glad_glBufferData;\n#define glBufferData glad_glBufferData\ntypedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data);\nGLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData;\n#define glBufferSubData glad_glBufferSubData\ntypedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void *data);\nGLAPI PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData;\n#define glGetBufferSubData glad_glGetBufferSubData\ntypedef void * (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum target, GLenum access);\nGLAPI PFNGLMAPBUFFERPROC glad_glMapBuffer;\n#define glMapBuffer glad_glMapBuffer\ntypedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum target);\nGLAPI PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer;\n#define glUnmapBuffer glad_glUnmapBuffer\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;\n#define glGetBufferParameteriv glad_glGetBufferParameteriv\ntypedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void **params);\nGLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv;\n#define glGetBufferPointerv glad_glGetBufferPointerv\n#endif\n#ifndef GL_VERSION_2_0\n#define GL_VERSION_2_0 1\nGLAPI int GLAD_GL_VERSION_2_0;\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha);\nGLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;\n#define glBlendEquationSeparate glad_glBlendEquationSeparate\ntypedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum *bufs);\nGLAPI PFNGLDRAWBUFFERSPROC glad_glDrawBuffers;\n#define glDrawBuffers glad_glDrawBuffers\ntypedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);\nGLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;\n#define glStencilOpSeparate glad_glStencilOpSeparate\ntypedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask);\nGLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;\n#define glStencilFuncSeparate glad_glStencilFuncSeparate\ntypedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask);\nGLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;\n#define glStencilMaskSeparate glad_glStencilMaskSeparate\ntypedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader);\nGLAPI PFNGLATTACHSHADERPROC glad_glAttachShader;\n#define glAttachShader glad_glAttachShader\ntypedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name);\nGLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;\n#define glBindAttribLocation glad_glBindAttribLocation\ntypedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader);\nGLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;\n#define glCompileShader glad_glCompileShader\ntypedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void);\nGLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;\n#define glCreateProgram glad_glCreateProgram\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum type);\nGLAPI PFNGLCREATESHADERPROC glad_glCreateShader;\n#define glCreateShader glad_glCreateShader\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program);\nGLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram;\n#define glDeleteProgram glad_glDeleteProgram\ntypedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader);\nGLAPI PFNGLDELETESHADERPROC glad_glDeleteShader;\n#define glDeleteShader glad_glDeleteShader\ntypedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader);\nGLAPI PFNGLDETACHSHADERPROC glad_glDetachShader;\n#define glDetachShader glad_glDetachShader\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);\nGLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;\n#define glDisableVertexAttribArray glad_glDisableVertexAttribArray\ntypedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);\nGLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;\n#define glEnableVertexAttribArray glad_glEnableVertexAttribArray\ntypedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);\nGLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;\n#define glGetActiveAttrib glad_glGetActiveAttrib\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);\nGLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;\n#define glGetActiveUniform glad_glGetActiveUniform\ntypedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders);\nGLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;\n#define glGetAttachedShaders glad_glGetAttachedShaders\ntypedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name);\nGLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;\n#define glGetAttribLocation glad_glGetAttribLocation\ntypedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params);\nGLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;\n#define glGetProgramiv glad_glGetProgramiv\ntypedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\nGLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;\n#define glGetProgramInfoLog glad_glGetProgramInfoLog\ntypedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params);\nGLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv;\n#define glGetShaderiv glad_glGetShaderiv\ntypedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\nGLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;\n#define glGetShaderInfoLog glad_glGetShaderInfoLog\ntypedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source);\nGLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;\n#define glGetShaderSource glad_glGetShaderSource\ntypedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name);\nGLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;\n#define glGetUniformLocation glad_glGetUniformLocation\ntypedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params);\nGLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv;\n#define glGetUniformfv glad_glGetUniformfv\ntypedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params);\nGLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv;\n#define glGetUniformiv glad_glGetUniformiv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble *params);\nGLAPI PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv;\n#define glGetVertexAttribdv glad_glGetVertexAttribdv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;\n#define glGetVertexAttribfv glad_glGetVertexAttribfv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params);\nGLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;\n#define glGetVertexAttribiv glad_glGetVertexAttribiv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer);\nGLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;\n#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program);\nGLAPI PFNGLISPROGRAMPROC glad_glIsProgram;\n#define glIsProgram glad_glIsProgram\ntypedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader);\nGLAPI PFNGLISSHADERPROC glad_glIsShader;\n#define glIsShader glad_glIsShader\ntypedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program);\nGLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram;\n#define glLinkProgram glad_glLinkProgram\ntypedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);\nGLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource;\n#define glShaderSource glad_glShaderSource\ntypedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program);\nGLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram;\n#define glUseProgram glad_glUseProgram\ntypedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0);\nGLAPI PFNGLUNIFORM1FPROC glad_glUniform1f;\n#define glUniform1f glad_glUniform1f\ntypedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1);\nGLAPI PFNGLUNIFORM2FPROC glad_glUniform2f;\n#define glUniform2f glad_glUniform2f\ntypedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\nGLAPI PFNGLUNIFORM3FPROC glad_glUniform3f;\n#define glUniform3f glad_glUniform3f\ntypedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\nGLAPI PFNGLUNIFORM4FPROC glad_glUniform4f;\n#define glUniform4f glad_glUniform4f\ntypedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0);\nGLAPI PFNGLUNIFORM1IPROC glad_glUniform1i;\n#define glUniform1i glad_glUniform1i\ntypedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1);\nGLAPI PFNGLUNIFORM2IPROC glad_glUniform2i;\n#define glUniform2i glad_glUniform2i\ntypedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2);\nGLAPI PFNGLUNIFORM3IPROC glad_glUniform3i;\n#define glUniform3i glad_glUniform3i\ntypedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\nGLAPI PFNGLUNIFORM4IPROC glad_glUniform4i;\n#define glUniform4i glad_glUniform4i\ntypedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv;\n#define glUniform1fv glad_glUniform1fv\ntypedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv;\n#define glUniform2fv glad_glUniform2fv\ntypedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv;\n#define glUniform3fv glad_glUniform3fv\ntypedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv;\n#define glUniform4fv glad_glUniform4fv\ntypedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv;\n#define glUniform1iv glad_glUniform1iv\ntypedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv;\n#define glUniform2iv glad_glUniform2iv\ntypedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv;\n#define glUniform3iv glad_glUniform3iv\ntypedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv;\n#define glUniform4iv glad_glUniform4iv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;\n#define glUniformMatrix2fv glad_glUniformMatrix2fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;\n#define glUniformMatrix3fv glad_glUniformMatrix3fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;\n#define glUniformMatrix4fv glad_glUniformMatrix4fv\ntypedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program);\nGLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;\n#define glValidateProgram glad_glValidateProgram\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x);\nGLAPI PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d;\n#define glVertexAttrib1d glad_glVertexAttrib1d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble *v);\nGLAPI PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv;\n#define glVertexAttrib1dv glad_glVertexAttrib1dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x);\nGLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;\n#define glVertexAttrib1f glad_glVertexAttrib1f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v);\nGLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;\n#define glVertexAttrib1fv glad_glVertexAttrib1fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x);\nGLAPI PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s;\n#define glVertexAttrib1s glad_glVertexAttrib1s\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort *v);\nGLAPI PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv;\n#define glVertexAttrib1sv glad_glVertexAttrib1sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y);\nGLAPI PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d;\n#define glVertexAttrib2d glad_glVertexAttrib2d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble *v);\nGLAPI PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv;\n#define glVertexAttrib2dv glad_glVertexAttrib2dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y);\nGLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;\n#define glVertexAttrib2f glad_glVertexAttrib2f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v);\nGLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;\n#define glVertexAttrib2fv glad_glVertexAttrib2fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y);\nGLAPI PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s;\n#define glVertexAttrib2s glad_glVertexAttrib2s\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort *v);\nGLAPI PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv;\n#define glVertexAttrib2sv glad_glVertexAttrib2sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z);\nGLAPI PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d;\n#define glVertexAttrib3d glad_glVertexAttrib3d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble *v);\nGLAPI PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv;\n#define glVertexAttrib3dv glad_glVertexAttrib3dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z);\nGLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;\n#define glVertexAttrib3f glad_glVertexAttrib3f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v);\nGLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;\n#define glVertexAttrib3fv glad_glVertexAttrib3fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z);\nGLAPI PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s;\n#define glVertexAttrib3s glad_glVertexAttrib3s\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort *v);\nGLAPI PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv;\n#define glVertexAttrib3sv glad_glVertexAttrib3sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte *v);\nGLAPI PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv;\n#define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint *v);\nGLAPI PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv;\n#define glVertexAttrib4Niv glad_glVertexAttrib4Niv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort *v);\nGLAPI PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv;\n#define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);\nGLAPI PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub;\n#define glVertexAttrib4Nub glad_glVertexAttrib4Nub\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte *v);\nGLAPI PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv;\n#define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint *v);\nGLAPI PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv;\n#define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort *v);\nGLAPI PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv;\n#define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte *v);\nGLAPI PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv;\n#define glVertexAttrib4bv glad_glVertexAttrib4bv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nGLAPI PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d;\n#define glVertexAttrib4d glad_glVertexAttrib4d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble *v);\nGLAPI PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv;\n#define glVertexAttrib4dv glad_glVertexAttrib4dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\nGLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;\n#define glVertexAttrib4f glad_glVertexAttrib4f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v);\nGLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;\n#define glVertexAttrib4fv glad_glVertexAttrib4fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint *v);\nGLAPI PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv;\n#define glVertexAttrib4iv glad_glVertexAttrib4iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w);\nGLAPI PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s;\n#define glVertexAttrib4s glad_glVertexAttrib4s\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort *v);\nGLAPI PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv;\n#define glVertexAttrib4sv glad_glVertexAttrib4sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte *v);\nGLAPI PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv;\n#define glVertexAttrib4ubv glad_glVertexAttrib4ubv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint *v);\nGLAPI PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv;\n#define glVertexAttrib4uiv glad_glVertexAttrib4uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort *v);\nGLAPI PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv;\n#define glVertexAttrib4usv glad_glVertexAttrib4usv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);\nGLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;\n#define glVertexAttribPointer glad_glVertexAttribPointer\n#endif\n#ifndef GL_VERSION_2_1\n#define GL_VERSION_2_1 1\nGLAPI int GLAD_GL_VERSION_2_1;\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv;\n#define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv;\n#define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv;\n#define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv;\n#define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv;\n#define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv;\n#define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv\n#endif\n#ifndef GL_VERSION_3_0\n#define GL_VERSION_3_0 1\nGLAPI int GLAD_GL_VERSION_3_0;\ntypedef void (APIENTRYP PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a);\nGLAPI PFNGLCOLORMASKIPROC glad_glColorMaski;\n#define glColorMaski glad_glColorMaski\ntypedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean *data);\nGLAPI PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v;\n#define glGetBooleani_v glad_glGetBooleani_v\ntypedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint *data);\nGLAPI PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v;\n#define glGetIntegeri_v glad_glGetIntegeri_v\ntypedef void (APIENTRYP PFNGLENABLEIPROC)(GLenum target, GLuint index);\nGLAPI PFNGLENABLEIPROC glad_glEnablei;\n#define glEnablei glad_glEnablei\ntypedef void (APIENTRYP PFNGLDISABLEIPROC)(GLenum target, GLuint index);\nGLAPI PFNGLDISABLEIPROC glad_glDisablei;\n#define glDisablei glad_glDisablei\ntypedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC)(GLenum target, GLuint index);\nGLAPI PFNGLISENABLEDIPROC glad_glIsEnabledi;\n#define glIsEnabledi glad_glIsEnabledi\ntypedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode);\nGLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback;\n#define glBeginTransformFeedback glad_glBeginTransformFeedback\ntypedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)(void);\nGLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;\n#define glEndTransformFeedback glad_glEndTransformFeedback\ntypedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);\nGLAPI PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange;\n#define glBindBufferRange glad_glBindBufferRange\ntypedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer);\nGLAPI PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase;\n#define glBindBufferBase glad_glBindBufferBase\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode);\nGLAPI PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings;\n#define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);\nGLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying;\n#define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying\ntypedef void (APIENTRYP PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp);\nGLAPI PFNGLCLAMPCOLORPROC glad_glClampColor;\n#define glClampColor glad_glClampColor\ntypedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode);\nGLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender;\n#define glBeginConditionalRender glad_glBeginConditionalRender\ntypedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)(void);\nGLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;\n#define glEndConditionalRender glad_glEndConditionalRender\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);\nGLAPI PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer;\n#define glVertexAttribIPointer glad_glVertexAttribIPointer\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint *params);\nGLAPI PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv;\n#define glGetVertexAttribIiv glad_glGetVertexAttribIiv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint *params);\nGLAPI PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv;\n#define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x);\nGLAPI PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i;\n#define glVertexAttribI1i glad_glVertexAttribI1i\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y);\nGLAPI PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i;\n#define glVertexAttribI2i glad_glVertexAttribI2i\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z);\nGLAPI PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i;\n#define glVertexAttribI3i glad_glVertexAttribI3i\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w);\nGLAPI PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i;\n#define glVertexAttribI4i glad_glVertexAttribI4i\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x);\nGLAPI PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui;\n#define glVertexAttribI1ui glad_glVertexAttribI1ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y);\nGLAPI PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui;\n#define glVertexAttribI2ui glad_glVertexAttribI2ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z);\nGLAPI PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui;\n#define glVertexAttribI3ui glad_glVertexAttribI3ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);\nGLAPI PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui;\n#define glVertexAttribI4ui glad_glVertexAttribI4ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint *v);\nGLAPI PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv;\n#define glVertexAttribI1iv glad_glVertexAttribI1iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint *v);\nGLAPI PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv;\n#define glVertexAttribI2iv glad_glVertexAttribI2iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint *v);\nGLAPI PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv;\n#define glVertexAttribI3iv glad_glVertexAttribI3iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint *v);\nGLAPI PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv;\n#define glVertexAttribI4iv glad_glVertexAttribI4iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint *v);\nGLAPI PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv;\n#define glVertexAttribI1uiv glad_glVertexAttribI1uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint *v);\nGLAPI PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv;\n#define glVertexAttribI2uiv glad_glVertexAttribI2uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint *v);\nGLAPI PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv;\n#define glVertexAttribI3uiv glad_glVertexAttribI3uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint *v);\nGLAPI PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv;\n#define glVertexAttribI4uiv glad_glVertexAttribI4uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte *v);\nGLAPI PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv;\n#define glVertexAttribI4bv glad_glVertexAttribI4bv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort *v);\nGLAPI PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv;\n#define glVertexAttribI4sv glad_glVertexAttribI4sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte *v);\nGLAPI PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv;\n#define glVertexAttribI4ubv glad_glVertexAttribI4ubv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort *v);\nGLAPI PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv;\n#define glVertexAttribI4usv glad_glVertexAttribI4usv\ntypedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint *params);\nGLAPI PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv;\n#define glGetUniformuiv glad_glGetUniformuiv\ntypedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar *name);\nGLAPI PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation;\n#define glBindFragDataLocation glad_glBindFragDataLocation\ntypedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar *name);\nGLAPI PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation;\n#define glGetFragDataLocation glad_glGetFragDataLocation\ntypedef void (APIENTRYP PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0);\nGLAPI PFNGLUNIFORM1UIPROC glad_glUniform1ui;\n#define glUniform1ui glad_glUniform1ui\ntypedef void (APIENTRYP PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1);\nGLAPI PFNGLUNIFORM2UIPROC glad_glUniform2ui;\n#define glUniform2ui glad_glUniform2ui\ntypedef void (APIENTRYP PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2);\nGLAPI PFNGLUNIFORM3UIPROC glad_glUniform3ui;\n#define glUniform3ui glad_glUniform3ui\ntypedef void (APIENTRYP PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);\nGLAPI PFNGLUNIFORM4UIPROC glad_glUniform4ui;\n#define glUniform4ui glad_glUniform4ui\ntypedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint *value);\nGLAPI PFNGLUNIFORM1UIVPROC glad_glUniform1uiv;\n#define glUniform1uiv glad_glUniform1uiv\ntypedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint *value);\nGLAPI PFNGLUNIFORM2UIVPROC glad_glUniform2uiv;\n#define glUniform2uiv glad_glUniform2uiv\ntypedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint *value);\nGLAPI PFNGLUNIFORM3UIVPROC glad_glUniform3uiv;\n#define glUniform3uiv glad_glUniform3uiv\ntypedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint *value);\nGLAPI PFNGLUNIFORM4UIVPROC glad_glUniform4uiv;\n#define glUniform4uiv glad_glUniform4uiv\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint *params);\nGLAPI PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv;\n#define glTexParameterIiv glad_glTexParameterIiv\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint *params);\nGLAPI PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv;\n#define glTexParameterIuiv glad_glTexParameterIuiv\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv;\n#define glGetTexParameterIiv glad_glGetTexParameterIiv\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint *params);\nGLAPI PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv;\n#define glGetTexParameterIuiv glad_glGetTexParameterIuiv\ntypedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint *value);\nGLAPI PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv;\n#define glClearBufferiv glad_glClearBufferiv\ntypedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint *value);\nGLAPI PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv;\n#define glClearBufferuiv glad_glClearBufferuiv\ntypedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat *value);\nGLAPI PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv;\n#define glClearBufferfv glad_glClearBufferfv\ntypedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);\nGLAPI PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi;\n#define glClearBufferfi glad_glClearBufferfi\ntypedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum name, GLuint index);\nGLAPI PFNGLGETSTRINGIPROC glad_glGetStringi;\n#define glGetStringi glad_glGetStringi\ntypedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer);\nGLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;\n#define glIsRenderbuffer glad_glIsRenderbuffer\ntypedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer);\nGLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;\n#define glBindRenderbuffer glad_glBindRenderbuffer\ntypedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers);\nGLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;\n#define glDeleteRenderbuffers glad_glDeleteRenderbuffers\ntypedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers);\nGLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;\n#define glGenRenderbuffers glad_glGenRenderbuffers\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;\n#define glRenderbufferStorage glad_glRenderbufferStorage\ntypedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;\n#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv\ntypedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer);\nGLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;\n#define glIsFramebuffer glad_glIsFramebuffer\ntypedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer);\nGLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;\n#define glBindFramebuffer glad_glBindFramebuffer\ntypedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers);\nGLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;\n#define glDeleteFramebuffers glad_glDeleteFramebuffers\ntypedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers);\nGLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;\n#define glGenFramebuffers glad_glGenFramebuffers\ntypedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target);\nGLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;\n#define glCheckFramebufferStatus glad_glCheckFramebufferStatus\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\nGLAPI PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D;\n#define glFramebufferTexture1D glad_glFramebufferTexture1D\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\nGLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;\n#define glFramebufferTexture2D glad_glFramebufferTexture2D\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);\nGLAPI PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D;\n#define glFramebufferTexture3D glad_glFramebufferTexture3D\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);\nGLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;\n#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params);\nGLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;\n#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv\ntypedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target);\nGLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;\n#define glGenerateMipmap glad_glGenerateMipmap\ntypedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);\nGLAPI PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer;\n#define glBlitFramebuffer glad_glBlitFramebuffer\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample;\n#define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);\nGLAPI PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer;\n#define glFramebufferTextureLayer glad_glFramebufferTextureLayer\ntypedef void * (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);\nGLAPI PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange;\n#define glMapBufferRange glad_glMapBufferRange\ntypedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length);\nGLAPI PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange;\n#define glFlushMappedBufferRange glad_glFlushMappedBufferRange\ntypedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array);\nGLAPI PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray;\n#define glBindVertexArray glad_glBindVertexArray\ntypedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint *arrays);\nGLAPI PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays;\n#define glDeleteVertexArrays glad_glDeleteVertexArrays\ntypedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays);\nGLAPI PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays;\n#define glGenVertexArrays glad_glGenVertexArrays\ntypedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC)(GLuint array);\nGLAPI PFNGLISVERTEXARRAYPROC glad_glIsVertexArray;\n#define glIsVertexArray glad_glIsVertexArray\n#endif\n#ifndef GL_VERSION_3_1\n#define GL_VERSION_3_1 1\nGLAPI int GLAD_GL_VERSION_3_1;\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);\nGLAPI PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced;\n#define glDrawArraysInstanced glad_glDrawArraysInstanced\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced;\n#define glDrawElementsInstanced glad_glDrawElementsInstanced\ntypedef void (APIENTRYP PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer);\nGLAPI PFNGLTEXBUFFERPROC glad_glTexBuffer;\n#define glTexBuffer glad_glTexBuffer\ntypedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index);\nGLAPI PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex;\n#define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex\ntypedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);\nGLAPI PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData;\n#define glCopyBufferSubData glad_glCopyBufferSubData\ntypedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices);\nGLAPI PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices;\n#define glGetUniformIndices glad_glGetUniformIndices\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params);\nGLAPI PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv;\n#define glGetActiveUniformsiv glad_glGetActiveUniformsiv\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName);\nGLAPI PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName;\n#define glGetActiveUniformName glad_glGetActiveUniformName\ntypedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar *uniformBlockName);\nGLAPI PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex;\n#define glGetUniformBlockIndex glad_glGetUniformBlockIndex\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params);\nGLAPI PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv;\n#define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName);\nGLAPI PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName;\n#define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName\ntypedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);\nGLAPI PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding;\n#define glUniformBlockBinding glad_glUniformBlockBinding\n#endif\n#ifndef GL_VERSION_3_2\n#define GL_VERSION_3_2 1\nGLAPI int GLAD_GL_VERSION_3_2;\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);\nGLAPI PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex;\n#define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex\ntypedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex);\nGLAPI PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex;\n#define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex;\n#define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex);\nGLAPI PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex;\n#define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex\ntypedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC)(GLenum mode);\nGLAPI PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex;\n#define glProvokingVertex glad_glProvokingVertex\ntypedef GLsync (APIENTRYP PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags);\nGLAPI PFNGLFENCESYNCPROC glad_glFenceSync;\n#define glFenceSync glad_glFenceSync\ntypedef GLboolean (APIENTRYP PFNGLISSYNCPROC)(GLsync sync);\nGLAPI PFNGLISSYNCPROC glad_glIsSync;\n#define glIsSync glad_glIsSync\ntypedef void (APIENTRYP PFNGLDELETESYNCPROC)(GLsync sync);\nGLAPI PFNGLDELETESYNCPROC glad_glDeleteSync;\n#define glDeleteSync glad_glDeleteSync\ntypedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout);\nGLAPI PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync;\n#define glClientWaitSync glad_glClientWaitSync\ntypedef void (APIENTRYP PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout);\nGLAPI PFNGLWAITSYNCPROC glad_glWaitSync;\n#define glWaitSync glad_glWaitSync\ntypedef void (APIENTRYP PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 *data);\nGLAPI PFNGLGETINTEGER64VPROC glad_glGetInteger64v;\n#define glGetInteger64v glad_glGetInteger64v\ntypedef void (APIENTRYP PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values);\nGLAPI PFNGLGETSYNCIVPROC glad_glGetSynciv;\n#define glGetSynciv glad_glGetSynciv\ntypedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 *data);\nGLAPI PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v;\n#define glGetInteger64i_v glad_glGetInteger64i_v\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 *params);\nGLAPI PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v;\n#define glGetBufferParameteri64v glad_glGetBufferParameteri64v\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level);\nGLAPI PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture;\n#define glFramebufferTexture glad_glFramebufferTexture\ntypedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\nGLAPI PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample;\n#define glTexImage2DMultisample glad_glTexImage2DMultisample\ntypedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\nGLAPI PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample;\n#define glTexImage3DMultisample glad_glTexImage3DMultisample\ntypedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat *val);\nGLAPI PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv;\n#define glGetMultisamplefv glad_glGetMultisamplefv\ntypedef void (APIENTRYP PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask);\nGLAPI PFNGLSAMPLEMASKIPROC glad_glSampleMaski;\n#define glSampleMaski glad_glSampleMaski\n#endif\n#ifndef GL_VERSION_3_3\n#define GL_VERSION_3_3 1\nGLAPI int GLAD_GL_VERSION_3_3;\ntypedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar *name);\nGLAPI PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed;\n#define glBindFragDataLocationIndexed glad_glBindFragDataLocationIndexed\ntypedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar *name);\nGLAPI PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex;\n#define glGetFragDataIndex glad_glGetFragDataIndex\ntypedef void (APIENTRYP PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint *samplers);\nGLAPI PFNGLGENSAMPLERSPROC glad_glGenSamplers;\n#define glGenSamplers glad_glGenSamplers\ntypedef void (APIENTRYP PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint *samplers);\nGLAPI PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers;\n#define glDeleteSamplers glad_glDeleteSamplers\ntypedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC)(GLuint sampler);\nGLAPI PFNGLISSAMPLERPROC glad_glIsSampler;\n#define glIsSampler glad_glIsSampler\ntypedef void (APIENTRYP PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler);\nGLAPI PFNGLBINDSAMPLERPROC glad_glBindSampler;\n#define glBindSampler glad_glBindSampler\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param);\nGLAPI PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri;\n#define glSamplerParameteri glad_glSamplerParameteri\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint *param);\nGLAPI PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv;\n#define glSamplerParameteriv glad_glSamplerParameteriv\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param);\nGLAPI PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf;\n#define glSamplerParameterf glad_glSamplerParameterf\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat *param);\nGLAPI PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv;\n#define glSamplerParameterfv glad_glSamplerParameterfv\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint *param);\nGLAPI PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv;\n#define glSamplerParameterIiv glad_glSamplerParameterIiv\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint *param);\nGLAPI PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv;\n#define glSamplerParameterIuiv glad_glSamplerParameterIuiv\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint *params);\nGLAPI PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv;\n#define glGetSamplerParameteriv glad_glGetSamplerParameteriv\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint *params);\nGLAPI PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv;\n#define glGetSamplerParameterIiv glad_glGetSamplerParameterIiv\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv;\n#define glGetSamplerParameterfv glad_glGetSamplerParameterfv\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint *params);\nGLAPI PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv;\n#define glGetSamplerParameterIuiv glad_glGetSamplerParameterIuiv\ntypedef void (APIENTRYP PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target);\nGLAPI PFNGLQUERYCOUNTERPROC glad_glQueryCounter;\n#define glQueryCounter glad_glQueryCounter\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 *params);\nGLAPI PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v;\n#define glGetQueryObjecti64v glad_glGetQueryObjecti64v\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 *params);\nGLAPI PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v;\n#define glGetQueryObjectui64v glad_glGetQueryObjectui64v\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor);\nGLAPI PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor;\n#define glVertexAttribDivisor glad_glVertexAttribDivisor\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);\nGLAPI PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui;\n#define glVertexAttribP1ui glad_glVertexAttribP1ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\nGLAPI PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv;\n#define glVertexAttribP1uiv glad_glVertexAttribP1uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);\nGLAPI PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui;\n#define glVertexAttribP2ui glad_glVertexAttribP2ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\nGLAPI PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv;\n#define glVertexAttribP2uiv glad_glVertexAttribP2uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);\nGLAPI PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui;\n#define glVertexAttribP3ui glad_glVertexAttribP3ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\nGLAPI PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv;\n#define glVertexAttribP3uiv glad_glVertexAttribP3uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);\nGLAPI PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui;\n#define glVertexAttribP4ui glad_glVertexAttribP4ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\nGLAPI PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv;\n#define glVertexAttribP4uiv glad_glVertexAttribP4uiv\ntypedef void (APIENTRYP PFNGLVERTEXP2UIPROC)(GLenum type, GLuint value);\nGLAPI PFNGLVERTEXP2UIPROC glad_glVertexP2ui;\n#define glVertexP2ui glad_glVertexP2ui\ntypedef void (APIENTRYP PFNGLVERTEXP2UIVPROC)(GLenum type, const GLuint *value);\nGLAPI PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv;\n#define glVertexP2uiv glad_glVertexP2uiv\ntypedef void (APIENTRYP PFNGLVERTEXP3UIPROC)(GLenum type, GLuint value);\nGLAPI PFNGLVERTEXP3UIPROC glad_glVertexP3ui;\n#define glVertexP3ui glad_glVertexP3ui\ntypedef void (APIENTRYP PFNGLVERTEXP3UIVPROC)(GLenum type, const GLuint *value);\nGLAPI PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv;\n#define glVertexP3uiv glad_glVertexP3uiv\ntypedef void (APIENTRYP PFNGLVERTEXP4UIPROC)(GLenum type, GLuint value);\nGLAPI PFNGLVERTEXP4UIPROC glad_glVertexP4ui;\n#define glVertexP4ui glad_glVertexP4ui\ntypedef void (APIENTRYP PFNGLVERTEXP4UIVPROC)(GLenum type, const GLuint *value);\nGLAPI PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv;\n#define glVertexP4uiv glad_glVertexP4uiv\ntypedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC)(GLenum type, GLuint coords);\nGLAPI PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui;\n#define glTexCoordP1ui glad_glTexCoordP1ui\ntypedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC)(GLenum type, const GLuint *coords);\nGLAPI PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv;\n#define glTexCoordP1uiv glad_glTexCoordP1uiv\ntypedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC)(GLenum type, GLuint coords);\nGLAPI PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui;\n#define glTexCoordP2ui glad_glTexCoordP2ui\ntypedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC)(GLenum type, const GLuint *coords);\nGLAPI PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv;\n#define glTexCoordP2uiv glad_glTexCoordP2uiv\ntypedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC)(GLenum type, GLuint coords);\nGLAPI PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui;\n#define glTexCoordP3ui glad_glTexCoordP3ui\ntypedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC)(GLenum type, const GLuint *coords);\nGLAPI PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv;\n#define glTexCoordP3uiv glad_glTexCoordP3uiv\ntypedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC)(GLenum type, GLuint coords);\nGLAPI PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui;\n#define glTexCoordP4ui glad_glTexCoordP4ui\ntypedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC)(GLenum type, const GLuint *coords);\nGLAPI PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv;\n#define glTexCoordP4uiv glad_glTexCoordP4uiv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC)(GLenum texture, GLenum type, GLuint coords);\nGLAPI PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui;\n#define glMultiTexCoordP1ui glad_glMultiTexCoordP1ui\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);\nGLAPI PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv;\n#define glMultiTexCoordP1uiv glad_glMultiTexCoordP1uiv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC)(GLenum texture, GLenum type, GLuint coords);\nGLAPI PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui;\n#define glMultiTexCoordP2ui glad_glMultiTexCoordP2ui\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);\nGLAPI PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv;\n#define glMultiTexCoordP2uiv glad_glMultiTexCoordP2uiv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum type, GLuint coords);\nGLAPI PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui;\n#define glMultiTexCoordP3ui glad_glMultiTexCoordP3ui\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);\nGLAPI PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv;\n#define glMultiTexCoordP3uiv glad_glMultiTexCoordP3uiv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords);\nGLAPI PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui;\n#define glMultiTexCoordP4ui glad_glMultiTexCoordP4ui\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);\nGLAPI PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv;\n#define glMultiTexCoordP4uiv glad_glMultiTexCoordP4uiv\ntypedef void (APIENTRYP PFNGLNORMALP3UIPROC)(GLenum type, GLuint coords);\nGLAPI PFNGLNORMALP3UIPROC glad_glNormalP3ui;\n#define glNormalP3ui glad_glNormalP3ui\ntypedef void (APIENTRYP PFNGLNORMALP3UIVPROC)(GLenum type, const GLuint *coords);\nGLAPI PFNGLNORMALP3UIVPROC glad_glNormalP3uiv;\n#define glNormalP3uiv glad_glNormalP3uiv\ntypedef void (APIENTRYP PFNGLCOLORP3UIPROC)(GLenum type, GLuint color);\nGLAPI PFNGLCOLORP3UIPROC glad_glColorP3ui;\n#define glColorP3ui glad_glColorP3ui\ntypedef void (APIENTRYP PFNGLCOLORP3UIVPROC)(GLenum type, const GLuint *color);\nGLAPI PFNGLCOLORP3UIVPROC glad_glColorP3uiv;\n#define glColorP3uiv glad_glColorP3uiv\ntypedef void (APIENTRYP PFNGLCOLORP4UIPROC)(GLenum type, GLuint color);\nGLAPI PFNGLCOLORP4UIPROC glad_glColorP4ui;\n#define glColorP4ui glad_glColorP4ui\ntypedef void (APIENTRYP PFNGLCOLORP4UIVPROC)(GLenum type, const GLuint *color);\nGLAPI PFNGLCOLORP4UIVPROC glad_glColorP4uiv;\n#define glColorP4uiv glad_glColorP4uiv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC)(GLenum type, GLuint color);\nGLAPI PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui;\n#define glSecondaryColorP3ui glad_glSecondaryColorP3ui\ntypedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC)(GLenum type, const GLuint *color);\nGLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv;\n#define glSecondaryColorP3uiv glad_glSecondaryColorP3uiv\n#endif\n#ifndef GL_VERSION_4_0\n#define GL_VERSION_4_0 1\nGLAPI int GLAD_GL_VERSION_4_0;\ntypedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC)(GLfloat value);\nGLAPI PFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading;\n#define glMinSampleShading glad_glMinSampleShading\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC)(GLuint buf, GLenum mode);\nGLAPI PFNGLBLENDEQUATIONIPROC glad_glBlendEquationi;\n#define glBlendEquationi glad_glBlendEquationi\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha);\nGLAPI PFNGLBLENDEQUATIONSEPARATEIPROC glad_glBlendEquationSeparatei;\n#define glBlendEquationSeparatei glad_glBlendEquationSeparatei\ntypedef void (APIENTRYP PFNGLBLENDFUNCIPROC)(GLuint buf, GLenum src, GLenum dst);\nGLAPI PFNGLBLENDFUNCIPROC glad_glBlendFunci;\n#define glBlendFunci glad_glBlendFunci\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);\nGLAPI PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei;\n#define glBlendFuncSeparatei glad_glBlendFuncSeparatei\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC)(GLenum mode, const void *indirect);\nGLAPI PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect;\n#define glDrawArraysIndirect glad_glDrawArraysIndirect\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void *indirect);\nGLAPI PFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect;\n#define glDrawElementsIndirect glad_glDrawElementsIndirect\ntypedef void (APIENTRYP PFNGLUNIFORM1DPROC)(GLint location, GLdouble x);\nGLAPI PFNGLUNIFORM1DPROC glad_glUniform1d;\n#define glUniform1d glad_glUniform1d\ntypedef void (APIENTRYP PFNGLUNIFORM2DPROC)(GLint location, GLdouble x, GLdouble y);\nGLAPI PFNGLUNIFORM2DPROC glad_glUniform2d;\n#define glUniform2d glad_glUniform2d\ntypedef void (APIENTRYP PFNGLUNIFORM3DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z);\nGLAPI PFNGLUNIFORM3DPROC glad_glUniform3d;\n#define glUniform3d glad_glUniform3d\ntypedef void (APIENTRYP PFNGLUNIFORM4DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nGLAPI PFNGLUNIFORM4DPROC glad_glUniform4d;\n#define glUniform4d glad_glUniform4d\ntypedef void (APIENTRYP PFNGLUNIFORM1DVPROC)(GLint location, GLsizei count, const GLdouble *value);\nGLAPI PFNGLUNIFORM1DVPROC glad_glUniform1dv;\n#define glUniform1dv glad_glUniform1dv\ntypedef void (APIENTRYP PFNGLUNIFORM2DVPROC)(GLint location, GLsizei count, const GLdouble *value);\nGLAPI PFNGLUNIFORM2DVPROC glad_glUniform2dv;\n#define glUniform2dv glad_glUniform2dv\ntypedef void (APIENTRYP PFNGLUNIFORM3DVPROC)(GLint location, GLsizei count, const GLdouble *value);\nGLAPI PFNGLUNIFORM3DVPROC glad_glUniform3dv;\n#define glUniform3dv glad_glUniform3dv\ntypedef void (APIENTRYP PFNGLUNIFORM4DVPROC)(GLint location, GLsizei count, const GLdouble *value);\nGLAPI PFNGLUNIFORM4DVPROC glad_glUniform4dv;\n#define glUniform4dv glad_glUniform4dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLUNIFORMMATRIX2DVPROC glad_glUniformMatrix2dv;\n#define glUniformMatrix2dv glad_glUniformMatrix2dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLUNIFORMMATRIX3DVPROC glad_glUniformMatrix3dv;\n#define glUniformMatrix3dv glad_glUniformMatrix3dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLUNIFORMMATRIX4DVPROC glad_glUniformMatrix4dv;\n#define glUniformMatrix4dv glad_glUniformMatrix4dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLUNIFORMMATRIX2X3DVPROC glad_glUniformMatrix2x3dv;\n#define glUniformMatrix2x3dv glad_glUniformMatrix2x3dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLUNIFORMMATRIX2X4DVPROC glad_glUniformMatrix2x4dv;\n#define glUniformMatrix2x4dv glad_glUniformMatrix2x4dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLUNIFORMMATRIX3X2DVPROC glad_glUniformMatrix3x2dv;\n#define glUniformMatrix3x2dv glad_glUniformMatrix3x2dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLUNIFORMMATRIX3X4DVPROC glad_glUniformMatrix3x4dv;\n#define glUniformMatrix3x4dv glad_glUniformMatrix3x4dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLUNIFORMMATRIX4X2DVPROC glad_glUniformMatrix4x2dv;\n#define glUniformMatrix4x2dv glad_glUniformMatrix4x2dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv;\n#define glUniformMatrix4x3dv glad_glUniformMatrix4x3dv\ntypedef void (APIENTRYP PFNGLGETUNIFORMDVPROC)(GLuint program, GLint location, GLdouble *params);\nGLAPI PFNGLGETUNIFORMDVPROC glad_glGetUniformdv;\n#define glGetUniformdv glad_glGetUniformdv\ntypedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)(GLuint program, GLenum shadertype, const GLchar *name);\nGLAPI PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glad_glGetSubroutineUniformLocation;\n#define glGetSubroutineUniformLocation glad_glGetSubroutineUniformLocation\ntypedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC)(GLuint program, GLenum shadertype, const GLchar *name);\nGLAPI PFNGLGETSUBROUTINEINDEXPROC glad_glGetSubroutineIndex;\n#define glGetSubroutineIndex glad_glGetSubroutineIndex\ntypedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)(GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values);\nGLAPI PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glad_glGetActiveSubroutineUniformiv;\n#define glGetActiveSubroutineUniformiv glad_glGetActiveSubroutineUniformiv\ntypedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);\nGLAPI PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glad_glGetActiveSubroutineUniformName;\n#define glGetActiveSubroutineUniformName glad_glGetActiveSubroutineUniformName\ntypedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);\nGLAPI PFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName;\n#define glGetActiveSubroutineName glad_glGetActiveSubroutineName\ntypedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC)(GLenum shadertype, GLsizei count, const GLuint *indices);\nGLAPI PFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv;\n#define glUniformSubroutinesuiv glad_glUniformSubroutinesuiv\ntypedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC)(GLenum shadertype, GLint location, GLuint *params);\nGLAPI PFNGLGETUNIFORMSUBROUTINEUIVPROC glad_glGetUniformSubroutineuiv;\n#define glGetUniformSubroutineuiv glad_glGetUniformSubroutineuiv\ntypedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC)(GLuint program, GLenum shadertype, GLenum pname, GLint *values);\nGLAPI PFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv;\n#define glGetProgramStageiv glad_glGetProgramStageiv\ntypedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC)(GLenum pname, GLint value);\nGLAPI PFNGLPATCHPARAMETERIPROC glad_glPatchParameteri;\n#define glPatchParameteri glad_glPatchParameteri\ntypedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC)(GLenum pname, const GLfloat *values);\nGLAPI PFNGLPATCHPARAMETERFVPROC glad_glPatchParameterfv;\n#define glPatchParameterfv glad_glPatchParameterfv\ntypedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC)(GLenum target, GLuint id);\nGLAPI PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback;\n#define glBindTransformFeedback glad_glBindTransformFeedback\ntypedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC)(GLsizei n, const GLuint *ids);\nGLAPI PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks;\n#define glDeleteTransformFeedbacks glad_glDeleteTransformFeedbacks\ntypedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint *ids);\nGLAPI PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks;\n#define glGenTransformFeedbacks glad_glGenTransformFeedbacks\ntypedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC)(GLuint id);\nGLAPI PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback;\n#define glIsTransformFeedback glad_glIsTransformFeedback\ntypedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC)(void);\nGLAPI PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback;\n#define glPauseTransformFeedback glad_glPauseTransformFeedback\ntypedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC)(void);\nGLAPI PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback;\n#define glResumeTransformFeedback glad_glResumeTransformFeedback\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC)(GLenum mode, GLuint id);\nGLAPI PFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback;\n#define glDrawTransformFeedback glad_glDrawTransformFeedback\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)(GLenum mode, GLuint id, GLuint stream);\nGLAPI PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream;\n#define glDrawTransformFeedbackStream glad_glDrawTransformFeedbackStream\ntypedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC)(GLenum target, GLuint index, GLuint id);\nGLAPI PFNGLBEGINQUERYINDEXEDPROC glad_glBeginQueryIndexed;\n#define glBeginQueryIndexed glad_glBeginQueryIndexed\ntypedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC)(GLenum target, GLuint index);\nGLAPI PFNGLENDQUERYINDEXEDPROC glad_glEndQueryIndexed;\n#define glEndQueryIndexed glad_glEndQueryIndexed\ntypedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC)(GLenum target, GLuint index, GLenum pname, GLint *params);\nGLAPI PFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv;\n#define glGetQueryIndexediv glad_glGetQueryIndexediv\n#endif\n#ifndef GL_VERSION_4_1\n#define GL_VERSION_4_1 1\nGLAPI int GLAD_GL_VERSION_4_1;\ntypedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC)(void);\nGLAPI PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler;\n#define glReleaseShaderCompiler glad_glReleaseShaderCompiler\ntypedef void (APIENTRYP PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length);\nGLAPI PFNGLSHADERBINARYPROC glad_glShaderBinary;\n#define glShaderBinary glad_glShaderBinary\ntypedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision);\nGLAPI PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat;\n#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat\ntypedef void (APIENTRYP PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f);\nGLAPI PFNGLDEPTHRANGEFPROC glad_glDepthRangef;\n#define glDepthRangef glad_glDepthRangef\ntypedef void (APIENTRYP PFNGLCLEARDEPTHFPROC)(GLfloat d);\nGLAPI PFNGLCLEARDEPTHFPROC glad_glClearDepthf;\n#define glClearDepthf glad_glClearDepthf\ntypedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary);\nGLAPI PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary;\n#define glGetProgramBinary glad_glGetProgramBinary\ntypedef void (APIENTRYP PFNGLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length);\nGLAPI PFNGLPROGRAMBINARYPROC glad_glProgramBinary;\n#define glProgramBinary glad_glProgramBinary\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value);\nGLAPI PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri;\n#define glProgramParameteri glad_glProgramParameteri\ntypedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC)(GLuint pipeline, GLbitfield stages, GLuint program);\nGLAPI PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages;\n#define glUseProgramStages glad_glUseProgramStages\ntypedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC)(GLuint pipeline, GLuint program);\nGLAPI PFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram;\n#define glActiveShaderProgram glad_glActiveShaderProgram\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC)(GLenum type, GLsizei count, const GLchar *const*strings);\nGLAPI PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv;\n#define glCreateShaderProgramv glad_glCreateShaderProgramv\ntypedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC)(GLuint pipeline);\nGLAPI PFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline;\n#define glBindProgramPipeline glad_glBindProgramPipeline\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC)(GLsizei n, const GLuint *pipelines);\nGLAPI PFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines;\n#define glDeleteProgramPipelines glad_glDeleteProgramPipelines\ntypedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC)(GLsizei n, GLuint *pipelines);\nGLAPI PFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines;\n#define glGenProgramPipelines glad_glGenProgramPipelines\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC)(GLuint pipeline);\nGLAPI PFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline;\n#define glIsProgramPipeline glad_glIsProgramPipeline\ntypedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC)(GLuint pipeline, GLenum pname, GLint *params);\nGLAPI PFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv;\n#define glGetProgramPipelineiv glad_glGetProgramPipelineiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC)(GLuint program, GLint location, GLint v0);\nGLAPI PFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i;\n#define glProgramUniform1i glad_glProgramUniform1i\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv;\n#define glProgramUniform1iv glad_glProgramUniform1iv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC)(GLuint program, GLint location, GLfloat v0);\nGLAPI PFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f;\n#define glProgramUniform1f glad_glProgramUniform1f\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv;\n#define glProgramUniform1fv glad_glProgramUniform1fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC)(GLuint program, GLint location, GLdouble v0);\nGLAPI PFNGLPROGRAMUNIFORM1DPROC glad_glProgramUniform1d;\n#define glProgramUniform1d glad_glProgramUniform1d\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORM1DVPROC glad_glProgramUniform1dv;\n#define glProgramUniform1dv glad_glProgramUniform1dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC)(GLuint program, GLint location, GLuint v0);\nGLAPI PFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui;\n#define glProgramUniform1ui glad_glProgramUniform1ui\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value);\nGLAPI PFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv;\n#define glProgramUniform1uiv glad_glProgramUniform1uiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC)(GLuint program, GLint location, GLint v0, GLint v1);\nGLAPI PFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i;\n#define glProgramUniform2i glad_glProgramUniform2i\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv;\n#define glProgramUniform2iv glad_glProgramUniform2iv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1);\nGLAPI PFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f;\n#define glProgramUniform2f glad_glProgramUniform2f\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv;\n#define glProgramUniform2fv glad_glProgramUniform2fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1);\nGLAPI PFNGLPROGRAMUNIFORM2DPROC glad_glProgramUniform2d;\n#define glProgramUniform2d glad_glProgramUniform2d\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORM2DVPROC glad_glProgramUniform2dv;\n#define glProgramUniform2dv glad_glProgramUniform2dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1);\nGLAPI PFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui;\n#define glProgramUniform2ui glad_glProgramUniform2ui\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value);\nGLAPI PFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv;\n#define glProgramUniform2uiv glad_glProgramUniform2uiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2);\nGLAPI PFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i;\n#define glProgramUniform3i glad_glProgramUniform3i\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv;\n#define glProgramUniform3iv glad_glProgramUniform3iv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\nGLAPI PFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f;\n#define glProgramUniform3f glad_glProgramUniform3f\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv;\n#define glProgramUniform3fv glad_glProgramUniform3fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2);\nGLAPI PFNGLPROGRAMUNIFORM3DPROC glad_glProgramUniform3d;\n#define glProgramUniform3d glad_glProgramUniform3d\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORM3DVPROC glad_glProgramUniform3dv;\n#define glProgramUniform3dv glad_glProgramUniform3dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2);\nGLAPI PFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui;\n#define glProgramUniform3ui glad_glProgramUniform3ui\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value);\nGLAPI PFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv;\n#define glProgramUniform3uiv glad_glProgramUniform3uiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\nGLAPI PFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i;\n#define glProgramUniform4i glad_glProgramUniform4i\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv;\n#define glProgramUniform4iv glad_glProgramUniform4iv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\nGLAPI PFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f;\n#define glProgramUniform4f glad_glProgramUniform4f\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv;\n#define glProgramUniform4fv glad_glProgramUniform4fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3);\nGLAPI PFNGLPROGRAMUNIFORM4DPROC glad_glProgramUniform4d;\n#define glProgramUniform4d glad_glProgramUniform4d\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORM4DVPROC glad_glProgramUniform4dv;\n#define glProgramUniform4dv glad_glProgramUniform4dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);\nGLAPI PFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui;\n#define glProgramUniform4ui glad_glProgramUniform4ui\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value);\nGLAPI PFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv;\n#define glProgramUniform4uiv glad_glProgramUniform4uiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv;\n#define glProgramUniformMatrix2fv glad_glProgramUniformMatrix2fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv;\n#define glProgramUniformMatrix3fv glad_glProgramUniformMatrix3fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv;\n#define glProgramUniformMatrix4fv glad_glProgramUniformMatrix4fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2DVPROC glad_glProgramUniformMatrix2dv;\n#define glProgramUniformMatrix2dv glad_glProgramUniformMatrix2dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3DVPROC glad_glProgramUniformMatrix3dv;\n#define glProgramUniformMatrix3dv glad_glProgramUniformMatrix3dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4DVPROC glad_glProgramUniformMatrix4dv;\n#define glProgramUniformMatrix4dv glad_glProgramUniformMatrix4dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv;\n#define glProgramUniformMatrix2x3fv glad_glProgramUniformMatrix2x3fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv;\n#define glProgramUniformMatrix3x2fv glad_glProgramUniformMatrix3x2fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv;\n#define glProgramUniformMatrix2x4fv glad_glProgramUniformMatrix2x4fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv;\n#define glProgramUniformMatrix4x2fv glad_glProgramUniformMatrix4x2fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv;\n#define glProgramUniformMatrix3x4fv glad_glProgramUniformMatrix3x4fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv;\n#define glProgramUniformMatrix4x3fv glad_glProgramUniformMatrix4x3fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glad_glProgramUniformMatrix2x3dv;\n#define glProgramUniformMatrix2x3dv glad_glProgramUniformMatrix2x3dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glad_glProgramUniformMatrix3x2dv;\n#define glProgramUniformMatrix3x2dv glad_glProgramUniformMatrix3x2dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glad_glProgramUniformMatrix2x4dv;\n#define glProgramUniformMatrix2x4dv glad_glProgramUniformMatrix2x4dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glad_glProgramUniformMatrix4x2dv;\n#define glProgramUniformMatrix4x2dv glad_glProgramUniformMatrix4x2dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glad_glProgramUniformMatrix3x4dv;\n#define glProgramUniformMatrix3x4dv glad_glProgramUniformMatrix3x4dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glad_glProgramUniformMatrix4x3dv;\n#define glProgramUniformMatrix4x3dv glad_glProgramUniformMatrix4x3dv\ntypedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC)(GLuint pipeline);\nGLAPI PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline;\n#define glValidateProgramPipeline glad_glValidateProgramPipeline\ntypedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\nGLAPI PFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog;\n#define glGetProgramPipelineInfoLog glad_glGetProgramPipelineInfoLog\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC)(GLuint index, GLdouble x);\nGLAPI PFNGLVERTEXATTRIBL1DPROC glad_glVertexAttribL1d;\n#define glVertexAttribL1d glad_glVertexAttribL1d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC)(GLuint index, GLdouble x, GLdouble y);\nGLAPI PFNGLVERTEXATTRIBL2DPROC glad_glVertexAttribL2d;\n#define glVertexAttribL2d glad_glVertexAttribL2d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z);\nGLAPI PFNGLVERTEXATTRIBL3DPROC glad_glVertexAttribL3d;\n#define glVertexAttribL3d glad_glVertexAttribL3d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nGLAPI PFNGLVERTEXATTRIBL4DPROC glad_glVertexAttribL4d;\n#define glVertexAttribL4d glad_glVertexAttribL4d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC)(GLuint index, const GLdouble *v);\nGLAPI PFNGLVERTEXATTRIBL1DVPROC glad_glVertexAttribL1dv;\n#define glVertexAttribL1dv glad_glVertexAttribL1dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC)(GLuint index, const GLdouble *v);\nGLAPI PFNGLVERTEXATTRIBL2DVPROC glad_glVertexAttribL2dv;\n#define glVertexAttribL2dv glad_glVertexAttribL2dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC)(GLuint index, const GLdouble *v);\nGLAPI PFNGLVERTEXATTRIBL3DVPROC glad_glVertexAttribL3dv;\n#define glVertexAttribL3dv glad_glVertexAttribL3dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC)(GLuint index, const GLdouble *v);\nGLAPI PFNGLVERTEXATTRIBL4DVPROC glad_glVertexAttribL4dv;\n#define glVertexAttribL4dv glad_glVertexAttribL4dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);\nGLAPI PFNGLVERTEXATTRIBLPOINTERPROC glad_glVertexAttribLPointer;\n#define glVertexAttribLPointer glad_glVertexAttribLPointer\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC)(GLuint index, GLenum pname, GLdouble *params);\nGLAPI PFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv;\n#define glGetVertexAttribLdv glad_glGetVertexAttribLdv\ntypedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC)(GLuint first, GLsizei count, const GLfloat *v);\nGLAPI PFNGLVIEWPORTARRAYVPROC glad_glViewportArrayv;\n#define glViewportArrayv glad_glViewportArrayv\ntypedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h);\nGLAPI PFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf;\n#define glViewportIndexedf glad_glViewportIndexedf\ntypedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC)(GLuint index, const GLfloat *v);\nGLAPI PFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv;\n#define glViewportIndexedfv glad_glViewportIndexedfv\ntypedef void (APIENTRYP PFNGLSCISSORARRAYVPROC)(GLuint first, GLsizei count, const GLint *v);\nGLAPI PFNGLSCISSORARRAYVPROC glad_glScissorArrayv;\n#define glScissorArrayv glad_glScissorArrayv\ntypedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC)(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height);\nGLAPI PFNGLSCISSORINDEXEDPROC glad_glScissorIndexed;\n#define glScissorIndexed glad_glScissorIndexed\ntypedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC)(GLuint index, const GLint *v);\nGLAPI PFNGLSCISSORINDEXEDVPROC glad_glScissorIndexedv;\n#define glScissorIndexedv glad_glScissorIndexedv\ntypedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC)(GLuint first, GLsizei count, const GLdouble *v);\nGLAPI PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv;\n#define glDepthRangeArrayv glad_glDepthRangeArrayv\ntypedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC)(GLuint index, GLdouble n, GLdouble f);\nGLAPI PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed;\n#define glDepthRangeIndexed glad_glDepthRangeIndexed\ntypedef void (APIENTRYP PFNGLGETFLOATI_VPROC)(GLenum target, GLuint index, GLfloat *data);\nGLAPI PFNGLGETFLOATI_VPROC glad_glGetFloati_v;\n#define glGetFloati_v glad_glGetFloati_v\ntypedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC)(GLenum target, GLuint index, GLdouble *data);\nGLAPI PFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v;\n#define glGetDoublei_v glad_glGetDoublei_v\n#endif\n#ifndef GL_VERSION_4_2\n#define GL_VERSION_4_2 1\nGLAPI int GLAD_GL_VERSION_4_2;\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance);\nGLAPI PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance;\n#define glDrawArraysInstancedBaseInstance glad_glDrawArraysInstancedBaseInstance\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance;\n#define glDrawElementsInstancedBaseInstance glad_glDrawElementsInstancedBaseInstance\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance;\n#define glDrawElementsInstancedBaseVertexBaseInstance glad_glDrawElementsInstancedBaseVertexBaseInstance\ntypedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params);\nGLAPI PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ;\n#define glGetInternalformativ glad_glGetInternalformativ\ntypedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)(GLuint program, GLuint bufferIndex, GLenum pname, GLint *params);\nGLAPI PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv;\n#define glGetActiveAtomicCounterBufferiv glad_glGetActiveAtomicCounterBufferiv\ntypedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);\nGLAPI PFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture;\n#define glBindImageTexture glad_glBindImageTexture\ntypedef void (APIENTRYP PFNGLMEMORYBARRIERPROC)(GLbitfield barriers);\nGLAPI PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier;\n#define glMemoryBarrier glad_glMemoryBarrier\ntypedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);\nGLAPI PFNGLTEXSTORAGE1DPROC glad_glTexStorage1D;\n#define glTexStorage1D glad_glTexStorage1D\ntypedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D;\n#define glTexStorage2D glad_glTexStorage2D\ntypedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\nGLAPI PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D;\n#define glTexStorage3D glad_glTexStorage3D\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)(GLenum mode, GLuint id, GLsizei instancecount);\nGLAPI PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced;\n#define glDrawTransformFeedbackInstanced glad_glDrawTransformFeedbackInstanced\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)(GLenum mode, GLuint id, GLuint stream, GLsizei instancecount);\nGLAPI PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced;\n#define glDrawTransformFeedbackStreamInstanced glad_glDrawTransformFeedbackStreamInstanced\n#endif\n#ifndef GL_VERSION_4_3\n#define GL_VERSION_4_3 1\nGLAPI int GLAD_GL_VERSION_4_3;\ntypedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC)(GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data);\nGLAPI PFNGLCLEARBUFFERDATAPROC glad_glClearBufferData;\n#define glClearBufferData glad_glClearBufferData\ntypedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC)(GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data);\nGLAPI PFNGLCLEARBUFFERSUBDATAPROC glad_glClearBufferSubData;\n#define glClearBufferSubData glad_glClearBufferSubData\ntypedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);\nGLAPI PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute;\n#define glDispatchCompute glad_glDispatchCompute\ntypedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC)(GLintptr indirect);\nGLAPI PFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect;\n#define glDispatchComputeIndirect glad_glDispatchComputeIndirect\ntypedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);\nGLAPI PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData;\n#define glCopyImageSubData glad_glCopyImageSubData\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC)(GLenum target, GLenum pname, GLint param);\nGLAPI PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri;\n#define glFramebufferParameteri glad_glFramebufferParameteri\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv;\n#define glGetFramebufferParameteriv glad_glGetFramebufferParameteriv\ntypedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params);\nGLAPI PFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v;\n#define glGetInternalformati64v glad_glGetInternalformati64v\ntypedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth);\nGLAPI PFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage;\n#define glInvalidateTexSubImage glad_glInvalidateTexSubImage\ntypedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC)(GLuint texture, GLint level);\nGLAPI PFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage;\n#define glInvalidateTexImage glad_glInvalidateTexImage\ntypedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length);\nGLAPI PFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData;\n#define glInvalidateBufferSubData glad_glInvalidateBufferSubData\ntypedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer);\nGLAPI PFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData;\n#define glInvalidateBufferData glad_glInvalidateBufferData\ntypedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum *attachments);\nGLAPI PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer;\n#define glInvalidateFramebuffer glad_glInvalidateFramebuffer\ntypedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer;\n#define glInvalidateSubFramebuffer glad_glInvalidateSubFramebuffer\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC)(GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride);\nGLAPI PFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect;\n#define glMultiDrawArraysIndirect glad_glMultiDrawArraysIndirect\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride);\nGLAPI PFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect;\n#define glMultiDrawElementsIndirect glad_glMultiDrawElementsIndirect\ntypedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC)(GLuint program, GLenum programInterface, GLenum pname, GLint *params);\nGLAPI PFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv;\n#define glGetProgramInterfaceiv glad_glGetProgramInterfaceiv\ntypedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC)(GLuint program, GLenum programInterface, const GLchar *name);\nGLAPI PFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex;\n#define glGetProgramResourceIndex glad_glGetProgramResourceIndex\ntypedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);\nGLAPI PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName;\n#define glGetProgramResourceName glad_glGetProgramResourceName\ntypedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params);\nGLAPI PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv;\n#define glGetProgramResourceiv glad_glGetProgramResourceiv\ntypedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC)(GLuint program, GLenum programInterface, const GLchar *name);\nGLAPI PFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation;\n#define glGetProgramResourceLocation glad_glGetProgramResourceLocation\ntypedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)(GLuint program, GLenum programInterface, const GLchar *name);\nGLAPI PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glad_glGetProgramResourceLocationIndex;\n#define glGetProgramResourceLocationIndex glad_glGetProgramResourceLocationIndex\ntypedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC)(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding);\nGLAPI PFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding;\n#define glShaderStorageBlockBinding glad_glShaderStorageBlockBinding\ntypedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);\nGLAPI PFNGLTEXBUFFERRANGEPROC glad_glTexBufferRange;\n#define glTexBufferRange glad_glTexBufferRange\ntypedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\nGLAPI PFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample;\n#define glTexStorage2DMultisample glad_glTexStorage2DMultisample\ntypedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\nGLAPI PFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample;\n#define glTexStorage3DMultisample glad_glTexStorage3DMultisample\ntypedef void (APIENTRYP PFNGLTEXTUREVIEWPROC)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers);\nGLAPI PFNGLTEXTUREVIEWPROC glad_glTextureView;\n#define glTextureView glad_glTextureView\ntypedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);\nGLAPI PFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer;\n#define glBindVertexBuffer glad_glBindVertexBuffer\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);\nGLAPI PFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat;\n#define glVertexAttribFormat glad_glVertexAttribFormat\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\nGLAPI PFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat;\n#define glVertexAttribIFormat glad_glVertexAttribIFormat\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\nGLAPI PFNGLVERTEXATTRIBLFORMATPROC glad_glVertexAttribLFormat;\n#define glVertexAttribLFormat glad_glVertexAttribLFormat\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC)(GLuint attribindex, GLuint bindingindex);\nGLAPI PFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding;\n#define glVertexAttribBinding glad_glVertexAttribBinding\ntypedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC)(GLuint bindingindex, GLuint divisor);\nGLAPI PFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor;\n#define glVertexBindingDivisor glad_glVertexBindingDivisor\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);\nGLAPI PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl;\n#define glDebugMessageControl glad_glDebugMessageControl\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);\nGLAPI PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert;\n#define glDebugMessageInsert glad_glDebugMessageInsert\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void *userParam);\nGLAPI PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback;\n#define glDebugMessageCallback glad_glDebugMessageCallback\ntypedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog);\nGLAPI PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog;\n#define glGetDebugMessageLog glad_glGetDebugMessageLog\ntypedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message);\nGLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup;\n#define glPushDebugGroup glad_glPushDebugGroup\ntypedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(void);\nGLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup;\n#define glPopDebugGroup glad_glPopDebugGroup\ntypedef void (APIENTRYP PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label);\nGLAPI PFNGLOBJECTLABELPROC glad_glObjectLabel;\n#define glObjectLabel glad_glObjectLabel\ntypedef void (APIENTRYP PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label);\nGLAPI PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel;\n#define glGetObjectLabel glad_glGetObjectLabel\ntypedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC)(const void *ptr, GLsizei length, const GLchar *label);\nGLAPI PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel;\n#define glObjectPtrLabel glad_glObjectPtrLabel\ntypedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label);\nGLAPI PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel;\n#define glGetObjectPtrLabel glad_glGetObjectPtrLabel\n#endif\n#ifndef GL_VERSION_4_4\n#define GL_VERSION_4_4 1\nGLAPI int GLAD_GL_VERSION_4_4;\ntypedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags);\nGLAPI PFNGLBUFFERSTORAGEPROC glad_glBufferStorage;\n#define glBufferStorage glad_glBufferStorage\ntypedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, const void *data);\nGLAPI PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage;\n#define glClearTexImage glad_glClearTexImage\ntypedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data);\nGLAPI PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage;\n#define glClearTexSubImage glad_glClearTexSubImage\ntypedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint *buffers);\nGLAPI PFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase;\n#define glBindBuffersBase glad_glBindBuffersBase\ntypedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes);\nGLAPI PFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange;\n#define glBindBuffersRange glad_glBindBuffersRange\ntypedef void (APIENTRYP PFNGLBINDTEXTURESPROC)(GLuint first, GLsizei count, const GLuint *textures);\nGLAPI PFNGLBINDTEXTURESPROC glad_glBindTextures;\n#define glBindTextures glad_glBindTextures\ntypedef void (APIENTRYP PFNGLBINDSAMPLERSPROC)(GLuint first, GLsizei count, const GLuint *samplers);\nGLAPI PFNGLBINDSAMPLERSPROC glad_glBindSamplers;\n#define glBindSamplers glad_glBindSamplers\ntypedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC)(GLuint first, GLsizei count, const GLuint *textures);\nGLAPI PFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures;\n#define glBindImageTextures glad_glBindImageTextures\ntypedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC)(GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides);\nGLAPI PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers;\n#define glBindVertexBuffers glad_glBindVertexBuffers\n#endif\n#ifndef GL_VERSION_4_5\n#define GL_VERSION_4_5 1\nGLAPI int GLAD_GL_VERSION_4_5;\ntypedef void (APIENTRYP PFNGLCLIPCONTROLPROC)(GLenum origin, GLenum depth);\nGLAPI PFNGLCLIPCONTROLPROC glad_glClipControl;\n#define glClipControl glad_glClipControl\ntypedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint *ids);\nGLAPI PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks;\n#define glCreateTransformFeedbacks glad_glCreateTransformFeedbacks\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)(GLuint xfb, GLuint index, GLuint buffer);\nGLAPI PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase;\n#define glTransformFeedbackBufferBase glad_glTransformFeedbackBufferBase\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)(GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);\nGLAPI PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange;\n#define glTransformFeedbackBufferRange glad_glTransformFeedbackBufferRange\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC)(GLuint xfb, GLenum pname, GLint *param);\nGLAPI PFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv;\n#define glGetTransformFeedbackiv glad_glGetTransformFeedbackiv\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint *param);\nGLAPI PFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v;\n#define glGetTransformFeedbacki_v glad_glGetTransformFeedbacki_v\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint64 *param);\nGLAPI PFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v;\n#define glGetTransformFeedbacki64_v glad_glGetTransformFeedbacki64_v\ntypedef void (APIENTRYP PFNGLCREATEBUFFERSPROC)(GLsizei n, GLuint *buffers);\nGLAPI PFNGLCREATEBUFFERSPROC glad_glCreateBuffers;\n#define glCreateBuffers glad_glCreateBuffers\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC)(GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags);\nGLAPI PFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage;\n#define glNamedBufferStorage glad_glNamedBufferStorage\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC)(GLuint buffer, GLsizeiptr size, const void *data, GLenum usage);\nGLAPI PFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData;\n#define glNamedBufferData glad_glNamedBufferData\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data);\nGLAPI PFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData;\n#define glNamedBufferSubData glad_glNamedBufferSubData\ntypedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC)(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);\nGLAPI PFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData;\n#define glCopyNamedBufferSubData glad_glCopyNamedBufferSubData\ntypedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC)(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data);\nGLAPI PFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData;\n#define glClearNamedBufferData glad_glClearNamedBufferData\ntypedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data);\nGLAPI PFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData;\n#define glClearNamedBufferSubData glad_glClearNamedBufferSubData\ntypedef void * (APIENTRYP PFNGLMAPNAMEDBUFFERPROC)(GLuint buffer, GLenum access);\nGLAPI PFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer;\n#define glMapNamedBuffer glad_glMapNamedBuffer\ntypedef void * (APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access);\nGLAPI PFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange;\n#define glMapNamedBufferRange glad_glMapNamedBufferRange\ntypedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC)(GLuint buffer);\nGLAPI PFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer;\n#define glUnmapNamedBuffer glad_glUnmapNamedBuffer\ntypedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length);\nGLAPI PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange;\n#define glFlushMappedNamedBufferRange glad_glFlushMappedNamedBufferRange\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC)(GLuint buffer, GLenum pname, GLint *params);\nGLAPI PFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv;\n#define glGetNamedBufferParameteriv glad_glGetNamedBufferParameteriv\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)(GLuint buffer, GLenum pname, GLint64 *params);\nGLAPI PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v;\n#define glGetNamedBufferParameteri64v glad_glGetNamedBufferParameteri64v\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC)(GLuint buffer, GLenum pname, void **params);\nGLAPI PFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv;\n#define glGetNamedBufferPointerv glad_glGetNamedBufferPointerv\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, void *data);\nGLAPI PFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData;\n#define glGetNamedBufferSubData glad_glGetNamedBufferSubData\ntypedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers);\nGLAPI PFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers;\n#define glCreateFramebuffers glad_glCreateFramebuffers\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);\nGLAPI PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer;\n#define glNamedFramebufferRenderbuffer glad_glNamedFramebufferRenderbuffer\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)(GLuint framebuffer, GLenum pname, GLint param);\nGLAPI PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri;\n#define glNamedFramebufferParameteri glad_glNamedFramebufferParameteri\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level);\nGLAPI PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture;\n#define glNamedFramebufferTexture glad_glNamedFramebufferTexture\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer);\nGLAPI PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer;\n#define glNamedFramebufferTextureLayer glad_glNamedFramebufferTextureLayer\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)(GLuint framebuffer, GLenum buf);\nGLAPI PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer;\n#define glNamedFramebufferDrawBuffer glad_glNamedFramebufferDrawBuffer\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)(GLuint framebuffer, GLsizei n, const GLenum *bufs);\nGLAPI PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers;\n#define glNamedFramebufferDrawBuffers glad_glNamedFramebufferDrawBuffers\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)(GLuint framebuffer, GLenum src);\nGLAPI PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer;\n#define glNamedFramebufferReadBuffer glad_glNamedFramebufferReadBuffer\ntypedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments);\nGLAPI PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData;\n#define glInvalidateNamedFramebufferData glad_glInvalidateNamedFramebufferData\ntypedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData;\n#define glInvalidateNamedFramebufferSubData glad_glInvalidateNamedFramebufferSubData\ntypedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value);\nGLAPI PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv;\n#define glClearNamedFramebufferiv glad_glClearNamedFramebufferiv\ntypedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value);\nGLAPI PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv;\n#define glClearNamedFramebufferuiv glad_glClearNamedFramebufferuiv\ntypedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value);\nGLAPI PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv;\n#define glClearNamedFramebufferfv glad_glClearNamedFramebufferfv\ntypedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);\nGLAPI PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi;\n#define glClearNamedFramebufferfi glad_glClearNamedFramebufferfi\ntypedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC)(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);\nGLAPI PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer;\n#define glBlitNamedFramebuffer glad_glBlitNamedFramebuffer\ntypedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)(GLuint framebuffer, GLenum target);\nGLAPI PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus;\n#define glCheckNamedFramebufferStatus glad_glCheckNamedFramebufferStatus\ntypedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)(GLuint framebuffer, GLenum pname, GLint *param);\nGLAPI PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv;\n#define glGetNamedFramebufferParameteriv glad_glGetNamedFramebufferParameteriv\ntypedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params);\nGLAPI PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv;\n#define glGetNamedFramebufferAttachmentParameteriv glad_glGetNamedFramebufferAttachmentParameteriv\ntypedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers);\nGLAPI PFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers;\n#define glCreateRenderbuffers glad_glCreateRenderbuffers\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC)(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI PFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage;\n#define glNamedRenderbufferStorage glad_glNamedRenderbufferStorage\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample;\n#define glNamedRenderbufferStorageMultisample glad_glNamedRenderbufferStorageMultisample\ntypedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)(GLuint renderbuffer, GLenum pname, GLint *params);\nGLAPI PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv;\n#define glGetNamedRenderbufferParameteriv glad_glGetNamedRenderbufferParameteriv\ntypedef void (APIENTRYP PFNGLCREATETEXTURESPROC)(GLenum target, GLsizei n, GLuint *textures);\nGLAPI PFNGLCREATETEXTURESPROC glad_glCreateTextures;\n#define glCreateTextures glad_glCreateTextures\ntypedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC)(GLuint texture, GLenum internalformat, GLuint buffer);\nGLAPI PFNGLTEXTUREBUFFERPROC glad_glTextureBuffer;\n#define glTextureBuffer glad_glTextureBuffer\ntypedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC)(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);\nGLAPI PFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange;\n#define glTextureBufferRange glad_glTextureBufferRange\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width);\nGLAPI PFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D;\n#define glTextureStorage1D glad_glTextureStorage1D\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI PFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D;\n#define glTextureStorage2D glad_glTextureStorage2D\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\nGLAPI PFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D;\n#define glTextureStorage3D glad_glTextureStorage3D\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\nGLAPI PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample;\n#define glTextureStorage2DMultisample glad_glTextureStorage2DMultisample\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\nGLAPI PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample;\n#define glTextureStorage3DMultisample glad_glTextureStorage3DMultisample\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D;\n#define glTextureSubImage1D glad_glTextureSubImage1D\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D;\n#define glTextureSubImage2D glad_glTextureSubImage2D\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D;\n#define glTextureSubImage3D glad_glTextureSubImage3D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D;\n#define glCompressedTextureSubImage1D glad_glCompressedTextureSubImage1D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D;\n#define glCompressedTextureSubImage2D glad_glCompressedTextureSubImage2D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D;\n#define glCompressedTextureSubImage3D glad_glCompressedTextureSubImage3D\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\nGLAPI PFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D;\n#define glCopyTextureSubImage1D glad_glCopyTextureSubImage1D\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D;\n#define glCopyTextureSubImage2D glad_glCopyTextureSubImage2D\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D;\n#define glCopyTextureSubImage3D glad_glCopyTextureSubImage3D\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC)(GLuint texture, GLenum pname, GLfloat param);\nGLAPI PFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf;\n#define glTextureParameterf glad_glTextureParameterf\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, const GLfloat *param);\nGLAPI PFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv;\n#define glTextureParameterfv glad_glTextureParameterfv\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC)(GLuint texture, GLenum pname, GLint param);\nGLAPI PFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri;\n#define glTextureParameteri glad_glTextureParameteri\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, const GLint *params);\nGLAPI PFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv;\n#define glTextureParameterIiv glad_glTextureParameterIiv\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, const GLuint *params);\nGLAPI PFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv;\n#define glTextureParameterIuiv glad_glTextureParameterIuiv\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, const GLint *param);\nGLAPI PFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv;\n#define glTextureParameteriv glad_glTextureParameteriv\ntypedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC)(GLuint texture);\nGLAPI PFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap;\n#define glGenerateTextureMipmap glad_glGenerateTextureMipmap\ntypedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC)(GLuint unit, GLuint texture);\nGLAPI PFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit;\n#define glBindTextureUnit glad_glBindTextureUnit\ntypedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels);\nGLAPI PFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage;\n#define glGetTextureImage glad_glGetTextureImage\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLsizei bufSize, void *pixels);\nGLAPI PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage;\n#define glGetCompressedTextureImage glad_glGetCompressedTextureImage\ntypedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC)(GLuint texture, GLint level, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv;\n#define glGetTextureLevelParameterfv glad_glGetTextureLevelParameterfv\ntypedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC)(GLuint texture, GLint level, GLenum pname, GLint *params);\nGLAPI PFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv;\n#define glGetTextureLevelParameteriv glad_glGetTextureLevelParameteriv\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv;\n#define glGetTextureParameterfv glad_glGetTextureParameterfv\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, GLint *params);\nGLAPI PFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv;\n#define glGetTextureParameterIiv glad_glGetTextureParameterIiv\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, GLuint *params);\nGLAPI PFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv;\n#define glGetTextureParameterIuiv glad_glGetTextureParameterIuiv\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, GLint *params);\nGLAPI PFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv;\n#define glGetTextureParameteriv glad_glGetTextureParameteriv\ntypedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays);\nGLAPI PFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays;\n#define glCreateVertexArrays glad_glCreateVertexArrays\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index);\nGLAPI PFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib;\n#define glDisableVertexArrayAttrib glad_glDisableVertexArrayAttrib\ntypedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index);\nGLAPI PFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib;\n#define glEnableVertexArrayAttrib glad_glEnableVertexArrayAttrib\ntypedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC)(GLuint vaobj, GLuint buffer);\nGLAPI PFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer;\n#define glVertexArrayElementBuffer glad_glVertexArrayElementBuffer\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC)(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);\nGLAPI PFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer;\n#define glVertexArrayVertexBuffer glad_glVertexArrayVertexBuffer\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC)(GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides);\nGLAPI PFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers;\n#define glVertexArrayVertexBuffers glad_glVertexArrayVertexBuffers\ntypedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC)(GLuint vaobj, GLuint attribindex, GLuint bindingindex);\nGLAPI PFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding;\n#define glVertexArrayAttribBinding glad_glVertexArrayAttribBinding\ntypedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);\nGLAPI PFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat;\n#define glVertexArrayAttribFormat glad_glVertexArrayAttribFormat\ntypedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\nGLAPI PFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat;\n#define glVertexArrayAttribIFormat glad_glVertexArrayAttribIFormat\ntypedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\nGLAPI PFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat;\n#define glVertexArrayAttribLFormat glad_glVertexArrayAttribLFormat\ntypedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC)(GLuint vaobj, GLuint bindingindex, GLuint divisor);\nGLAPI PFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor;\n#define glVertexArrayBindingDivisor glad_glVertexArrayBindingDivisor\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC)(GLuint vaobj, GLenum pname, GLint *param);\nGLAPI PFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv;\n#define glGetVertexArrayiv glad_glGetVertexArrayiv\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint *param);\nGLAPI PFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv;\n#define glGetVertexArrayIndexediv glad_glGetVertexArrayIndexediv\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint64 *param);\nGLAPI PFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv;\n#define glGetVertexArrayIndexed64iv glad_glGetVertexArrayIndexed64iv\ntypedef void (APIENTRYP PFNGLCREATESAMPLERSPROC)(GLsizei n, GLuint *samplers);\nGLAPI PFNGLCREATESAMPLERSPROC glad_glCreateSamplers;\n#define glCreateSamplers glad_glCreateSamplers\ntypedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC)(GLsizei n, GLuint *pipelines);\nGLAPI PFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines;\n#define glCreateProgramPipelines glad_glCreateProgramPipelines\ntypedef void (APIENTRYP PFNGLCREATEQUERIESPROC)(GLenum target, GLsizei n, GLuint *ids);\nGLAPI PFNGLCREATEQUERIESPROC glad_glCreateQueries;\n#define glCreateQueries glad_glCreateQueries\ntypedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\nGLAPI PFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v;\n#define glGetQueryBufferObjecti64v glad_glGetQueryBufferObjecti64v\ntypedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\nGLAPI PFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv;\n#define glGetQueryBufferObjectiv glad_glGetQueryBufferObjectiv\ntypedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\nGLAPI PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v;\n#define glGetQueryBufferObjectui64v glad_glGetQueryBufferObjectui64v\ntypedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\nGLAPI PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv;\n#define glGetQueryBufferObjectuiv glad_glGetQueryBufferObjectuiv\ntypedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers);\nGLAPI PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion;\n#define glMemoryBarrierByRegion glad_glMemoryBarrierByRegion\ntypedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels);\nGLAPI PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage;\n#define glGetTextureSubImage glad_glGetTextureSubImage\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels);\nGLAPI PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage;\n#define glGetCompressedTextureSubImage glad_glGetCompressedTextureSubImage\ntypedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC)(void);\nGLAPI PFNGLGETGRAPHICSRESETSTATUSPROC glad_glGetGraphicsResetStatus;\n#define glGetGraphicsResetStatus glad_glGetGraphicsResetStatus\ntypedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint lod, GLsizei bufSize, void *pixels);\nGLAPI PFNGLGETNCOMPRESSEDTEXIMAGEPROC glad_glGetnCompressedTexImage;\n#define glGetnCompressedTexImage glad_glGetnCompressedTexImage\ntypedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels);\nGLAPI PFNGLGETNTEXIMAGEPROC glad_glGetnTexImage;\n#define glGetnTexImage glad_glGetnTexImage\ntypedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble *params);\nGLAPI PFNGLGETNUNIFORMDVPROC glad_glGetnUniformdv;\n#define glGetnUniformdv glad_glGetnUniformdv\ntypedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params);\nGLAPI PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv;\n#define glGetnUniformfv glad_glGetnUniformfv\ntypedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLint *params);\nGLAPI PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv;\n#define glGetnUniformiv glad_glGetnUniformiv\ntypedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint *params);\nGLAPI PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv;\n#define glGetnUniformuiv glad_glGetnUniformuiv\ntypedef void (APIENTRYP PFNGLREADNPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);\nGLAPI PFNGLREADNPIXELSPROC glad_glReadnPixels;\n#define glReadnPixels glad_glReadnPixels\ntypedef void (APIENTRYP PFNGLGETNMAPDVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble *v);\nGLAPI PFNGLGETNMAPDVPROC glad_glGetnMapdv;\n#define glGetnMapdv glad_glGetnMapdv\ntypedef void (APIENTRYP PFNGLGETNMAPFVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat *v);\nGLAPI PFNGLGETNMAPFVPROC glad_glGetnMapfv;\n#define glGetnMapfv glad_glGetnMapfv\ntypedef void (APIENTRYP PFNGLGETNMAPIVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint *v);\nGLAPI PFNGLGETNMAPIVPROC glad_glGetnMapiv;\n#define glGetnMapiv glad_glGetnMapiv\ntypedef void (APIENTRYP PFNGLGETNPIXELMAPFVPROC)(GLenum map, GLsizei bufSize, GLfloat *values);\nGLAPI PFNGLGETNPIXELMAPFVPROC glad_glGetnPixelMapfv;\n#define glGetnPixelMapfv glad_glGetnPixelMapfv\ntypedef void (APIENTRYP PFNGLGETNPIXELMAPUIVPROC)(GLenum map, GLsizei bufSize, GLuint *values);\nGLAPI PFNGLGETNPIXELMAPUIVPROC glad_glGetnPixelMapuiv;\n#define glGetnPixelMapuiv glad_glGetnPixelMapuiv\ntypedef void (APIENTRYP PFNGLGETNPIXELMAPUSVPROC)(GLenum map, GLsizei bufSize, GLushort *values);\nGLAPI PFNGLGETNPIXELMAPUSVPROC glad_glGetnPixelMapusv;\n#define glGetnPixelMapusv glad_glGetnPixelMapusv\ntypedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEPROC)(GLsizei bufSize, GLubyte *pattern);\nGLAPI PFNGLGETNPOLYGONSTIPPLEPROC glad_glGetnPolygonStipple;\n#define glGetnPolygonStipple glad_glGetnPolygonStipple\ntypedef void (APIENTRYP PFNGLGETNCOLORTABLEPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table);\nGLAPI PFNGLGETNCOLORTABLEPROC glad_glGetnColorTable;\n#define glGetnColorTable glad_glGetnColorTable\ntypedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image);\nGLAPI PFNGLGETNCONVOLUTIONFILTERPROC glad_glGetnConvolutionFilter;\n#define glGetnConvolutionFilter glad_glGetnConvolutionFilter\ntypedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span);\nGLAPI PFNGLGETNSEPARABLEFILTERPROC glad_glGetnSeparableFilter;\n#define glGetnSeparableFilter glad_glGetnSeparableFilter\ntypedef void (APIENTRYP PFNGLGETNHISTOGRAMPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values);\nGLAPI PFNGLGETNHISTOGRAMPROC glad_glGetnHistogram;\n#define glGetnHistogram glad_glGetnHistogram\ntypedef void (APIENTRYP PFNGLGETNMINMAXPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values);\nGLAPI PFNGLGETNMINMAXPROC glad_glGetnMinmax;\n#define glGetnMinmax glad_glGetnMinmax\ntypedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC)(void);\nGLAPI PFNGLTEXTUREBARRIERPROC glad_glTextureBarrier;\n#define glTextureBarrier glad_glTextureBarrier\n#endif\n#ifndef GL_VERSION_4_6\n#define GL_VERSION_4_6 1\nGLAPI int GLAD_GL_VERSION_4_6;\ntypedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC)(GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);\nGLAPI PFNGLSPECIALIZESHADERPROC glad_glSpecializeShader;\n#define glSpecializeShader glad_glSpecializeShader\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)(GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);\nGLAPI PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glad_glMultiDrawArraysIndirectCount;\n#define glMultiDrawArraysIndirectCount glad_glMultiDrawArraysIndirectCount\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)(GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);\nGLAPI PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glad_glMultiDrawElementsIndirectCount;\n#define glMultiDrawElementsIndirectCount glad_glMultiDrawElementsIndirectCount\ntypedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC)(GLfloat factor, GLfloat units, GLfloat clamp);\nGLAPI PFNGLPOLYGONOFFSETCLAMPPROC glad_glPolygonOffsetClamp;\n#define glPolygonOffsetClamp glad_glPolygonOffsetClamp\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "extern/glad/src/glad.c",
    "content": "/*\n\n    OpenGL loader generated by glad 0.1.34 on Sat Jul  8 18:48:45 2023.\n\n    Language/Generator: C/C++\n    Specification: gl\n    APIs: gl=4.6\n    Profile: compatibility\n    Extensions:\n        \n    Loader: True\n    Local files: False\n    Omit khrplatform: False\n    Reproducible: False\n\n    Commandline:\n        --profile=\"compatibility\" --api=\"gl=4.6\" --generator=\"c\" --spec=\"gl\" --extensions=\"\"\n    Online:\n        https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D4.6\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <glad/glad.h>\n\nstatic void* get_proc(const char *namez);\n\n#if defined(_WIN32) || defined(__CYGWIN__)\n#ifndef _WINDOWS_\n#undef APIENTRY\n#endif\n#include <windows.h>\nstatic HMODULE libGL;\n\ntypedef void* (APIENTRYP PFNWGLGETPROCADDRESSPROC_PRIVATE)(const char*);\nstatic PFNWGLGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;\n\n#ifdef _MSC_VER\n#ifdef __has_include\n  #if __has_include(<winapifamily.h>)\n    #define HAVE_WINAPIFAMILY 1\n  #endif\n#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_\n  #define HAVE_WINAPIFAMILY 1\n#endif\n#endif\n\n#ifdef HAVE_WINAPIFAMILY\n  #include <winapifamily.h>\n  #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)\n    #define IS_UWP 1\n  #endif\n#endif\n\nstatic\nint open_gl(void) {\n#ifndef IS_UWP\n    libGL = LoadLibraryW(L\"opengl32.dll\");\n    if(libGL != NULL) {\n        void (* tmp)(void);\n        tmp = (void(*)(void)) GetProcAddress(libGL, \"wglGetProcAddress\");\n        gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE) tmp;\n        return gladGetProcAddressPtr != NULL;\n    }\n#endif\n\n    return 0;\n}\n\nstatic\nvoid close_gl(void) {\n    if(libGL != NULL) {\n        FreeLibrary((HMODULE) libGL);\n        libGL = NULL;\n    }\n}\n#else\n#include <dlfcn.h>\nstatic void* libGL;\n\n#if !defined(__APPLE__) && !defined(__HAIKU__)\ntypedef void* (APIENTRYP PFNGLXGETPROCADDRESSPROC_PRIVATE)(const char*);\nstatic PFNGLXGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;\n#endif\n\nstatic\nint open_gl(void) {\n#ifdef __APPLE__\n    static const char *NAMES[] = {\n        \"../Frameworks/OpenGL.framework/OpenGL\",\n        \"/Library/Frameworks/OpenGL.framework/OpenGL\",\n        \"/System/Library/Frameworks/OpenGL.framework/OpenGL\",\n        \"/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL\"\n    };\n#else\n    static const char *NAMES[] = {\"libGL.so.1\", \"libGL.so\"};\n#endif\n\n    unsigned int index = 0;\n    for(index = 0; index < (sizeof(NAMES) / sizeof(NAMES[0])); index++) {\n        libGL = dlopen(NAMES[index], RTLD_NOW | RTLD_GLOBAL);\n\n        if(libGL != NULL) {\n#if defined(__APPLE__) || defined(__HAIKU__)\n            return 1;\n#else\n            gladGetProcAddressPtr = (PFNGLXGETPROCADDRESSPROC_PRIVATE)dlsym(libGL,\n                \"glXGetProcAddressARB\");\n            return gladGetProcAddressPtr != NULL;\n#endif\n        }\n    }\n\n    return 0;\n}\n\nstatic\nvoid close_gl(void) {\n    if(libGL != NULL) {\n        dlclose(libGL);\n        libGL = NULL;\n    }\n}\n#endif\n\nstatic\nvoid* get_proc(const char *namez) {\n    void* result = NULL;\n    if(libGL == NULL) return NULL;\n\n#if !defined(__APPLE__) && !defined(__HAIKU__)\n    if(gladGetProcAddressPtr != NULL) {\n        result = gladGetProcAddressPtr(namez);\n    }\n#endif\n    if(result == NULL) {\n#if defined(_WIN32) || defined(__CYGWIN__)\n        result = (void*)GetProcAddress((HMODULE) libGL, namez);\n#else\n        result = dlsym(libGL, namez);\n#endif\n    }\n\n    return result;\n}\n\nint gladLoadGL(void) {\n    int status = 0;\n\n    if(open_gl()) {\n        status = gladLoadGLLoader(&get_proc);\n        close_gl();\n    }\n\n    return status;\n}\n\nstruct gladGLversionStruct GLVersion = { 0, 0 };\n\n#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)\n#define _GLAD_IS_SOME_NEW_VERSION 1\n#endif\n\nstatic int max_loaded_major;\nstatic int max_loaded_minor;\n\nstatic const char *exts = NULL;\nstatic int num_exts_i = 0;\nstatic char **exts_i = NULL;\n\nstatic int get_exts(void) {\n#ifdef _GLAD_IS_SOME_NEW_VERSION\n    if(max_loaded_major < 3) {\n#endif\n        exts = (const char *)glGetString(GL_EXTENSIONS);\n#ifdef _GLAD_IS_SOME_NEW_VERSION\n    } else {\n        unsigned int index;\n\n        num_exts_i = 0;\n        glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i);\n        if (num_exts_i > 0) {\n            exts_i = (char **)malloc((size_t)num_exts_i * (sizeof *exts_i));\n        }\n\n        if (exts_i == NULL) {\n            return 0;\n        }\n\n        for(index = 0; index < (unsigned)num_exts_i; index++) {\n            const char *gl_str_tmp = (const char*)glGetStringi(GL_EXTENSIONS, index);\n            size_t len = strlen(gl_str_tmp);\n\n            char *local_str = (char*)malloc((len+1) * sizeof(char));\n            if(local_str != NULL) {\n                memcpy(local_str, gl_str_tmp, (len+1) * sizeof(char));\n            }\n            exts_i[index] = local_str;\n        }\n    }\n#endif\n    return 1;\n}\n\nstatic void free_exts(void) {\n    if (exts_i != NULL) {\n        int index;\n        for(index = 0; index < num_exts_i; index++) {\n            free((char *)exts_i[index]);\n        }\n        free((void *)exts_i);\n        exts_i = NULL;\n    }\n}\n\nstatic int has_ext(const char *ext) {\n#ifdef _GLAD_IS_SOME_NEW_VERSION\n    if(max_loaded_major < 3) {\n#endif\n        const char *extensions;\n        const char *loc;\n        const char *terminator;\n        extensions = exts;\n        if(extensions == NULL || ext == NULL) {\n            return 0;\n        }\n\n        while(1) {\n            loc = strstr(extensions, ext);\n            if(loc == NULL) {\n                return 0;\n            }\n\n            terminator = loc + strlen(ext);\n            if((loc == extensions || *(loc - 1) == ' ') &&\n                (*terminator == ' ' || *terminator == '\\0')) {\n                return 1;\n            }\n            extensions = terminator;\n        }\n#ifdef _GLAD_IS_SOME_NEW_VERSION\n    } else {\n        int index;\n        if(exts_i == NULL) return 0;\n        for(index = 0; index < num_exts_i; index++) {\n            const char *e = exts_i[index];\n\n            if(exts_i[index] != NULL && strcmp(e, ext) == 0) {\n                return 1;\n            }\n        }\n    }\n#endif\n\n    return 0;\n}\nint GLAD_GL_VERSION_1_0 = 0;\nint GLAD_GL_VERSION_1_1 = 0;\nint GLAD_GL_VERSION_1_2 = 0;\nint GLAD_GL_VERSION_1_3 = 0;\nint GLAD_GL_VERSION_1_4 = 0;\nint GLAD_GL_VERSION_1_5 = 0;\nint GLAD_GL_VERSION_2_0 = 0;\nint GLAD_GL_VERSION_2_1 = 0;\nint GLAD_GL_VERSION_3_0 = 0;\nint GLAD_GL_VERSION_3_1 = 0;\nint GLAD_GL_VERSION_3_2 = 0;\nint GLAD_GL_VERSION_3_3 = 0;\nint GLAD_GL_VERSION_4_0 = 0;\nint GLAD_GL_VERSION_4_1 = 0;\nint GLAD_GL_VERSION_4_2 = 0;\nint GLAD_GL_VERSION_4_3 = 0;\nint GLAD_GL_VERSION_4_4 = 0;\nint GLAD_GL_VERSION_4_5 = 0;\nint GLAD_GL_VERSION_4_6 = 0;\nPFNGLACCUMPROC glad_glAccum = NULL;\nPFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram = NULL;\nPFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL;\nPFNGLALPHAFUNCPROC glad_glAlphaFunc = NULL;\nPFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident = NULL;\nPFNGLARRAYELEMENTPROC glad_glArrayElement = NULL;\nPFNGLATTACHSHADERPROC glad_glAttachShader = NULL;\nPFNGLBEGINPROC glad_glBegin = NULL;\nPFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL;\nPFNGLBEGINQUERYPROC glad_glBeginQuery = NULL;\nPFNGLBEGINQUERYINDEXEDPROC glad_glBeginQueryIndexed = NULL;\nPFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL;\nPFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL;\nPFNGLBINDBUFFERPROC glad_glBindBuffer = NULL;\nPFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL;\nPFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL;\nPFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase = NULL;\nPFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange = NULL;\nPFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL;\nPFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL;\nPFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL;\nPFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture = NULL;\nPFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures = NULL;\nPFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline = NULL;\nPFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL;\nPFNGLBINDSAMPLERPROC glad_glBindSampler = NULL;\nPFNGLBINDSAMPLERSPROC glad_glBindSamplers = NULL;\nPFNGLBINDTEXTUREPROC glad_glBindTexture = NULL;\nPFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit = NULL;\nPFNGLBINDTEXTURESPROC glad_glBindTextures = NULL;\nPFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback = NULL;\nPFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL;\nPFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer = NULL;\nPFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers = NULL;\nPFNGLBITMAPPROC glad_glBitmap = NULL;\nPFNGLBLENDCOLORPROC glad_glBlendColor = NULL;\nPFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL;\nPFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL;\nPFNGLBLENDEQUATIONSEPARATEIPROC glad_glBlendEquationSeparatei = NULL;\nPFNGLBLENDEQUATIONIPROC glad_glBlendEquationi = NULL;\nPFNGLBLENDFUNCPROC glad_glBlendFunc = NULL;\nPFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL;\nPFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei = NULL;\nPFNGLBLENDFUNCIPROC glad_glBlendFunci = NULL;\nPFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL;\nPFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer = NULL;\nPFNGLBUFFERDATAPROC glad_glBufferData = NULL;\nPFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL;\nPFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL;\nPFNGLCALLLISTPROC glad_glCallList = NULL;\nPFNGLCALLLISTSPROC glad_glCallLists = NULL;\nPFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL;\nPFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus = NULL;\nPFNGLCLAMPCOLORPROC glad_glClampColor = NULL;\nPFNGLCLEARPROC glad_glClear = NULL;\nPFNGLCLEARACCUMPROC glad_glClearAccum = NULL;\nPFNGLCLEARBUFFERDATAPROC glad_glClearBufferData = NULL;\nPFNGLCLEARBUFFERSUBDATAPROC glad_glClearBufferSubData = NULL;\nPFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL;\nPFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL;\nPFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL;\nPFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL;\nPFNGLCLEARCOLORPROC glad_glClearColor = NULL;\nPFNGLCLEARDEPTHPROC glad_glClearDepth = NULL;\nPFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL;\nPFNGLCLEARINDEXPROC glad_glClearIndex = NULL;\nPFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData = NULL;\nPFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData = NULL;\nPFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi = NULL;\nPFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv = NULL;\nPFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv = NULL;\nPFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv = NULL;\nPFNGLCLEARSTENCILPROC glad_glClearStencil = NULL;\nPFNGLCLEARTEXIMAGEPROC glad_glClearTexImage = NULL;\nPFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage = NULL;\nPFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture = NULL;\nPFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL;\nPFNGLCLIPCONTROLPROC glad_glClipControl = NULL;\nPFNGLCLIPPLANEPROC glad_glClipPlane = NULL;\nPFNGLCOLOR3BPROC glad_glColor3b = NULL;\nPFNGLCOLOR3BVPROC glad_glColor3bv = NULL;\nPFNGLCOLOR3DPROC glad_glColor3d = NULL;\nPFNGLCOLOR3DVPROC glad_glColor3dv = NULL;\nPFNGLCOLOR3FPROC glad_glColor3f = NULL;\nPFNGLCOLOR3FVPROC glad_glColor3fv = NULL;\nPFNGLCOLOR3IPROC glad_glColor3i = NULL;\nPFNGLCOLOR3IVPROC glad_glColor3iv = NULL;\nPFNGLCOLOR3SPROC glad_glColor3s = NULL;\nPFNGLCOLOR3SVPROC glad_glColor3sv = NULL;\nPFNGLCOLOR3UBPROC glad_glColor3ub = NULL;\nPFNGLCOLOR3UBVPROC glad_glColor3ubv = NULL;\nPFNGLCOLOR3UIPROC glad_glColor3ui = NULL;\nPFNGLCOLOR3UIVPROC glad_glColor3uiv = NULL;\nPFNGLCOLOR3USPROC glad_glColor3us = NULL;\nPFNGLCOLOR3USVPROC glad_glColor3usv = NULL;\nPFNGLCOLOR4BPROC glad_glColor4b = NULL;\nPFNGLCOLOR4BVPROC glad_glColor4bv = NULL;\nPFNGLCOLOR4DPROC glad_glColor4d = NULL;\nPFNGLCOLOR4DVPROC glad_glColor4dv = NULL;\nPFNGLCOLOR4FPROC glad_glColor4f = NULL;\nPFNGLCOLOR4FVPROC glad_glColor4fv = NULL;\nPFNGLCOLOR4IPROC glad_glColor4i = NULL;\nPFNGLCOLOR4IVPROC glad_glColor4iv = NULL;\nPFNGLCOLOR4SPROC glad_glColor4s = NULL;\nPFNGLCOLOR4SVPROC glad_glColor4sv = NULL;\nPFNGLCOLOR4UBPROC glad_glColor4ub = NULL;\nPFNGLCOLOR4UBVPROC glad_glColor4ubv = NULL;\nPFNGLCOLOR4UIPROC glad_glColor4ui = NULL;\nPFNGLCOLOR4UIVPROC glad_glColor4uiv = NULL;\nPFNGLCOLOR4USPROC glad_glColor4us = NULL;\nPFNGLCOLOR4USVPROC glad_glColor4usv = NULL;\nPFNGLCOLORMASKPROC glad_glColorMask = NULL;\nPFNGLCOLORMASKIPROC glad_glColorMaski = NULL;\nPFNGLCOLORMATERIALPROC glad_glColorMaterial = NULL;\nPFNGLCOLORP3UIPROC glad_glColorP3ui = NULL;\nPFNGLCOLORP3UIVPROC glad_glColorP3uiv = NULL;\nPFNGLCOLORP4UIPROC glad_glColorP4ui = NULL;\nPFNGLCOLORP4UIVPROC glad_glColorP4uiv = NULL;\nPFNGLCOLORPOINTERPROC glad_glColorPointer = NULL;\nPFNGLCOMPILESHADERPROC glad_glCompileShader = NULL;\nPFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL;\nPFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL;\nPFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL;\nPFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL;\nPFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL;\nPFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL;\nPFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D = NULL;\nPFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D = NULL;\nPFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D = NULL;\nPFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL;\nPFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData = NULL;\nPFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData = NULL;\nPFNGLCOPYPIXELSPROC glad_glCopyPixels = NULL;\nPFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL;\nPFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL;\nPFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL;\nPFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL;\nPFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL;\nPFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D = NULL;\nPFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D = NULL;\nPFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D = NULL;\nPFNGLCREATEBUFFERSPROC glad_glCreateBuffers = NULL;\nPFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers = NULL;\nPFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL;\nPFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines = NULL;\nPFNGLCREATEQUERIESPROC glad_glCreateQueries = NULL;\nPFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers = NULL;\nPFNGLCREATESAMPLERSPROC glad_glCreateSamplers = NULL;\nPFNGLCREATESHADERPROC glad_glCreateShader = NULL;\nPFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv = NULL;\nPFNGLCREATETEXTURESPROC glad_glCreateTextures = NULL;\nPFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks = NULL;\nPFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays = NULL;\nPFNGLCULLFACEPROC glad_glCullFace = NULL;\nPFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL;\nPFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL;\nPFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL;\nPFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL;\nPFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL;\nPFNGLDELETELISTSPROC glad_glDeleteLists = NULL;\nPFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL;\nPFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines = NULL;\nPFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL;\nPFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL;\nPFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL;\nPFNGLDELETESHADERPROC glad_glDeleteShader = NULL;\nPFNGLDELETESYNCPROC glad_glDeleteSync = NULL;\nPFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL;\nPFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks = NULL;\nPFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL;\nPFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL;\nPFNGLDEPTHMASKPROC glad_glDepthMask = NULL;\nPFNGLDEPTHRANGEPROC glad_glDepthRange = NULL;\nPFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv = NULL;\nPFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed = NULL;\nPFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL;\nPFNGLDETACHSHADERPROC glad_glDetachShader = NULL;\nPFNGLDISABLEPROC glad_glDisable = NULL;\nPFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState = NULL;\nPFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib = NULL;\nPFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL;\nPFNGLDISABLEIPROC glad_glDisablei = NULL;\nPFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute = NULL;\nPFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect = NULL;\nPFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL;\nPFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect = NULL;\nPFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL;\nPFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance = NULL;\nPFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL;\nPFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL;\nPFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL;\nPFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL;\nPFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect = NULL;\nPFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL;\nPFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance = NULL;\nPFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL;\nPFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance = NULL;\nPFNGLDRAWPIXELSPROC glad_glDrawPixels = NULL;\nPFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL;\nPFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL;\nPFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback = NULL;\nPFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced = NULL;\nPFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream = NULL;\nPFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced = NULL;\nPFNGLEDGEFLAGPROC glad_glEdgeFlag = NULL;\nPFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer = NULL;\nPFNGLEDGEFLAGVPROC glad_glEdgeFlagv = NULL;\nPFNGLENABLEPROC glad_glEnable = NULL;\nPFNGLENABLECLIENTSTATEPROC glad_glEnableClientState = NULL;\nPFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib = NULL;\nPFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL;\nPFNGLENABLEIPROC glad_glEnablei = NULL;\nPFNGLENDPROC glad_glEnd = NULL;\nPFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL;\nPFNGLENDLISTPROC glad_glEndList = NULL;\nPFNGLENDQUERYPROC glad_glEndQuery = NULL;\nPFNGLENDQUERYINDEXEDPROC glad_glEndQueryIndexed = NULL;\nPFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL;\nPFNGLEVALCOORD1DPROC glad_glEvalCoord1d = NULL;\nPFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv = NULL;\nPFNGLEVALCOORD1FPROC glad_glEvalCoord1f = NULL;\nPFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv = NULL;\nPFNGLEVALCOORD2DPROC glad_glEvalCoord2d = NULL;\nPFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv = NULL;\nPFNGLEVALCOORD2FPROC glad_glEvalCoord2f = NULL;\nPFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv = NULL;\nPFNGLEVALMESH1PROC glad_glEvalMesh1 = NULL;\nPFNGLEVALMESH2PROC glad_glEvalMesh2 = NULL;\nPFNGLEVALPOINT1PROC glad_glEvalPoint1 = NULL;\nPFNGLEVALPOINT2PROC glad_glEvalPoint2 = NULL;\nPFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer = NULL;\nPFNGLFENCESYNCPROC glad_glFenceSync = NULL;\nPFNGLFINISHPROC glad_glFinish = NULL;\nPFNGLFLUSHPROC glad_glFlush = NULL;\nPFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL;\nPFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange = NULL;\nPFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer = NULL;\nPFNGLFOGCOORDDPROC glad_glFogCoordd = NULL;\nPFNGLFOGCOORDDVPROC glad_glFogCoorddv = NULL;\nPFNGLFOGCOORDFPROC glad_glFogCoordf = NULL;\nPFNGLFOGCOORDFVPROC glad_glFogCoordfv = NULL;\nPFNGLFOGFPROC glad_glFogf = NULL;\nPFNGLFOGFVPROC glad_glFogfv = NULL;\nPFNGLFOGIPROC glad_glFogi = NULL;\nPFNGLFOGIVPROC glad_glFogiv = NULL;\nPFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri = NULL;\nPFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL;\nPFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL;\nPFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL;\nPFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL;\nPFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL;\nPFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL;\nPFNGLFRONTFACEPROC glad_glFrontFace = NULL;\nPFNGLFRUSTUMPROC glad_glFrustum = NULL;\nPFNGLGENBUFFERSPROC glad_glGenBuffers = NULL;\nPFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL;\nPFNGLGENLISTSPROC glad_glGenLists = NULL;\nPFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines = NULL;\nPFNGLGENQUERIESPROC glad_glGenQueries = NULL;\nPFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL;\nPFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL;\nPFNGLGENTEXTURESPROC glad_glGenTextures = NULL;\nPFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks = NULL;\nPFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL;\nPFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL;\nPFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap = NULL;\nPFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv = NULL;\nPFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL;\nPFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName = NULL;\nPFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glad_glGetActiveSubroutineUniformName = NULL;\nPFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glad_glGetActiveSubroutineUniformiv = NULL;\nPFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL;\nPFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL;\nPFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL;\nPFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL;\nPFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL;\nPFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL;\nPFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL;\nPFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL;\nPFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL;\nPFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL;\nPFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL;\nPFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL;\nPFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL;\nPFNGLGETCLIPPLANEPROC glad_glGetClipPlane = NULL;\nPFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL;\nPFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage = NULL;\nPFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage = NULL;\nPFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL;\nPFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v = NULL;\nPFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL;\nPFNGLGETERRORPROC glad_glGetError = NULL;\nPFNGLGETFLOATI_VPROC glad_glGetFloati_v = NULL;\nPFNGLGETFLOATVPROC glad_glGetFloatv = NULL;\nPFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL;\nPFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL;\nPFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL;\nPFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv = NULL;\nPFNGLGETGRAPHICSRESETSTATUSPROC glad_glGetGraphicsResetStatus = NULL;\nPFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL;\nPFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL;\nPFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL;\nPFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL;\nPFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v = NULL;\nPFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ = NULL;\nPFNGLGETLIGHTFVPROC glad_glGetLightfv = NULL;\nPFNGLGETLIGHTIVPROC glad_glGetLightiv = NULL;\nPFNGLGETMAPDVPROC glad_glGetMapdv = NULL;\nPFNGLGETMAPFVPROC glad_glGetMapfv = NULL;\nPFNGLGETMAPIVPROC glad_glGetMapiv = NULL;\nPFNGLGETMATERIALFVPROC glad_glGetMaterialfv = NULL;\nPFNGLGETMATERIALIVPROC glad_glGetMaterialiv = NULL;\nPFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL;\nPFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v = NULL;\nPFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv = NULL;\nPFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv = NULL;\nPFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData = NULL;\nPFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv = NULL;\nPFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv = NULL;\nPFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv = NULL;\nPFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL;\nPFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL;\nPFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv = NULL;\nPFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv = NULL;\nPFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv = NULL;\nPFNGLGETPOINTERVPROC glad_glGetPointerv = NULL;\nPFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple = NULL;\nPFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary = NULL;\nPFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL;\nPFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv = NULL;\nPFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog = NULL;\nPFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv = NULL;\nPFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex = NULL;\nPFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation = NULL;\nPFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glad_glGetProgramResourceLocationIndex = NULL;\nPFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName = NULL;\nPFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv = NULL;\nPFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv = NULL;\nPFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;\nPFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v = NULL;\nPFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv = NULL;\nPFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v = NULL;\nPFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv = NULL;\nPFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv = NULL;\nPFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL;\nPFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL;\nPFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL;\nPFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL;\nPFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL;\nPFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL;\nPFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL;\nPFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL;\nPFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL;\nPFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL;\nPFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL;\nPFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat = NULL;\nPFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL;\nPFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL;\nPFNGLGETSTRINGPROC glad_glGetString = NULL;\nPFNGLGETSTRINGIPROC glad_glGetStringi = NULL;\nPFNGLGETSUBROUTINEINDEXPROC glad_glGetSubroutineIndex = NULL;\nPFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glad_glGetSubroutineUniformLocation = NULL;\nPFNGLGETSYNCIVPROC glad_glGetSynciv = NULL;\nPFNGLGETTEXENVFVPROC glad_glGetTexEnvfv = NULL;\nPFNGLGETTEXENVIVPROC glad_glGetTexEnviv = NULL;\nPFNGLGETTEXGENDVPROC glad_glGetTexGendv = NULL;\nPFNGLGETTEXGENFVPROC glad_glGetTexGenfv = NULL;\nPFNGLGETTEXGENIVPROC glad_glGetTexGeniv = NULL;\nPFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL;\nPFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL;\nPFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL;\nPFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL;\nPFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL;\nPFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL;\nPFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL;\nPFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage = NULL;\nPFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv = NULL;\nPFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv = NULL;\nPFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv = NULL;\nPFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv = NULL;\nPFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv = NULL;\nPFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv = NULL;\nPFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage = NULL;\nPFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL;\nPFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v = NULL;\nPFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v = NULL;\nPFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv = NULL;\nPFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL;\nPFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL;\nPFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL;\nPFNGLGETUNIFORMSUBROUTINEUIVPROC glad_glGetUniformSubroutineuiv = NULL;\nPFNGLGETUNIFORMDVPROC glad_glGetUniformdv = NULL;\nPFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL;\nPFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL;\nPFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL;\nPFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv = NULL;\nPFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv = NULL;\nPFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv = NULL;\nPFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL;\nPFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL;\nPFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv = NULL;\nPFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL;\nPFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL;\nPFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL;\nPFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL;\nPFNGLGETNCOLORTABLEPROC glad_glGetnColorTable = NULL;\nPFNGLGETNCOMPRESSEDTEXIMAGEPROC glad_glGetnCompressedTexImage = NULL;\nPFNGLGETNCONVOLUTIONFILTERPROC glad_glGetnConvolutionFilter = NULL;\nPFNGLGETNHISTOGRAMPROC glad_glGetnHistogram = NULL;\nPFNGLGETNMAPDVPROC glad_glGetnMapdv = NULL;\nPFNGLGETNMAPFVPROC glad_glGetnMapfv = NULL;\nPFNGLGETNMAPIVPROC glad_glGetnMapiv = NULL;\nPFNGLGETNMINMAXPROC glad_glGetnMinmax = NULL;\nPFNGLGETNPIXELMAPFVPROC glad_glGetnPixelMapfv = NULL;\nPFNGLGETNPIXELMAPUIVPROC glad_glGetnPixelMapuiv = NULL;\nPFNGLGETNPIXELMAPUSVPROC glad_glGetnPixelMapusv = NULL;\nPFNGLGETNPOLYGONSTIPPLEPROC glad_glGetnPolygonStipple = NULL;\nPFNGLGETNSEPARABLEFILTERPROC glad_glGetnSeparableFilter = NULL;\nPFNGLGETNTEXIMAGEPROC glad_glGetnTexImage = NULL;\nPFNGLGETNUNIFORMDVPROC glad_glGetnUniformdv = NULL;\nPFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv = NULL;\nPFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv = NULL;\nPFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv = NULL;\nPFNGLHINTPROC glad_glHint = NULL;\nPFNGLINDEXMASKPROC glad_glIndexMask = NULL;\nPFNGLINDEXPOINTERPROC glad_glIndexPointer = NULL;\nPFNGLINDEXDPROC glad_glIndexd = NULL;\nPFNGLINDEXDVPROC glad_glIndexdv = NULL;\nPFNGLINDEXFPROC glad_glIndexf = NULL;\nPFNGLINDEXFVPROC glad_glIndexfv = NULL;\nPFNGLINDEXIPROC glad_glIndexi = NULL;\nPFNGLINDEXIVPROC glad_glIndexiv = NULL;\nPFNGLINDEXSPROC glad_glIndexs = NULL;\nPFNGLINDEXSVPROC glad_glIndexsv = NULL;\nPFNGLINDEXUBPROC glad_glIndexub = NULL;\nPFNGLINDEXUBVPROC glad_glIndexubv = NULL;\nPFNGLINITNAMESPROC glad_glInitNames = NULL;\nPFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays = NULL;\nPFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData = NULL;\nPFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData = NULL;\nPFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer = NULL;\nPFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData = NULL;\nPFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData = NULL;\nPFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer = NULL;\nPFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage = NULL;\nPFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage = NULL;\nPFNGLISBUFFERPROC glad_glIsBuffer = NULL;\nPFNGLISENABLEDPROC glad_glIsEnabled = NULL;\nPFNGLISENABLEDIPROC glad_glIsEnabledi = NULL;\nPFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL;\nPFNGLISLISTPROC glad_glIsList = NULL;\nPFNGLISPROGRAMPROC glad_glIsProgram = NULL;\nPFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline = NULL;\nPFNGLISQUERYPROC glad_glIsQuery = NULL;\nPFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL;\nPFNGLISSAMPLERPROC glad_glIsSampler = NULL;\nPFNGLISSHADERPROC glad_glIsShader = NULL;\nPFNGLISSYNCPROC glad_glIsSync = NULL;\nPFNGLISTEXTUREPROC glad_glIsTexture = NULL;\nPFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback = NULL;\nPFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL;\nPFNGLLIGHTMODELFPROC glad_glLightModelf = NULL;\nPFNGLLIGHTMODELFVPROC glad_glLightModelfv = NULL;\nPFNGLLIGHTMODELIPROC glad_glLightModeli = NULL;\nPFNGLLIGHTMODELIVPROC glad_glLightModeliv = NULL;\nPFNGLLIGHTFPROC glad_glLightf = NULL;\nPFNGLLIGHTFVPROC glad_glLightfv = NULL;\nPFNGLLIGHTIPROC glad_glLighti = NULL;\nPFNGLLIGHTIVPROC glad_glLightiv = NULL;\nPFNGLLINESTIPPLEPROC glad_glLineStipple = NULL;\nPFNGLLINEWIDTHPROC glad_glLineWidth = NULL;\nPFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL;\nPFNGLLISTBASEPROC glad_glListBase = NULL;\nPFNGLLOADIDENTITYPROC glad_glLoadIdentity = NULL;\nPFNGLLOADMATRIXDPROC glad_glLoadMatrixd = NULL;\nPFNGLLOADMATRIXFPROC glad_glLoadMatrixf = NULL;\nPFNGLLOADNAMEPROC glad_glLoadName = NULL;\nPFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd = NULL;\nPFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf = NULL;\nPFNGLLOGICOPPROC glad_glLogicOp = NULL;\nPFNGLMAP1DPROC glad_glMap1d = NULL;\nPFNGLMAP1FPROC glad_glMap1f = NULL;\nPFNGLMAP2DPROC glad_glMap2d = NULL;\nPFNGLMAP2FPROC glad_glMap2f = NULL;\nPFNGLMAPBUFFERPROC glad_glMapBuffer = NULL;\nPFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL;\nPFNGLMAPGRID1DPROC glad_glMapGrid1d = NULL;\nPFNGLMAPGRID1FPROC glad_glMapGrid1f = NULL;\nPFNGLMAPGRID2DPROC glad_glMapGrid2d = NULL;\nPFNGLMAPGRID2FPROC glad_glMapGrid2f = NULL;\nPFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer = NULL;\nPFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange = NULL;\nPFNGLMATERIALFPROC glad_glMaterialf = NULL;\nPFNGLMATERIALFVPROC glad_glMaterialfv = NULL;\nPFNGLMATERIALIPROC glad_glMateriali = NULL;\nPFNGLMATERIALIVPROC glad_glMaterialiv = NULL;\nPFNGLMATRIXMODEPROC glad_glMatrixMode = NULL;\nPFNGLMEMORYBARRIERPROC glad_glMemoryBarrier = NULL;\nPFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion = NULL;\nPFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading = NULL;\nPFNGLMULTMATRIXDPROC glad_glMultMatrixd = NULL;\nPFNGLMULTMATRIXFPROC glad_glMultMatrixf = NULL;\nPFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd = NULL;\nPFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf = NULL;\nPFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL;\nPFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect = NULL;\nPFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glad_glMultiDrawArraysIndirectCount = NULL;\nPFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL;\nPFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL;\nPFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect = NULL;\nPFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glad_glMultiDrawElementsIndirectCount = NULL;\nPFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d = NULL;\nPFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv = NULL;\nPFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f = NULL;\nPFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv = NULL;\nPFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i = NULL;\nPFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv = NULL;\nPFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s = NULL;\nPFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv = NULL;\nPFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d = NULL;\nPFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv = NULL;\nPFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f = NULL;\nPFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv = NULL;\nPFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i = NULL;\nPFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv = NULL;\nPFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s = NULL;\nPFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv = NULL;\nPFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d = NULL;\nPFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv = NULL;\nPFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f = NULL;\nPFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv = NULL;\nPFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i = NULL;\nPFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv = NULL;\nPFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s = NULL;\nPFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv = NULL;\nPFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d = NULL;\nPFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv = NULL;\nPFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f = NULL;\nPFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv = NULL;\nPFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i = NULL;\nPFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv = NULL;\nPFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s = NULL;\nPFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv = NULL;\nPFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL;\nPFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL;\nPFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL;\nPFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv = NULL;\nPFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL;\nPFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL;\nPFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL;\nPFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL;\nPFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData = NULL;\nPFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage = NULL;\nPFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData = NULL;\nPFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer = NULL;\nPFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers = NULL;\nPFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri = NULL;\nPFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer = NULL;\nPFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer = NULL;\nPFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture = NULL;\nPFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer = NULL;\nPFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage = NULL;\nPFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample = NULL;\nPFNGLNEWLISTPROC glad_glNewList = NULL;\nPFNGLNORMAL3BPROC glad_glNormal3b = NULL;\nPFNGLNORMAL3BVPROC glad_glNormal3bv = NULL;\nPFNGLNORMAL3DPROC glad_glNormal3d = NULL;\nPFNGLNORMAL3DVPROC glad_glNormal3dv = NULL;\nPFNGLNORMAL3FPROC glad_glNormal3f = NULL;\nPFNGLNORMAL3FVPROC glad_glNormal3fv = NULL;\nPFNGLNORMAL3IPROC glad_glNormal3i = NULL;\nPFNGLNORMAL3IVPROC glad_glNormal3iv = NULL;\nPFNGLNORMAL3SPROC glad_glNormal3s = NULL;\nPFNGLNORMAL3SVPROC glad_glNormal3sv = NULL;\nPFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL;\nPFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL;\nPFNGLNORMALPOINTERPROC glad_glNormalPointer = NULL;\nPFNGLOBJECTLABELPROC glad_glObjectLabel = NULL;\nPFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL;\nPFNGLORTHOPROC glad_glOrtho = NULL;\nPFNGLPASSTHROUGHPROC glad_glPassThrough = NULL;\nPFNGLPATCHPARAMETERFVPROC glad_glPatchParameterfv = NULL;\nPFNGLPATCHPARAMETERIPROC glad_glPatchParameteri = NULL;\nPFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback = NULL;\nPFNGLPIXELMAPFVPROC glad_glPixelMapfv = NULL;\nPFNGLPIXELMAPUIVPROC glad_glPixelMapuiv = NULL;\nPFNGLPIXELMAPUSVPROC glad_glPixelMapusv = NULL;\nPFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL;\nPFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL;\nPFNGLPIXELTRANSFERFPROC glad_glPixelTransferf = NULL;\nPFNGLPIXELTRANSFERIPROC glad_glPixelTransferi = NULL;\nPFNGLPIXELZOOMPROC glad_glPixelZoom = NULL;\nPFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL;\nPFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL;\nPFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL;\nPFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL;\nPFNGLPOINTSIZEPROC glad_glPointSize = NULL;\nPFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL;\nPFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL;\nPFNGLPOLYGONOFFSETCLAMPPROC glad_glPolygonOffsetClamp = NULL;\nPFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple = NULL;\nPFNGLPOPATTRIBPROC glad_glPopAttrib = NULL;\nPFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib = NULL;\nPFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL;\nPFNGLPOPMATRIXPROC glad_glPopMatrix = NULL;\nPFNGLPOPNAMEPROC glad_glPopName = NULL;\nPFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL;\nPFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures = NULL;\nPFNGLPROGRAMBINARYPROC glad_glProgramBinary = NULL;\nPFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri = NULL;\nPFNGLPROGRAMUNIFORM1DPROC glad_glProgramUniform1d = NULL;\nPFNGLPROGRAMUNIFORM1DVPROC glad_glProgramUniform1dv = NULL;\nPFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f = NULL;\nPFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv = NULL;\nPFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i = NULL;\nPFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv = NULL;\nPFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui = NULL;\nPFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv = NULL;\nPFNGLPROGRAMUNIFORM2DPROC glad_glProgramUniform2d = NULL;\nPFNGLPROGRAMUNIFORM2DVPROC glad_glProgramUniform2dv = NULL;\nPFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f = NULL;\nPFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv = NULL;\nPFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i = NULL;\nPFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv = NULL;\nPFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui = NULL;\nPFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv = NULL;\nPFNGLPROGRAMUNIFORM3DPROC glad_glProgramUniform3d = NULL;\nPFNGLPROGRAMUNIFORM3DVPROC glad_glProgramUniform3dv = NULL;\nPFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f = NULL;\nPFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv = NULL;\nPFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i = NULL;\nPFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv = NULL;\nPFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui = NULL;\nPFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv = NULL;\nPFNGLPROGRAMUNIFORM4DPROC glad_glProgramUniform4d = NULL;\nPFNGLPROGRAMUNIFORM4DVPROC glad_glProgramUniform4dv = NULL;\nPFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f = NULL;\nPFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv = NULL;\nPFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i = NULL;\nPFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv = NULL;\nPFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui = NULL;\nPFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX2DVPROC glad_glProgramUniformMatrix2dv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glad_glProgramUniformMatrix2x3dv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glad_glProgramUniformMatrix2x4dv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX3DVPROC glad_glProgramUniformMatrix3dv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glad_glProgramUniformMatrix3x2dv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glad_glProgramUniformMatrix3x4dv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX4DVPROC glad_glProgramUniformMatrix4dv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glad_glProgramUniformMatrix4x2dv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glad_glProgramUniformMatrix4x3dv = NULL;\nPFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv = NULL;\nPFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL;\nPFNGLPUSHATTRIBPROC glad_glPushAttrib = NULL;\nPFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib = NULL;\nPFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL;\nPFNGLPUSHMATRIXPROC glad_glPushMatrix = NULL;\nPFNGLPUSHNAMEPROC glad_glPushName = NULL;\nPFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL;\nPFNGLRASTERPOS2DPROC glad_glRasterPos2d = NULL;\nPFNGLRASTERPOS2DVPROC glad_glRasterPos2dv = NULL;\nPFNGLRASTERPOS2FPROC glad_glRasterPos2f = NULL;\nPFNGLRASTERPOS2FVPROC glad_glRasterPos2fv = NULL;\nPFNGLRASTERPOS2IPROC glad_glRasterPos2i = NULL;\nPFNGLRASTERPOS2IVPROC glad_glRasterPos2iv = NULL;\nPFNGLRASTERPOS2SPROC glad_glRasterPos2s = NULL;\nPFNGLRASTERPOS2SVPROC glad_glRasterPos2sv = NULL;\nPFNGLRASTERPOS3DPROC glad_glRasterPos3d = NULL;\nPFNGLRASTERPOS3DVPROC glad_glRasterPos3dv = NULL;\nPFNGLRASTERPOS3FPROC glad_glRasterPos3f = NULL;\nPFNGLRASTERPOS3FVPROC glad_glRasterPos3fv = NULL;\nPFNGLRASTERPOS3IPROC glad_glRasterPos3i = NULL;\nPFNGLRASTERPOS3IVPROC glad_glRasterPos3iv = NULL;\nPFNGLRASTERPOS3SPROC glad_glRasterPos3s = NULL;\nPFNGLRASTERPOS3SVPROC glad_glRasterPos3sv = NULL;\nPFNGLRASTERPOS4DPROC glad_glRasterPos4d = NULL;\nPFNGLRASTERPOS4DVPROC glad_glRasterPos4dv = NULL;\nPFNGLRASTERPOS4FPROC glad_glRasterPos4f = NULL;\nPFNGLRASTERPOS4FVPROC glad_glRasterPos4fv = NULL;\nPFNGLRASTERPOS4IPROC glad_glRasterPos4i = NULL;\nPFNGLRASTERPOS4IVPROC glad_glRasterPos4iv = NULL;\nPFNGLRASTERPOS4SPROC glad_glRasterPos4s = NULL;\nPFNGLRASTERPOS4SVPROC glad_glRasterPos4sv = NULL;\nPFNGLREADBUFFERPROC glad_glReadBuffer = NULL;\nPFNGLREADPIXELSPROC glad_glReadPixels = NULL;\nPFNGLREADNPIXELSPROC glad_glReadnPixels = NULL;\nPFNGLRECTDPROC glad_glRectd = NULL;\nPFNGLRECTDVPROC glad_glRectdv = NULL;\nPFNGLRECTFPROC glad_glRectf = NULL;\nPFNGLRECTFVPROC glad_glRectfv = NULL;\nPFNGLRECTIPROC glad_glRecti = NULL;\nPFNGLRECTIVPROC glad_glRectiv = NULL;\nPFNGLRECTSPROC glad_glRects = NULL;\nPFNGLRECTSVPROC glad_glRectsv = NULL;\nPFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler = NULL;\nPFNGLRENDERMODEPROC glad_glRenderMode = NULL;\nPFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL;\nPFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL;\nPFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback = NULL;\nPFNGLROTATEDPROC glad_glRotated = NULL;\nPFNGLROTATEFPROC glad_glRotatef = NULL;\nPFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL;\nPFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL;\nPFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL;\nPFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL;\nPFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL;\nPFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL;\nPFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL;\nPFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL;\nPFNGLSCALEDPROC glad_glScaled = NULL;\nPFNGLSCALEFPROC glad_glScalef = NULL;\nPFNGLSCISSORPROC glad_glScissor = NULL;\nPFNGLSCISSORARRAYVPROC glad_glScissorArrayv = NULL;\nPFNGLSCISSORINDEXEDPROC glad_glScissorIndexed = NULL;\nPFNGLSCISSORINDEXEDVPROC glad_glScissorIndexedv = NULL;\nPFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b = NULL;\nPFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv = NULL;\nPFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d = NULL;\nPFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv = NULL;\nPFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f = NULL;\nPFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv = NULL;\nPFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i = NULL;\nPFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv = NULL;\nPFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s = NULL;\nPFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv = NULL;\nPFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub = NULL;\nPFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv = NULL;\nPFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui = NULL;\nPFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv = NULL;\nPFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us = NULL;\nPFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv = NULL;\nPFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui = NULL;\nPFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL;\nPFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer = NULL;\nPFNGLSELECTBUFFERPROC glad_glSelectBuffer = NULL;\nPFNGLSHADEMODELPROC glad_glShadeModel = NULL;\nPFNGLSHADERBINARYPROC glad_glShaderBinary = NULL;\nPFNGLSHADERSOURCEPROC glad_glShaderSource = NULL;\nPFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding = NULL;\nPFNGLSPECIALIZESHADERPROC glad_glSpecializeShader = NULL;\nPFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL;\nPFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL;\nPFNGLSTENCILMASKPROC glad_glStencilMask = NULL;\nPFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL;\nPFNGLSTENCILOPPROC glad_glStencilOp = NULL;\nPFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL;\nPFNGLTEXBUFFERPROC glad_glTexBuffer = NULL;\nPFNGLTEXBUFFERRANGEPROC glad_glTexBufferRange = NULL;\nPFNGLTEXCOORD1DPROC glad_glTexCoord1d = NULL;\nPFNGLTEXCOORD1DVPROC glad_glTexCoord1dv = NULL;\nPFNGLTEXCOORD1FPROC glad_glTexCoord1f = NULL;\nPFNGLTEXCOORD1FVPROC glad_glTexCoord1fv = NULL;\nPFNGLTEXCOORD1IPROC glad_glTexCoord1i = NULL;\nPFNGLTEXCOORD1IVPROC glad_glTexCoord1iv = NULL;\nPFNGLTEXCOORD1SPROC glad_glTexCoord1s = NULL;\nPFNGLTEXCOORD1SVPROC glad_glTexCoord1sv = NULL;\nPFNGLTEXCOORD2DPROC glad_glTexCoord2d = NULL;\nPFNGLTEXCOORD2DVPROC glad_glTexCoord2dv = NULL;\nPFNGLTEXCOORD2FPROC glad_glTexCoord2f = NULL;\nPFNGLTEXCOORD2FVPROC glad_glTexCoord2fv = NULL;\nPFNGLTEXCOORD2IPROC glad_glTexCoord2i = NULL;\nPFNGLTEXCOORD2IVPROC glad_glTexCoord2iv = NULL;\nPFNGLTEXCOORD2SPROC glad_glTexCoord2s = NULL;\nPFNGLTEXCOORD2SVPROC glad_glTexCoord2sv = NULL;\nPFNGLTEXCOORD3DPROC glad_glTexCoord3d = NULL;\nPFNGLTEXCOORD3DVPROC glad_glTexCoord3dv = NULL;\nPFNGLTEXCOORD3FPROC glad_glTexCoord3f = NULL;\nPFNGLTEXCOORD3FVPROC glad_glTexCoord3fv = NULL;\nPFNGLTEXCOORD3IPROC glad_glTexCoord3i = NULL;\nPFNGLTEXCOORD3IVPROC glad_glTexCoord3iv = NULL;\nPFNGLTEXCOORD3SPROC glad_glTexCoord3s = NULL;\nPFNGLTEXCOORD3SVPROC glad_glTexCoord3sv = NULL;\nPFNGLTEXCOORD4DPROC glad_glTexCoord4d = NULL;\nPFNGLTEXCOORD4DVPROC glad_glTexCoord4dv = NULL;\nPFNGLTEXCOORD4FPROC glad_glTexCoord4f = NULL;\nPFNGLTEXCOORD4FVPROC glad_glTexCoord4fv = NULL;\nPFNGLTEXCOORD4IPROC glad_glTexCoord4i = NULL;\nPFNGLTEXCOORD4IVPROC glad_glTexCoord4iv = NULL;\nPFNGLTEXCOORD4SPROC glad_glTexCoord4s = NULL;\nPFNGLTEXCOORD4SVPROC glad_glTexCoord4sv = NULL;\nPFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui = NULL;\nPFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv = NULL;\nPFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui = NULL;\nPFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv = NULL;\nPFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui = NULL;\nPFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv = NULL;\nPFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui = NULL;\nPFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv = NULL;\nPFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer = NULL;\nPFNGLTEXENVFPROC glad_glTexEnvf = NULL;\nPFNGLTEXENVFVPROC glad_glTexEnvfv = NULL;\nPFNGLTEXENVIPROC glad_glTexEnvi = NULL;\nPFNGLTEXENVIVPROC glad_glTexEnviv = NULL;\nPFNGLTEXGENDPROC glad_glTexGend = NULL;\nPFNGLTEXGENDVPROC glad_glTexGendv = NULL;\nPFNGLTEXGENFPROC glad_glTexGenf = NULL;\nPFNGLTEXGENFVPROC glad_glTexGenfv = NULL;\nPFNGLTEXGENIPROC glad_glTexGeni = NULL;\nPFNGLTEXGENIVPROC glad_glTexGeniv = NULL;\nPFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL;\nPFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL;\nPFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL;\nPFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL;\nPFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL;\nPFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL;\nPFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL;\nPFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL;\nPFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL;\nPFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL;\nPFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL;\nPFNGLTEXSTORAGE1DPROC glad_glTexStorage1D = NULL;\nPFNGLTEXSTORAGE2DPROC glad_glTexStorage2D = NULL;\nPFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample = NULL;\nPFNGLTEXSTORAGE3DPROC glad_glTexStorage3D = NULL;\nPFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample = NULL;\nPFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL;\nPFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL;\nPFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL;\nPFNGLTEXTUREBARRIERPROC glad_glTextureBarrier = NULL;\nPFNGLTEXTUREBUFFERPROC glad_glTextureBuffer = NULL;\nPFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange = NULL;\nPFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv = NULL;\nPFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv = NULL;\nPFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf = NULL;\nPFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv = NULL;\nPFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri = NULL;\nPFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv = NULL;\nPFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D = NULL;\nPFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D = NULL;\nPFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample = NULL;\nPFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D = NULL;\nPFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample = NULL;\nPFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D = NULL;\nPFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D = NULL;\nPFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D = NULL;\nPFNGLTEXTUREVIEWPROC glad_glTextureView = NULL;\nPFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase = NULL;\nPFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange = NULL;\nPFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL;\nPFNGLTRANSLATEDPROC glad_glTranslated = NULL;\nPFNGLTRANSLATEFPROC glad_glTranslatef = NULL;\nPFNGLUNIFORM1DPROC glad_glUniform1d = NULL;\nPFNGLUNIFORM1DVPROC glad_glUniform1dv = NULL;\nPFNGLUNIFORM1FPROC glad_glUniform1f = NULL;\nPFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL;\nPFNGLUNIFORM1IPROC glad_glUniform1i = NULL;\nPFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL;\nPFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL;\nPFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL;\nPFNGLUNIFORM2DPROC glad_glUniform2d = NULL;\nPFNGLUNIFORM2DVPROC glad_glUniform2dv = NULL;\nPFNGLUNIFORM2FPROC glad_glUniform2f = NULL;\nPFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL;\nPFNGLUNIFORM2IPROC glad_glUniform2i = NULL;\nPFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL;\nPFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL;\nPFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL;\nPFNGLUNIFORM3DPROC glad_glUniform3d = NULL;\nPFNGLUNIFORM3DVPROC glad_glUniform3dv = NULL;\nPFNGLUNIFORM3FPROC glad_glUniform3f = NULL;\nPFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL;\nPFNGLUNIFORM3IPROC glad_glUniform3i = NULL;\nPFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL;\nPFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL;\nPFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL;\nPFNGLUNIFORM4DPROC glad_glUniform4d = NULL;\nPFNGLUNIFORM4DVPROC glad_glUniform4dv = NULL;\nPFNGLUNIFORM4FPROC glad_glUniform4f = NULL;\nPFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL;\nPFNGLUNIFORM4IPROC glad_glUniform4i = NULL;\nPFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL;\nPFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL;\nPFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL;\nPFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL;\nPFNGLUNIFORMMATRIX2DVPROC glad_glUniformMatrix2dv = NULL;\nPFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL;\nPFNGLUNIFORMMATRIX2X3DVPROC glad_glUniformMatrix2x3dv = NULL;\nPFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL;\nPFNGLUNIFORMMATRIX2X4DVPROC glad_glUniformMatrix2x4dv = NULL;\nPFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL;\nPFNGLUNIFORMMATRIX3DVPROC glad_glUniformMatrix3dv = NULL;\nPFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL;\nPFNGLUNIFORMMATRIX3X2DVPROC glad_glUniformMatrix3x2dv = NULL;\nPFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL;\nPFNGLUNIFORMMATRIX3X4DVPROC glad_glUniformMatrix3x4dv = NULL;\nPFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL;\nPFNGLUNIFORMMATRIX4DVPROC glad_glUniformMatrix4dv = NULL;\nPFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL;\nPFNGLUNIFORMMATRIX4X2DVPROC glad_glUniformMatrix4x2dv = NULL;\nPFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL;\nPFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv = NULL;\nPFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL;\nPFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv = NULL;\nPFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL;\nPFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer = NULL;\nPFNGLUSEPROGRAMPROC glad_glUseProgram = NULL;\nPFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages = NULL;\nPFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL;\nPFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline = NULL;\nPFNGLVERTEX2DPROC glad_glVertex2d = NULL;\nPFNGLVERTEX2DVPROC glad_glVertex2dv = NULL;\nPFNGLVERTEX2FPROC glad_glVertex2f = NULL;\nPFNGLVERTEX2FVPROC glad_glVertex2fv = NULL;\nPFNGLVERTEX2IPROC glad_glVertex2i = NULL;\nPFNGLVERTEX2IVPROC glad_glVertex2iv = NULL;\nPFNGLVERTEX2SPROC glad_glVertex2s = NULL;\nPFNGLVERTEX2SVPROC glad_glVertex2sv = NULL;\nPFNGLVERTEX3DPROC glad_glVertex3d = NULL;\nPFNGLVERTEX3DVPROC glad_glVertex3dv = NULL;\nPFNGLVERTEX3FPROC glad_glVertex3f = NULL;\nPFNGLVERTEX3FVPROC glad_glVertex3fv = NULL;\nPFNGLVERTEX3IPROC glad_glVertex3i = NULL;\nPFNGLVERTEX3IVPROC glad_glVertex3iv = NULL;\nPFNGLVERTEX3SPROC glad_glVertex3s = NULL;\nPFNGLVERTEX3SVPROC glad_glVertex3sv = NULL;\nPFNGLVERTEX4DPROC glad_glVertex4d = NULL;\nPFNGLVERTEX4DVPROC glad_glVertex4dv = NULL;\nPFNGLVERTEX4FPROC glad_glVertex4f = NULL;\nPFNGLVERTEX4FVPROC glad_glVertex4fv = NULL;\nPFNGLVERTEX4IPROC glad_glVertex4i = NULL;\nPFNGLVERTEX4IVPROC glad_glVertex4iv = NULL;\nPFNGLVERTEX4SPROC glad_glVertex4s = NULL;\nPFNGLVERTEX4SVPROC glad_glVertex4sv = NULL;\nPFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding = NULL;\nPFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat = NULL;\nPFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat = NULL;\nPFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat = NULL;\nPFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor = NULL;\nPFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer = NULL;\nPFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer = NULL;\nPFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers = NULL;\nPFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL;\nPFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL;\nPFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL;\nPFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL;\nPFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL;\nPFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL;\nPFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL;\nPFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL;\nPFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL;\nPFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL;\nPFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL;\nPFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL;\nPFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL;\nPFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL;\nPFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL;\nPFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL;\nPFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL;\nPFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL;\nPFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL;\nPFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL;\nPFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL;\nPFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL;\nPFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL;\nPFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL;\nPFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL;\nPFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL;\nPFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL;\nPFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL;\nPFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL;\nPFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL;\nPFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL;\nPFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL;\nPFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL;\nPFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL;\nPFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL;\nPFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL;\nPFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding = NULL;\nPFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL;\nPFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat = NULL;\nPFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL;\nPFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL;\nPFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL;\nPFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL;\nPFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL;\nPFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL;\nPFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL;\nPFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL;\nPFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL;\nPFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL;\nPFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL;\nPFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL;\nPFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL;\nPFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL;\nPFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL;\nPFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL;\nPFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL;\nPFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL;\nPFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL;\nPFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL;\nPFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat = NULL;\nPFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL;\nPFNGLVERTEXATTRIBL1DPROC glad_glVertexAttribL1d = NULL;\nPFNGLVERTEXATTRIBL1DVPROC glad_glVertexAttribL1dv = NULL;\nPFNGLVERTEXATTRIBL2DPROC glad_glVertexAttribL2d = NULL;\nPFNGLVERTEXATTRIBL2DVPROC glad_glVertexAttribL2dv = NULL;\nPFNGLVERTEXATTRIBL3DPROC glad_glVertexAttribL3d = NULL;\nPFNGLVERTEXATTRIBL3DVPROC glad_glVertexAttribL3dv = NULL;\nPFNGLVERTEXATTRIBL4DPROC glad_glVertexAttribL4d = NULL;\nPFNGLVERTEXATTRIBL4DVPROC glad_glVertexAttribL4dv = NULL;\nPFNGLVERTEXATTRIBLFORMATPROC glad_glVertexAttribLFormat = NULL;\nPFNGLVERTEXATTRIBLPOINTERPROC glad_glVertexAttribLPointer = NULL;\nPFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL;\nPFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL;\nPFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL;\nPFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL;\nPFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL;\nPFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL;\nPFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL;\nPFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL;\nPFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL;\nPFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor = NULL;\nPFNGLVERTEXP2UIPROC glad_glVertexP2ui = NULL;\nPFNGLVERTEXP2UIVPROC glad_glVertexP2uiv = NULL;\nPFNGLVERTEXP3UIPROC glad_glVertexP3ui = NULL;\nPFNGLVERTEXP3UIVPROC glad_glVertexP3uiv = NULL;\nPFNGLVERTEXP4UIPROC glad_glVertexP4ui = NULL;\nPFNGLVERTEXP4UIVPROC glad_glVertexP4uiv = NULL;\nPFNGLVERTEXPOINTERPROC glad_glVertexPointer = NULL;\nPFNGLVIEWPORTPROC glad_glViewport = NULL;\nPFNGLVIEWPORTARRAYVPROC glad_glViewportArrayv = NULL;\nPFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf = NULL;\nPFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv = NULL;\nPFNGLWAITSYNCPROC glad_glWaitSync = NULL;\nPFNGLWINDOWPOS2DPROC glad_glWindowPos2d = NULL;\nPFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv = NULL;\nPFNGLWINDOWPOS2FPROC glad_glWindowPos2f = NULL;\nPFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv = NULL;\nPFNGLWINDOWPOS2IPROC glad_glWindowPos2i = NULL;\nPFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv = NULL;\nPFNGLWINDOWPOS2SPROC glad_glWindowPos2s = NULL;\nPFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv = NULL;\nPFNGLWINDOWPOS3DPROC glad_glWindowPos3d = NULL;\nPFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv = NULL;\nPFNGLWINDOWPOS3FPROC glad_glWindowPos3f = NULL;\nPFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv = NULL;\nPFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL;\nPFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL;\nPFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL;\nPFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL;\nstatic void load_GL_VERSION_1_0(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_0) return;\n\tglad_glCullFace = (PFNGLCULLFACEPROC)load(\"glCullFace\");\n\tglad_glFrontFace = (PFNGLFRONTFACEPROC)load(\"glFrontFace\");\n\tglad_glHint = (PFNGLHINTPROC)load(\"glHint\");\n\tglad_glLineWidth = (PFNGLLINEWIDTHPROC)load(\"glLineWidth\");\n\tglad_glPointSize = (PFNGLPOINTSIZEPROC)load(\"glPointSize\");\n\tglad_glPolygonMode = (PFNGLPOLYGONMODEPROC)load(\"glPolygonMode\");\n\tglad_glScissor = (PFNGLSCISSORPROC)load(\"glScissor\");\n\tglad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load(\"glTexParameterf\");\n\tglad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load(\"glTexParameterfv\");\n\tglad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load(\"glTexParameteri\");\n\tglad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load(\"glTexParameteriv\");\n\tglad_glTexImage1D = (PFNGLTEXIMAGE1DPROC)load(\"glTexImage1D\");\n\tglad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load(\"glTexImage2D\");\n\tglad_glDrawBuffer = (PFNGLDRAWBUFFERPROC)load(\"glDrawBuffer\");\n\tglad_glClear = (PFNGLCLEARPROC)load(\"glClear\");\n\tglad_glClearColor = (PFNGLCLEARCOLORPROC)load(\"glClearColor\");\n\tglad_glClearStencil = (PFNGLCLEARSTENCILPROC)load(\"glClearStencil\");\n\tglad_glClearDepth = (PFNGLCLEARDEPTHPROC)load(\"glClearDepth\");\n\tglad_glStencilMask = (PFNGLSTENCILMASKPROC)load(\"glStencilMask\");\n\tglad_glColorMask = (PFNGLCOLORMASKPROC)load(\"glColorMask\");\n\tglad_glDepthMask = (PFNGLDEPTHMASKPROC)load(\"glDepthMask\");\n\tglad_glDisable = (PFNGLDISABLEPROC)load(\"glDisable\");\n\tglad_glEnable = (PFNGLENABLEPROC)load(\"glEnable\");\n\tglad_glFinish = (PFNGLFINISHPROC)load(\"glFinish\");\n\tglad_glFlush = (PFNGLFLUSHPROC)load(\"glFlush\");\n\tglad_glBlendFunc = (PFNGLBLENDFUNCPROC)load(\"glBlendFunc\");\n\tglad_glLogicOp = (PFNGLLOGICOPPROC)load(\"glLogicOp\");\n\tglad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load(\"glStencilFunc\");\n\tglad_glStencilOp = (PFNGLSTENCILOPPROC)load(\"glStencilOp\");\n\tglad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load(\"glDepthFunc\");\n\tglad_glPixelStoref = (PFNGLPIXELSTOREFPROC)load(\"glPixelStoref\");\n\tglad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load(\"glPixelStorei\");\n\tglad_glReadBuffer = (PFNGLREADBUFFERPROC)load(\"glReadBuffer\");\n\tglad_glReadPixels = (PFNGLREADPIXELSPROC)load(\"glReadPixels\");\n\tglad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load(\"glGetBooleanv\");\n\tglad_glGetDoublev = (PFNGLGETDOUBLEVPROC)load(\"glGetDoublev\");\n\tglad_glGetError = (PFNGLGETERRORPROC)load(\"glGetError\");\n\tglad_glGetFloatv = (PFNGLGETFLOATVPROC)load(\"glGetFloatv\");\n\tglad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load(\"glGetIntegerv\");\n\tglad_glGetString = (PFNGLGETSTRINGPROC)load(\"glGetString\");\n\tglad_glGetTexImage = (PFNGLGETTEXIMAGEPROC)load(\"glGetTexImage\");\n\tglad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load(\"glGetTexParameterfv\");\n\tglad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load(\"glGetTexParameteriv\");\n\tglad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC)load(\"glGetTexLevelParameterfv\");\n\tglad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC)load(\"glGetTexLevelParameteriv\");\n\tglad_glIsEnabled = (PFNGLISENABLEDPROC)load(\"glIsEnabled\");\n\tglad_glDepthRange = (PFNGLDEPTHRANGEPROC)load(\"glDepthRange\");\n\tglad_glViewport = (PFNGLVIEWPORTPROC)load(\"glViewport\");\n\tglad_glNewList = (PFNGLNEWLISTPROC)load(\"glNewList\");\n\tglad_glEndList = (PFNGLENDLISTPROC)load(\"glEndList\");\n\tglad_glCallList = (PFNGLCALLLISTPROC)load(\"glCallList\");\n\tglad_glCallLists = (PFNGLCALLLISTSPROC)load(\"glCallLists\");\n\tglad_glDeleteLists = (PFNGLDELETELISTSPROC)load(\"glDeleteLists\");\n\tglad_glGenLists = (PFNGLGENLISTSPROC)load(\"glGenLists\");\n\tglad_glListBase = (PFNGLLISTBASEPROC)load(\"glListBase\");\n\tglad_glBegin = (PFNGLBEGINPROC)load(\"glBegin\");\n\tglad_glBitmap = (PFNGLBITMAPPROC)load(\"glBitmap\");\n\tglad_glColor3b = (PFNGLCOLOR3BPROC)load(\"glColor3b\");\n\tglad_glColor3bv = (PFNGLCOLOR3BVPROC)load(\"glColor3bv\");\n\tglad_glColor3d = (PFNGLCOLOR3DPROC)load(\"glColor3d\");\n\tglad_glColor3dv = (PFNGLCOLOR3DVPROC)load(\"glColor3dv\");\n\tglad_glColor3f = (PFNGLCOLOR3FPROC)load(\"glColor3f\");\n\tglad_glColor3fv = (PFNGLCOLOR3FVPROC)load(\"glColor3fv\");\n\tglad_glColor3i = (PFNGLCOLOR3IPROC)load(\"glColor3i\");\n\tglad_glColor3iv = (PFNGLCOLOR3IVPROC)load(\"glColor3iv\");\n\tglad_glColor3s = (PFNGLCOLOR3SPROC)load(\"glColor3s\");\n\tglad_glColor3sv = (PFNGLCOLOR3SVPROC)load(\"glColor3sv\");\n\tglad_glColor3ub = (PFNGLCOLOR3UBPROC)load(\"glColor3ub\");\n\tglad_glColor3ubv = (PFNGLCOLOR3UBVPROC)load(\"glColor3ubv\");\n\tglad_glColor3ui = (PFNGLCOLOR3UIPROC)load(\"glColor3ui\");\n\tglad_glColor3uiv = (PFNGLCOLOR3UIVPROC)load(\"glColor3uiv\");\n\tglad_glColor3us = (PFNGLCOLOR3USPROC)load(\"glColor3us\");\n\tglad_glColor3usv = (PFNGLCOLOR3USVPROC)load(\"glColor3usv\");\n\tglad_glColor4b = (PFNGLCOLOR4BPROC)load(\"glColor4b\");\n\tglad_glColor4bv = (PFNGLCOLOR4BVPROC)load(\"glColor4bv\");\n\tglad_glColor4d = (PFNGLCOLOR4DPROC)load(\"glColor4d\");\n\tglad_glColor4dv = (PFNGLCOLOR4DVPROC)load(\"glColor4dv\");\n\tglad_glColor4f = (PFNGLCOLOR4FPROC)load(\"glColor4f\");\n\tglad_glColor4fv = (PFNGLCOLOR4FVPROC)load(\"glColor4fv\");\n\tglad_glColor4i = (PFNGLCOLOR4IPROC)load(\"glColor4i\");\n\tglad_glColor4iv = (PFNGLCOLOR4IVPROC)load(\"glColor4iv\");\n\tglad_glColor4s = (PFNGLCOLOR4SPROC)load(\"glColor4s\");\n\tglad_glColor4sv = (PFNGLCOLOR4SVPROC)load(\"glColor4sv\");\n\tglad_glColor4ub = (PFNGLCOLOR4UBPROC)load(\"glColor4ub\");\n\tglad_glColor4ubv = (PFNGLCOLOR4UBVPROC)load(\"glColor4ubv\");\n\tglad_glColor4ui = (PFNGLCOLOR4UIPROC)load(\"glColor4ui\");\n\tglad_glColor4uiv = (PFNGLCOLOR4UIVPROC)load(\"glColor4uiv\");\n\tglad_glColor4us = (PFNGLCOLOR4USPROC)load(\"glColor4us\");\n\tglad_glColor4usv = (PFNGLCOLOR4USVPROC)load(\"glColor4usv\");\n\tglad_glEdgeFlag = (PFNGLEDGEFLAGPROC)load(\"glEdgeFlag\");\n\tglad_glEdgeFlagv = (PFNGLEDGEFLAGVPROC)load(\"glEdgeFlagv\");\n\tglad_glEnd = (PFNGLENDPROC)load(\"glEnd\");\n\tglad_glIndexd = (PFNGLINDEXDPROC)load(\"glIndexd\");\n\tglad_glIndexdv = (PFNGLINDEXDVPROC)load(\"glIndexdv\");\n\tglad_glIndexf = (PFNGLINDEXFPROC)load(\"glIndexf\");\n\tglad_glIndexfv = (PFNGLINDEXFVPROC)load(\"glIndexfv\");\n\tglad_glIndexi = (PFNGLINDEXIPROC)load(\"glIndexi\");\n\tglad_glIndexiv = (PFNGLINDEXIVPROC)load(\"glIndexiv\");\n\tglad_glIndexs = (PFNGLINDEXSPROC)load(\"glIndexs\");\n\tglad_glIndexsv = (PFNGLINDEXSVPROC)load(\"glIndexsv\");\n\tglad_glNormal3b = (PFNGLNORMAL3BPROC)load(\"glNormal3b\");\n\tglad_glNormal3bv = (PFNGLNORMAL3BVPROC)load(\"glNormal3bv\");\n\tglad_glNormal3d = (PFNGLNORMAL3DPROC)load(\"glNormal3d\");\n\tglad_glNormal3dv = (PFNGLNORMAL3DVPROC)load(\"glNormal3dv\");\n\tglad_glNormal3f = (PFNGLNORMAL3FPROC)load(\"glNormal3f\");\n\tglad_glNormal3fv = (PFNGLNORMAL3FVPROC)load(\"glNormal3fv\");\n\tglad_glNormal3i = (PFNGLNORMAL3IPROC)load(\"glNormal3i\");\n\tglad_glNormal3iv = (PFNGLNORMAL3IVPROC)load(\"glNormal3iv\");\n\tglad_glNormal3s = (PFNGLNORMAL3SPROC)load(\"glNormal3s\");\n\tglad_glNormal3sv = (PFNGLNORMAL3SVPROC)load(\"glNormal3sv\");\n\tglad_glRasterPos2d = (PFNGLRASTERPOS2DPROC)load(\"glRasterPos2d\");\n\tglad_glRasterPos2dv = (PFNGLRASTERPOS2DVPROC)load(\"glRasterPos2dv\");\n\tglad_glRasterPos2f = (PFNGLRASTERPOS2FPROC)load(\"glRasterPos2f\");\n\tglad_glRasterPos2fv = (PFNGLRASTERPOS2FVPROC)load(\"glRasterPos2fv\");\n\tglad_glRasterPos2i = (PFNGLRASTERPOS2IPROC)load(\"glRasterPos2i\");\n\tglad_glRasterPos2iv = (PFNGLRASTERPOS2IVPROC)load(\"glRasterPos2iv\");\n\tglad_glRasterPos2s = (PFNGLRASTERPOS2SPROC)load(\"glRasterPos2s\");\n\tglad_glRasterPos2sv = (PFNGLRASTERPOS2SVPROC)load(\"glRasterPos2sv\");\n\tglad_glRasterPos3d = (PFNGLRASTERPOS3DPROC)load(\"glRasterPos3d\");\n\tglad_glRasterPos3dv = (PFNGLRASTERPOS3DVPROC)load(\"glRasterPos3dv\");\n\tglad_glRasterPos3f = (PFNGLRASTERPOS3FPROC)load(\"glRasterPos3f\");\n\tglad_glRasterPos3fv = (PFNGLRASTERPOS3FVPROC)load(\"glRasterPos3fv\");\n\tglad_glRasterPos3i = (PFNGLRASTERPOS3IPROC)load(\"glRasterPos3i\");\n\tglad_glRasterPos3iv = (PFNGLRASTERPOS3IVPROC)load(\"glRasterPos3iv\");\n\tglad_glRasterPos3s = (PFNGLRASTERPOS3SPROC)load(\"glRasterPos3s\");\n\tglad_glRasterPos3sv = (PFNGLRASTERPOS3SVPROC)load(\"glRasterPos3sv\");\n\tglad_glRasterPos4d = (PFNGLRASTERPOS4DPROC)load(\"glRasterPos4d\");\n\tglad_glRasterPos4dv = (PFNGLRASTERPOS4DVPROC)load(\"glRasterPos4dv\");\n\tglad_glRasterPos4f = (PFNGLRASTERPOS4FPROC)load(\"glRasterPos4f\");\n\tglad_glRasterPos4fv = (PFNGLRASTERPOS4FVPROC)load(\"glRasterPos4fv\");\n\tglad_glRasterPos4i = (PFNGLRASTERPOS4IPROC)load(\"glRasterPos4i\");\n\tglad_glRasterPos4iv = (PFNGLRASTERPOS4IVPROC)load(\"glRasterPos4iv\");\n\tglad_glRasterPos4s = (PFNGLRASTERPOS4SPROC)load(\"glRasterPos4s\");\n\tglad_glRasterPos4sv = (PFNGLRASTERPOS4SVPROC)load(\"glRasterPos4sv\");\n\tglad_glRectd = (PFNGLRECTDPROC)load(\"glRectd\");\n\tglad_glRectdv = (PFNGLRECTDVPROC)load(\"glRectdv\");\n\tglad_glRectf = (PFNGLRECTFPROC)load(\"glRectf\");\n\tglad_glRectfv = (PFNGLRECTFVPROC)load(\"glRectfv\");\n\tglad_glRecti = (PFNGLRECTIPROC)load(\"glRecti\");\n\tglad_glRectiv = (PFNGLRECTIVPROC)load(\"glRectiv\");\n\tglad_glRects = (PFNGLRECTSPROC)load(\"glRects\");\n\tglad_glRectsv = (PFNGLRECTSVPROC)load(\"glRectsv\");\n\tglad_glTexCoord1d = (PFNGLTEXCOORD1DPROC)load(\"glTexCoord1d\");\n\tglad_glTexCoord1dv = (PFNGLTEXCOORD1DVPROC)load(\"glTexCoord1dv\");\n\tglad_glTexCoord1f = (PFNGLTEXCOORD1FPROC)load(\"glTexCoord1f\");\n\tglad_glTexCoord1fv = (PFNGLTEXCOORD1FVPROC)load(\"glTexCoord1fv\");\n\tglad_glTexCoord1i = (PFNGLTEXCOORD1IPROC)load(\"glTexCoord1i\");\n\tglad_glTexCoord1iv = (PFNGLTEXCOORD1IVPROC)load(\"glTexCoord1iv\");\n\tglad_glTexCoord1s = (PFNGLTEXCOORD1SPROC)load(\"glTexCoord1s\");\n\tglad_glTexCoord1sv = (PFNGLTEXCOORD1SVPROC)load(\"glTexCoord1sv\");\n\tglad_glTexCoord2d = (PFNGLTEXCOORD2DPROC)load(\"glTexCoord2d\");\n\tglad_glTexCoord2dv = (PFNGLTEXCOORD2DVPROC)load(\"glTexCoord2dv\");\n\tglad_glTexCoord2f = (PFNGLTEXCOORD2FPROC)load(\"glTexCoord2f\");\n\tglad_glTexCoord2fv = (PFNGLTEXCOORD2FVPROC)load(\"glTexCoord2fv\");\n\tglad_glTexCoord2i = (PFNGLTEXCOORD2IPROC)load(\"glTexCoord2i\");\n\tglad_glTexCoord2iv = (PFNGLTEXCOORD2IVPROC)load(\"glTexCoord2iv\");\n\tglad_glTexCoord2s = (PFNGLTEXCOORD2SPROC)load(\"glTexCoord2s\");\n\tglad_glTexCoord2sv = (PFNGLTEXCOORD2SVPROC)load(\"glTexCoord2sv\");\n\tglad_glTexCoord3d = (PFNGLTEXCOORD3DPROC)load(\"glTexCoord3d\");\n\tglad_glTexCoord3dv = (PFNGLTEXCOORD3DVPROC)load(\"glTexCoord3dv\");\n\tglad_glTexCoord3f = (PFNGLTEXCOORD3FPROC)load(\"glTexCoord3f\");\n\tglad_glTexCoord3fv = (PFNGLTEXCOORD3FVPROC)load(\"glTexCoord3fv\");\n\tglad_glTexCoord3i = (PFNGLTEXCOORD3IPROC)load(\"glTexCoord3i\");\n\tglad_glTexCoord3iv = (PFNGLTEXCOORD3IVPROC)load(\"glTexCoord3iv\");\n\tglad_glTexCoord3s = (PFNGLTEXCOORD3SPROC)load(\"glTexCoord3s\");\n\tglad_glTexCoord3sv = (PFNGLTEXCOORD3SVPROC)load(\"glTexCoord3sv\");\n\tglad_glTexCoord4d = (PFNGLTEXCOORD4DPROC)load(\"glTexCoord4d\");\n\tglad_glTexCoord4dv = (PFNGLTEXCOORD4DVPROC)load(\"glTexCoord4dv\");\n\tglad_glTexCoord4f = (PFNGLTEXCOORD4FPROC)load(\"glTexCoord4f\");\n\tglad_glTexCoord4fv = (PFNGLTEXCOORD4FVPROC)load(\"glTexCoord4fv\");\n\tglad_glTexCoord4i = (PFNGLTEXCOORD4IPROC)load(\"glTexCoord4i\");\n\tglad_glTexCoord4iv = (PFNGLTEXCOORD4IVPROC)load(\"glTexCoord4iv\");\n\tglad_glTexCoord4s = (PFNGLTEXCOORD4SPROC)load(\"glTexCoord4s\");\n\tglad_glTexCoord4sv = (PFNGLTEXCOORD4SVPROC)load(\"glTexCoord4sv\");\n\tglad_glVertex2d = (PFNGLVERTEX2DPROC)load(\"glVertex2d\");\n\tglad_glVertex2dv = (PFNGLVERTEX2DVPROC)load(\"glVertex2dv\");\n\tglad_glVertex2f = (PFNGLVERTEX2FPROC)load(\"glVertex2f\");\n\tglad_glVertex2fv = (PFNGLVERTEX2FVPROC)load(\"glVertex2fv\");\n\tglad_glVertex2i = (PFNGLVERTEX2IPROC)load(\"glVertex2i\");\n\tglad_glVertex2iv = (PFNGLVERTEX2IVPROC)load(\"glVertex2iv\");\n\tglad_glVertex2s = (PFNGLVERTEX2SPROC)load(\"glVertex2s\");\n\tglad_glVertex2sv = (PFNGLVERTEX2SVPROC)load(\"glVertex2sv\");\n\tglad_glVertex3d = (PFNGLVERTEX3DPROC)load(\"glVertex3d\");\n\tglad_glVertex3dv = (PFNGLVERTEX3DVPROC)load(\"glVertex3dv\");\n\tglad_glVertex3f = (PFNGLVERTEX3FPROC)load(\"glVertex3f\");\n\tglad_glVertex3fv = (PFNGLVERTEX3FVPROC)load(\"glVertex3fv\");\n\tglad_glVertex3i = (PFNGLVERTEX3IPROC)load(\"glVertex3i\");\n\tglad_glVertex3iv = (PFNGLVERTEX3IVPROC)load(\"glVertex3iv\");\n\tglad_glVertex3s = (PFNGLVERTEX3SPROC)load(\"glVertex3s\");\n\tglad_glVertex3sv = (PFNGLVERTEX3SVPROC)load(\"glVertex3sv\");\n\tglad_glVertex4d = (PFNGLVERTEX4DPROC)load(\"glVertex4d\");\n\tglad_glVertex4dv = (PFNGLVERTEX4DVPROC)load(\"glVertex4dv\");\n\tglad_glVertex4f = (PFNGLVERTEX4FPROC)load(\"glVertex4f\");\n\tglad_glVertex4fv = (PFNGLVERTEX4FVPROC)load(\"glVertex4fv\");\n\tglad_glVertex4i = (PFNGLVERTEX4IPROC)load(\"glVertex4i\");\n\tglad_glVertex4iv = (PFNGLVERTEX4IVPROC)load(\"glVertex4iv\");\n\tglad_glVertex4s = (PFNGLVERTEX4SPROC)load(\"glVertex4s\");\n\tglad_glVertex4sv = (PFNGLVERTEX4SVPROC)load(\"glVertex4sv\");\n\tglad_glClipPlane = (PFNGLCLIPPLANEPROC)load(\"glClipPlane\");\n\tglad_glColorMaterial = (PFNGLCOLORMATERIALPROC)load(\"glColorMaterial\");\n\tglad_glFogf = (PFNGLFOGFPROC)load(\"glFogf\");\n\tglad_glFogfv = (PFNGLFOGFVPROC)load(\"glFogfv\");\n\tglad_glFogi = (PFNGLFOGIPROC)load(\"glFogi\");\n\tglad_glFogiv = (PFNGLFOGIVPROC)load(\"glFogiv\");\n\tglad_glLightf = (PFNGLLIGHTFPROC)load(\"glLightf\");\n\tglad_glLightfv = (PFNGLLIGHTFVPROC)load(\"glLightfv\");\n\tglad_glLighti = (PFNGLLIGHTIPROC)load(\"glLighti\");\n\tglad_glLightiv = (PFNGLLIGHTIVPROC)load(\"glLightiv\");\n\tglad_glLightModelf = (PFNGLLIGHTMODELFPROC)load(\"glLightModelf\");\n\tglad_glLightModelfv = (PFNGLLIGHTMODELFVPROC)load(\"glLightModelfv\");\n\tglad_glLightModeli = (PFNGLLIGHTMODELIPROC)load(\"glLightModeli\");\n\tglad_glLightModeliv = (PFNGLLIGHTMODELIVPROC)load(\"glLightModeliv\");\n\tglad_glLineStipple = (PFNGLLINESTIPPLEPROC)load(\"glLineStipple\");\n\tglad_glMaterialf = (PFNGLMATERIALFPROC)load(\"glMaterialf\");\n\tglad_glMaterialfv = (PFNGLMATERIALFVPROC)load(\"glMaterialfv\");\n\tglad_glMateriali = (PFNGLMATERIALIPROC)load(\"glMateriali\");\n\tglad_glMaterialiv = (PFNGLMATERIALIVPROC)load(\"glMaterialiv\");\n\tglad_glPolygonStipple = (PFNGLPOLYGONSTIPPLEPROC)load(\"glPolygonStipple\");\n\tglad_glShadeModel = (PFNGLSHADEMODELPROC)load(\"glShadeModel\");\n\tglad_glTexEnvf = (PFNGLTEXENVFPROC)load(\"glTexEnvf\");\n\tglad_glTexEnvfv = (PFNGLTEXENVFVPROC)load(\"glTexEnvfv\");\n\tglad_glTexEnvi = (PFNGLTEXENVIPROC)load(\"glTexEnvi\");\n\tglad_glTexEnviv = (PFNGLTEXENVIVPROC)load(\"glTexEnviv\");\n\tglad_glTexGend = (PFNGLTEXGENDPROC)load(\"glTexGend\");\n\tglad_glTexGendv = (PFNGLTEXGENDVPROC)load(\"glTexGendv\");\n\tglad_glTexGenf = (PFNGLTEXGENFPROC)load(\"glTexGenf\");\n\tglad_glTexGenfv = (PFNGLTEXGENFVPROC)load(\"glTexGenfv\");\n\tglad_glTexGeni = (PFNGLTEXGENIPROC)load(\"glTexGeni\");\n\tglad_glTexGeniv = (PFNGLTEXGENIVPROC)load(\"glTexGeniv\");\n\tglad_glFeedbackBuffer = (PFNGLFEEDBACKBUFFERPROC)load(\"glFeedbackBuffer\");\n\tglad_glSelectBuffer = (PFNGLSELECTBUFFERPROC)load(\"glSelectBuffer\");\n\tglad_glRenderMode = (PFNGLRENDERMODEPROC)load(\"glRenderMode\");\n\tglad_glInitNames = (PFNGLINITNAMESPROC)load(\"glInitNames\");\n\tglad_glLoadName = (PFNGLLOADNAMEPROC)load(\"glLoadName\");\n\tglad_glPassThrough = (PFNGLPASSTHROUGHPROC)load(\"glPassThrough\");\n\tglad_glPopName = (PFNGLPOPNAMEPROC)load(\"glPopName\");\n\tglad_glPushName = (PFNGLPUSHNAMEPROC)load(\"glPushName\");\n\tglad_glClearAccum = (PFNGLCLEARACCUMPROC)load(\"glClearAccum\");\n\tglad_glClearIndex = (PFNGLCLEARINDEXPROC)load(\"glClearIndex\");\n\tglad_glIndexMask = (PFNGLINDEXMASKPROC)load(\"glIndexMask\");\n\tglad_glAccum = (PFNGLACCUMPROC)load(\"glAccum\");\n\tglad_glPopAttrib = (PFNGLPOPATTRIBPROC)load(\"glPopAttrib\");\n\tglad_glPushAttrib = (PFNGLPUSHATTRIBPROC)load(\"glPushAttrib\");\n\tglad_glMap1d = (PFNGLMAP1DPROC)load(\"glMap1d\");\n\tglad_glMap1f = (PFNGLMAP1FPROC)load(\"glMap1f\");\n\tglad_glMap2d = (PFNGLMAP2DPROC)load(\"glMap2d\");\n\tglad_glMap2f = (PFNGLMAP2FPROC)load(\"glMap2f\");\n\tglad_glMapGrid1d = (PFNGLMAPGRID1DPROC)load(\"glMapGrid1d\");\n\tglad_glMapGrid1f = (PFNGLMAPGRID1FPROC)load(\"glMapGrid1f\");\n\tglad_glMapGrid2d = (PFNGLMAPGRID2DPROC)load(\"glMapGrid2d\");\n\tglad_glMapGrid2f = (PFNGLMAPGRID2FPROC)load(\"glMapGrid2f\");\n\tglad_glEvalCoord1d = (PFNGLEVALCOORD1DPROC)load(\"glEvalCoord1d\");\n\tglad_glEvalCoord1dv = (PFNGLEVALCOORD1DVPROC)load(\"glEvalCoord1dv\");\n\tglad_glEvalCoord1f = (PFNGLEVALCOORD1FPROC)load(\"glEvalCoord1f\");\n\tglad_glEvalCoord1fv = (PFNGLEVALCOORD1FVPROC)load(\"glEvalCoord1fv\");\n\tglad_glEvalCoord2d = (PFNGLEVALCOORD2DPROC)load(\"glEvalCoord2d\");\n\tglad_glEvalCoord2dv = (PFNGLEVALCOORD2DVPROC)load(\"glEvalCoord2dv\");\n\tglad_glEvalCoord2f = (PFNGLEVALCOORD2FPROC)load(\"glEvalCoord2f\");\n\tglad_glEvalCoord2fv = (PFNGLEVALCOORD2FVPROC)load(\"glEvalCoord2fv\");\n\tglad_glEvalMesh1 = (PFNGLEVALMESH1PROC)load(\"glEvalMesh1\");\n\tglad_glEvalPoint1 = (PFNGLEVALPOINT1PROC)load(\"glEvalPoint1\");\n\tglad_glEvalMesh2 = (PFNGLEVALMESH2PROC)load(\"glEvalMesh2\");\n\tglad_glEvalPoint2 = (PFNGLEVALPOINT2PROC)load(\"glEvalPoint2\");\n\tglad_glAlphaFunc = (PFNGLALPHAFUNCPROC)load(\"glAlphaFunc\");\n\tglad_glPixelZoom = (PFNGLPIXELZOOMPROC)load(\"glPixelZoom\");\n\tglad_glPixelTransferf = (PFNGLPIXELTRANSFERFPROC)load(\"glPixelTransferf\");\n\tglad_glPixelTransferi = (PFNGLPIXELTRANSFERIPROC)load(\"glPixelTransferi\");\n\tglad_glPixelMapfv = (PFNGLPIXELMAPFVPROC)load(\"glPixelMapfv\");\n\tglad_glPixelMapuiv = (PFNGLPIXELMAPUIVPROC)load(\"glPixelMapuiv\");\n\tglad_glPixelMapusv = (PFNGLPIXELMAPUSVPROC)load(\"glPixelMapusv\");\n\tglad_glCopyPixels = (PFNGLCOPYPIXELSPROC)load(\"glCopyPixels\");\n\tglad_glDrawPixels = (PFNGLDRAWPIXELSPROC)load(\"glDrawPixels\");\n\tglad_glGetClipPlane = (PFNGLGETCLIPPLANEPROC)load(\"glGetClipPlane\");\n\tglad_glGetLightfv = (PFNGLGETLIGHTFVPROC)load(\"glGetLightfv\");\n\tglad_glGetLightiv = (PFNGLGETLIGHTIVPROC)load(\"glGetLightiv\");\n\tglad_glGetMapdv = (PFNGLGETMAPDVPROC)load(\"glGetMapdv\");\n\tglad_glGetMapfv = (PFNGLGETMAPFVPROC)load(\"glGetMapfv\");\n\tglad_glGetMapiv = (PFNGLGETMAPIVPROC)load(\"glGetMapiv\");\n\tglad_glGetMaterialfv = (PFNGLGETMATERIALFVPROC)load(\"glGetMaterialfv\");\n\tglad_glGetMaterialiv = (PFNGLGETMATERIALIVPROC)load(\"glGetMaterialiv\");\n\tglad_glGetPixelMapfv = (PFNGLGETPIXELMAPFVPROC)load(\"glGetPixelMapfv\");\n\tglad_glGetPixelMapuiv = (PFNGLGETPIXELMAPUIVPROC)load(\"glGetPixelMapuiv\");\n\tglad_glGetPixelMapusv = (PFNGLGETPIXELMAPUSVPROC)load(\"glGetPixelMapusv\");\n\tglad_glGetPolygonStipple = (PFNGLGETPOLYGONSTIPPLEPROC)load(\"glGetPolygonStipple\");\n\tglad_glGetTexEnvfv = (PFNGLGETTEXENVFVPROC)load(\"glGetTexEnvfv\");\n\tglad_glGetTexEnviv = (PFNGLGETTEXENVIVPROC)load(\"glGetTexEnviv\");\n\tglad_glGetTexGendv = (PFNGLGETTEXGENDVPROC)load(\"glGetTexGendv\");\n\tglad_glGetTexGenfv = (PFNGLGETTEXGENFVPROC)load(\"glGetTexGenfv\");\n\tglad_glGetTexGeniv = (PFNGLGETTEXGENIVPROC)load(\"glGetTexGeniv\");\n\tglad_glIsList = (PFNGLISLISTPROC)load(\"glIsList\");\n\tglad_glFrustum = (PFNGLFRUSTUMPROC)load(\"glFrustum\");\n\tglad_glLoadIdentity = (PFNGLLOADIDENTITYPROC)load(\"glLoadIdentity\");\n\tglad_glLoadMatrixf = (PFNGLLOADMATRIXFPROC)load(\"glLoadMatrixf\");\n\tglad_glLoadMatrixd = (PFNGLLOADMATRIXDPROC)load(\"glLoadMatrixd\");\n\tglad_glMatrixMode = (PFNGLMATRIXMODEPROC)load(\"glMatrixMode\");\n\tglad_glMultMatrixf = (PFNGLMULTMATRIXFPROC)load(\"glMultMatrixf\");\n\tglad_glMultMatrixd = (PFNGLMULTMATRIXDPROC)load(\"glMultMatrixd\");\n\tglad_glOrtho = (PFNGLORTHOPROC)load(\"glOrtho\");\n\tglad_glPopMatrix = (PFNGLPOPMATRIXPROC)load(\"glPopMatrix\");\n\tglad_glPushMatrix = (PFNGLPUSHMATRIXPROC)load(\"glPushMatrix\");\n\tglad_glRotated = (PFNGLROTATEDPROC)load(\"glRotated\");\n\tglad_glRotatef = (PFNGLROTATEFPROC)load(\"glRotatef\");\n\tglad_glScaled = (PFNGLSCALEDPROC)load(\"glScaled\");\n\tglad_glScalef = (PFNGLSCALEFPROC)load(\"glScalef\");\n\tglad_glTranslated = (PFNGLTRANSLATEDPROC)load(\"glTranslated\");\n\tglad_glTranslatef = (PFNGLTRANSLATEFPROC)load(\"glTranslatef\");\n}\nstatic void load_GL_VERSION_1_1(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_1) return;\n\tglad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load(\"glDrawArrays\");\n\tglad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load(\"glDrawElements\");\n\tglad_glGetPointerv = (PFNGLGETPOINTERVPROC)load(\"glGetPointerv\");\n\tglad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load(\"glPolygonOffset\");\n\tglad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC)load(\"glCopyTexImage1D\");\n\tglad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load(\"glCopyTexImage2D\");\n\tglad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC)load(\"glCopyTexSubImage1D\");\n\tglad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load(\"glCopyTexSubImage2D\");\n\tglad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC)load(\"glTexSubImage1D\");\n\tglad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load(\"glTexSubImage2D\");\n\tglad_glBindTexture = (PFNGLBINDTEXTUREPROC)load(\"glBindTexture\");\n\tglad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load(\"glDeleteTextures\");\n\tglad_glGenTextures = (PFNGLGENTEXTURESPROC)load(\"glGenTextures\");\n\tglad_glIsTexture = (PFNGLISTEXTUREPROC)load(\"glIsTexture\");\n\tglad_glArrayElement = (PFNGLARRAYELEMENTPROC)load(\"glArrayElement\");\n\tglad_glColorPointer = (PFNGLCOLORPOINTERPROC)load(\"glColorPointer\");\n\tglad_glDisableClientState = (PFNGLDISABLECLIENTSTATEPROC)load(\"glDisableClientState\");\n\tglad_glEdgeFlagPointer = (PFNGLEDGEFLAGPOINTERPROC)load(\"glEdgeFlagPointer\");\n\tglad_glEnableClientState = (PFNGLENABLECLIENTSTATEPROC)load(\"glEnableClientState\");\n\tglad_glIndexPointer = (PFNGLINDEXPOINTERPROC)load(\"glIndexPointer\");\n\tglad_glInterleavedArrays = (PFNGLINTERLEAVEDARRAYSPROC)load(\"glInterleavedArrays\");\n\tglad_glNormalPointer = (PFNGLNORMALPOINTERPROC)load(\"glNormalPointer\");\n\tglad_glTexCoordPointer = (PFNGLTEXCOORDPOINTERPROC)load(\"glTexCoordPointer\");\n\tglad_glVertexPointer = (PFNGLVERTEXPOINTERPROC)load(\"glVertexPointer\");\n\tglad_glAreTexturesResident = (PFNGLARETEXTURESRESIDENTPROC)load(\"glAreTexturesResident\");\n\tglad_glPrioritizeTextures = (PFNGLPRIORITIZETEXTURESPROC)load(\"glPrioritizeTextures\");\n\tglad_glIndexub = (PFNGLINDEXUBPROC)load(\"glIndexub\");\n\tglad_glIndexubv = (PFNGLINDEXUBVPROC)load(\"glIndexubv\");\n\tglad_glPopClientAttrib = (PFNGLPOPCLIENTATTRIBPROC)load(\"glPopClientAttrib\");\n\tglad_glPushClientAttrib = (PFNGLPUSHCLIENTATTRIBPROC)load(\"glPushClientAttrib\");\n}\nstatic void load_GL_VERSION_1_2(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_2) return;\n\tglad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load(\"glDrawRangeElements\");\n\tglad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load(\"glTexImage3D\");\n\tglad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load(\"glTexSubImage3D\");\n\tglad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load(\"glCopyTexSubImage3D\");\n}\nstatic void load_GL_VERSION_1_3(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_3) return;\n\tglad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load(\"glActiveTexture\");\n\tglad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load(\"glSampleCoverage\");\n\tglad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load(\"glCompressedTexImage3D\");\n\tglad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load(\"glCompressedTexImage2D\");\n\tglad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)load(\"glCompressedTexImage1D\");\n\tglad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load(\"glCompressedTexSubImage3D\");\n\tglad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load(\"glCompressedTexSubImage2D\");\n\tglad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)load(\"glCompressedTexSubImage1D\");\n\tglad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)load(\"glGetCompressedTexImage\");\n\tglad_glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC)load(\"glClientActiveTexture\");\n\tglad_glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC)load(\"glMultiTexCoord1d\");\n\tglad_glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC)load(\"glMultiTexCoord1dv\");\n\tglad_glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC)load(\"glMultiTexCoord1f\");\n\tglad_glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC)load(\"glMultiTexCoord1fv\");\n\tglad_glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC)load(\"glMultiTexCoord1i\");\n\tglad_glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC)load(\"glMultiTexCoord1iv\");\n\tglad_glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC)load(\"glMultiTexCoord1s\");\n\tglad_glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC)load(\"glMultiTexCoord1sv\");\n\tglad_glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC)load(\"glMultiTexCoord2d\");\n\tglad_glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC)load(\"glMultiTexCoord2dv\");\n\tglad_glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC)load(\"glMultiTexCoord2f\");\n\tglad_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)load(\"glMultiTexCoord2fv\");\n\tglad_glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC)load(\"glMultiTexCoord2i\");\n\tglad_glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC)load(\"glMultiTexCoord2iv\");\n\tglad_glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC)load(\"glMultiTexCoord2s\");\n\tglad_glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC)load(\"glMultiTexCoord2sv\");\n\tglad_glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC)load(\"glMultiTexCoord3d\");\n\tglad_glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC)load(\"glMultiTexCoord3dv\");\n\tglad_glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC)load(\"glMultiTexCoord3f\");\n\tglad_glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC)load(\"glMultiTexCoord3fv\");\n\tglad_glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC)load(\"glMultiTexCoord3i\");\n\tglad_glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC)load(\"glMultiTexCoord3iv\");\n\tglad_glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC)load(\"glMultiTexCoord3s\");\n\tglad_glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC)load(\"glMultiTexCoord3sv\");\n\tglad_glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC)load(\"glMultiTexCoord4d\");\n\tglad_glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC)load(\"glMultiTexCoord4dv\");\n\tglad_glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC)load(\"glMultiTexCoord4f\");\n\tglad_glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC)load(\"glMultiTexCoord4fv\");\n\tglad_glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC)load(\"glMultiTexCoord4i\");\n\tglad_glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC)load(\"glMultiTexCoord4iv\");\n\tglad_glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC)load(\"glMultiTexCoord4s\");\n\tglad_glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC)load(\"glMultiTexCoord4sv\");\n\tglad_glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC)load(\"glLoadTransposeMatrixf\");\n\tglad_glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC)load(\"glLoadTransposeMatrixd\");\n\tglad_glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC)load(\"glMultTransposeMatrixf\");\n\tglad_glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC)load(\"glMultTransposeMatrixd\");\n}\nstatic void load_GL_VERSION_1_4(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_4) return;\n\tglad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load(\"glBlendFuncSeparate\");\n\tglad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)load(\"glMultiDrawArrays\");\n\tglad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)load(\"glMultiDrawElements\");\n\tglad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC)load(\"glPointParameterf\");\n\tglad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)load(\"glPointParameterfv\");\n\tglad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC)load(\"glPointParameteri\");\n\tglad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)load(\"glPointParameteriv\");\n\tglad_glFogCoordf = (PFNGLFOGCOORDFPROC)load(\"glFogCoordf\");\n\tglad_glFogCoordfv = (PFNGLFOGCOORDFVPROC)load(\"glFogCoordfv\");\n\tglad_glFogCoordd = (PFNGLFOGCOORDDPROC)load(\"glFogCoordd\");\n\tglad_glFogCoorddv = (PFNGLFOGCOORDDVPROC)load(\"glFogCoorddv\");\n\tglad_glFogCoordPointer = (PFNGLFOGCOORDPOINTERPROC)load(\"glFogCoordPointer\");\n\tglad_glSecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC)load(\"glSecondaryColor3b\");\n\tglad_glSecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC)load(\"glSecondaryColor3bv\");\n\tglad_glSecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC)load(\"glSecondaryColor3d\");\n\tglad_glSecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC)load(\"glSecondaryColor3dv\");\n\tglad_glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC)load(\"glSecondaryColor3f\");\n\tglad_glSecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC)load(\"glSecondaryColor3fv\");\n\tglad_glSecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC)load(\"glSecondaryColor3i\");\n\tglad_glSecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC)load(\"glSecondaryColor3iv\");\n\tglad_glSecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC)load(\"glSecondaryColor3s\");\n\tglad_glSecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC)load(\"glSecondaryColor3sv\");\n\tglad_glSecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC)load(\"glSecondaryColor3ub\");\n\tglad_glSecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC)load(\"glSecondaryColor3ubv\");\n\tglad_glSecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC)load(\"glSecondaryColor3ui\");\n\tglad_glSecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC)load(\"glSecondaryColor3uiv\");\n\tglad_glSecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC)load(\"glSecondaryColor3us\");\n\tglad_glSecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC)load(\"glSecondaryColor3usv\");\n\tglad_glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC)load(\"glSecondaryColorPointer\");\n\tglad_glWindowPos2d = (PFNGLWINDOWPOS2DPROC)load(\"glWindowPos2d\");\n\tglad_glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC)load(\"glWindowPos2dv\");\n\tglad_glWindowPos2f = (PFNGLWINDOWPOS2FPROC)load(\"glWindowPos2f\");\n\tglad_glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC)load(\"glWindowPos2fv\");\n\tglad_glWindowPos2i = (PFNGLWINDOWPOS2IPROC)load(\"glWindowPos2i\");\n\tglad_glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC)load(\"glWindowPos2iv\");\n\tglad_glWindowPos2s = (PFNGLWINDOWPOS2SPROC)load(\"glWindowPos2s\");\n\tglad_glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC)load(\"glWindowPos2sv\");\n\tglad_glWindowPos3d = (PFNGLWINDOWPOS3DPROC)load(\"glWindowPos3d\");\n\tglad_glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC)load(\"glWindowPos3dv\");\n\tglad_glWindowPos3f = (PFNGLWINDOWPOS3FPROC)load(\"glWindowPos3f\");\n\tglad_glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC)load(\"glWindowPos3fv\");\n\tglad_glWindowPos3i = (PFNGLWINDOWPOS3IPROC)load(\"glWindowPos3i\");\n\tglad_glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC)load(\"glWindowPos3iv\");\n\tglad_glWindowPos3s = (PFNGLWINDOWPOS3SPROC)load(\"glWindowPos3s\");\n\tglad_glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC)load(\"glWindowPos3sv\");\n\tglad_glBlendColor = (PFNGLBLENDCOLORPROC)load(\"glBlendColor\");\n\tglad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load(\"glBlendEquation\");\n}\nstatic void load_GL_VERSION_1_5(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_5) return;\n\tglad_glGenQueries = (PFNGLGENQUERIESPROC)load(\"glGenQueries\");\n\tglad_glDeleteQueries = (PFNGLDELETEQUERIESPROC)load(\"glDeleteQueries\");\n\tglad_glIsQuery = (PFNGLISQUERYPROC)load(\"glIsQuery\");\n\tglad_glBeginQuery = (PFNGLBEGINQUERYPROC)load(\"glBeginQuery\");\n\tglad_glEndQuery = (PFNGLENDQUERYPROC)load(\"glEndQuery\");\n\tglad_glGetQueryiv = (PFNGLGETQUERYIVPROC)load(\"glGetQueryiv\");\n\tglad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)load(\"glGetQueryObjectiv\");\n\tglad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load(\"glGetQueryObjectuiv\");\n\tglad_glBindBuffer = (PFNGLBINDBUFFERPROC)load(\"glBindBuffer\");\n\tglad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load(\"glDeleteBuffers\");\n\tglad_glGenBuffers = (PFNGLGENBUFFERSPROC)load(\"glGenBuffers\");\n\tglad_glIsBuffer = (PFNGLISBUFFERPROC)load(\"glIsBuffer\");\n\tglad_glBufferData = (PFNGLBUFFERDATAPROC)load(\"glBufferData\");\n\tglad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load(\"glBufferSubData\");\n\tglad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)load(\"glGetBufferSubData\");\n\tglad_glMapBuffer = (PFNGLMAPBUFFERPROC)load(\"glMapBuffer\");\n\tglad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load(\"glUnmapBuffer\");\n\tglad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load(\"glGetBufferParameteriv\");\n\tglad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load(\"glGetBufferPointerv\");\n}\nstatic void load_GL_VERSION_2_0(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_2_0) return;\n\tglad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load(\"glBlendEquationSeparate\");\n\tglad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load(\"glDrawBuffers\");\n\tglad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load(\"glStencilOpSeparate\");\n\tglad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load(\"glStencilFuncSeparate\");\n\tglad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load(\"glStencilMaskSeparate\");\n\tglad_glAttachShader = (PFNGLATTACHSHADERPROC)load(\"glAttachShader\");\n\tglad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load(\"glBindAttribLocation\");\n\tglad_glCompileShader = (PFNGLCOMPILESHADERPROC)load(\"glCompileShader\");\n\tglad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load(\"glCreateProgram\");\n\tglad_glCreateShader = (PFNGLCREATESHADERPROC)load(\"glCreateShader\");\n\tglad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load(\"glDeleteProgram\");\n\tglad_glDeleteShader = (PFNGLDELETESHADERPROC)load(\"glDeleteShader\");\n\tglad_glDetachShader = (PFNGLDETACHSHADERPROC)load(\"glDetachShader\");\n\tglad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load(\"glDisableVertexAttribArray\");\n\tglad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load(\"glEnableVertexAttribArray\");\n\tglad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load(\"glGetActiveAttrib\");\n\tglad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load(\"glGetActiveUniform\");\n\tglad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load(\"glGetAttachedShaders\");\n\tglad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load(\"glGetAttribLocation\");\n\tglad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load(\"glGetProgramiv\");\n\tglad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load(\"glGetProgramInfoLog\");\n\tglad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load(\"glGetShaderiv\");\n\tglad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load(\"glGetShaderInfoLog\");\n\tglad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load(\"glGetShaderSource\");\n\tglad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load(\"glGetUniformLocation\");\n\tglad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load(\"glGetUniformfv\");\n\tglad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load(\"glGetUniformiv\");\n\tglad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)load(\"glGetVertexAttribdv\");\n\tglad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load(\"glGetVertexAttribfv\");\n\tglad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load(\"glGetVertexAttribiv\");\n\tglad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load(\"glGetVertexAttribPointerv\");\n\tglad_glIsProgram = (PFNGLISPROGRAMPROC)load(\"glIsProgram\");\n\tglad_glIsShader = (PFNGLISSHADERPROC)load(\"glIsShader\");\n\tglad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load(\"glLinkProgram\");\n\tglad_glShaderSource = (PFNGLSHADERSOURCEPROC)load(\"glShaderSource\");\n\tglad_glUseProgram = (PFNGLUSEPROGRAMPROC)load(\"glUseProgram\");\n\tglad_glUniform1f = (PFNGLUNIFORM1FPROC)load(\"glUniform1f\");\n\tglad_glUniform2f = (PFNGLUNIFORM2FPROC)load(\"glUniform2f\");\n\tglad_glUniform3f = (PFNGLUNIFORM3FPROC)load(\"glUniform3f\");\n\tglad_glUniform4f = (PFNGLUNIFORM4FPROC)load(\"glUniform4f\");\n\tglad_glUniform1i = (PFNGLUNIFORM1IPROC)load(\"glUniform1i\");\n\tglad_glUniform2i = (PFNGLUNIFORM2IPROC)load(\"glUniform2i\");\n\tglad_glUniform3i = (PFNGLUNIFORM3IPROC)load(\"glUniform3i\");\n\tglad_glUniform4i = (PFNGLUNIFORM4IPROC)load(\"glUniform4i\");\n\tglad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load(\"glUniform1fv\");\n\tglad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load(\"glUniform2fv\");\n\tglad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load(\"glUniform3fv\");\n\tglad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load(\"glUniform4fv\");\n\tglad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load(\"glUniform1iv\");\n\tglad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load(\"glUniform2iv\");\n\tglad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load(\"glUniform3iv\");\n\tglad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load(\"glUniform4iv\");\n\tglad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load(\"glUniformMatrix2fv\");\n\tglad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load(\"glUniformMatrix3fv\");\n\tglad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load(\"glUniformMatrix4fv\");\n\tglad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load(\"glValidateProgram\");\n\tglad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)load(\"glVertexAttrib1d\");\n\tglad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)load(\"glVertexAttrib1dv\");\n\tglad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load(\"glVertexAttrib1f\");\n\tglad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load(\"glVertexAttrib1fv\");\n\tglad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)load(\"glVertexAttrib1s\");\n\tglad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)load(\"glVertexAttrib1sv\");\n\tglad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)load(\"glVertexAttrib2d\");\n\tglad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)load(\"glVertexAttrib2dv\");\n\tglad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load(\"glVertexAttrib2f\");\n\tglad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load(\"glVertexAttrib2fv\");\n\tglad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)load(\"glVertexAttrib2s\");\n\tglad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)load(\"glVertexAttrib2sv\");\n\tglad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)load(\"glVertexAttrib3d\");\n\tglad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)load(\"glVertexAttrib3dv\");\n\tglad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load(\"glVertexAttrib3f\");\n\tglad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load(\"glVertexAttrib3fv\");\n\tglad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)load(\"glVertexAttrib3s\");\n\tglad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)load(\"glVertexAttrib3sv\");\n\tglad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)load(\"glVertexAttrib4Nbv\");\n\tglad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)load(\"glVertexAttrib4Niv\");\n\tglad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)load(\"glVertexAttrib4Nsv\");\n\tglad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)load(\"glVertexAttrib4Nub\");\n\tglad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)load(\"glVertexAttrib4Nubv\");\n\tglad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)load(\"glVertexAttrib4Nuiv\");\n\tglad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)load(\"glVertexAttrib4Nusv\");\n\tglad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)load(\"glVertexAttrib4bv\");\n\tglad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)load(\"glVertexAttrib4d\");\n\tglad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)load(\"glVertexAttrib4dv\");\n\tglad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load(\"glVertexAttrib4f\");\n\tglad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load(\"glVertexAttrib4fv\");\n\tglad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)load(\"glVertexAttrib4iv\");\n\tglad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)load(\"glVertexAttrib4s\");\n\tglad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)load(\"glVertexAttrib4sv\");\n\tglad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)load(\"glVertexAttrib4ubv\");\n\tglad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)load(\"glVertexAttrib4uiv\");\n\tglad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)load(\"glVertexAttrib4usv\");\n\tglad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load(\"glVertexAttribPointer\");\n}\nstatic void load_GL_VERSION_2_1(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_2_1) return;\n\tglad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)load(\"glUniformMatrix2x3fv\");\n\tglad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)load(\"glUniformMatrix3x2fv\");\n\tglad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)load(\"glUniformMatrix2x4fv\");\n\tglad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)load(\"glUniformMatrix4x2fv\");\n\tglad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)load(\"glUniformMatrix3x4fv\");\n\tglad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)load(\"glUniformMatrix4x3fv\");\n}\nstatic void load_GL_VERSION_3_0(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_3_0) return;\n\tglad_glColorMaski = (PFNGLCOLORMASKIPROC)load(\"glColorMaski\");\n\tglad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)load(\"glGetBooleani_v\");\n\tglad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load(\"glGetIntegeri_v\");\n\tglad_glEnablei = (PFNGLENABLEIPROC)load(\"glEnablei\");\n\tglad_glDisablei = (PFNGLDISABLEIPROC)load(\"glDisablei\");\n\tglad_glIsEnabledi = (PFNGLISENABLEDIPROC)load(\"glIsEnabledi\");\n\tglad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)load(\"glBeginTransformFeedback\");\n\tglad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)load(\"glEndTransformFeedback\");\n\tglad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load(\"glBindBufferRange\");\n\tglad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load(\"glBindBufferBase\");\n\tglad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)load(\"glTransformFeedbackVaryings\");\n\tglad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)load(\"glGetTransformFeedbackVarying\");\n\tglad_glClampColor = (PFNGLCLAMPCOLORPROC)load(\"glClampColor\");\n\tglad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)load(\"glBeginConditionalRender\");\n\tglad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)load(\"glEndConditionalRender\");\n\tglad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load(\"glVertexAttribIPointer\");\n\tglad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load(\"glGetVertexAttribIiv\");\n\tglad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load(\"glGetVertexAttribIuiv\");\n\tglad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)load(\"glVertexAttribI1i\");\n\tglad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)load(\"glVertexAttribI2i\");\n\tglad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)load(\"glVertexAttribI3i\");\n\tglad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load(\"glVertexAttribI4i\");\n\tglad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)load(\"glVertexAttribI1ui\");\n\tglad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)load(\"glVertexAttribI2ui\");\n\tglad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)load(\"glVertexAttribI3ui\");\n\tglad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load(\"glVertexAttribI4ui\");\n\tglad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)load(\"glVertexAttribI1iv\");\n\tglad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)load(\"glVertexAttribI2iv\");\n\tglad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)load(\"glVertexAttribI3iv\");\n\tglad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load(\"glVertexAttribI4iv\");\n\tglad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)load(\"glVertexAttribI1uiv\");\n\tglad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)load(\"glVertexAttribI2uiv\");\n\tglad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)load(\"glVertexAttribI3uiv\");\n\tglad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load(\"glVertexAttribI4uiv\");\n\tglad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)load(\"glVertexAttribI4bv\");\n\tglad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)load(\"glVertexAttribI4sv\");\n\tglad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)load(\"glVertexAttribI4ubv\");\n\tglad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)load(\"glVertexAttribI4usv\");\n\tglad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load(\"glGetUniformuiv\");\n\tglad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)load(\"glBindFragDataLocation\");\n\tglad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)load(\"glGetFragDataLocation\");\n\tglad_glUniform1ui = (PFNGLUNIFORM1UIPROC)load(\"glUniform1ui\");\n\tglad_glUniform2ui = (PFNGLUNIFORM2UIPROC)load(\"glUniform2ui\");\n\tglad_glUniform3ui = (PFNGLUNIFORM3UIPROC)load(\"glUniform3ui\");\n\tglad_glUniform4ui = (PFNGLUNIFORM4UIPROC)load(\"glUniform4ui\");\n\tglad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC)load(\"glUniform1uiv\");\n\tglad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC)load(\"glUniform2uiv\");\n\tglad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC)load(\"glUniform3uiv\");\n\tglad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC)load(\"glUniform4uiv\");\n\tglad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)load(\"glTexParameterIiv\");\n\tglad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)load(\"glTexParameterIuiv\");\n\tglad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)load(\"glGetTexParameterIiv\");\n\tglad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)load(\"glGetTexParameterIuiv\");\n\tglad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)load(\"glClearBufferiv\");\n\tglad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)load(\"glClearBufferuiv\");\n\tglad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)load(\"glClearBufferfv\");\n\tglad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)load(\"glClearBufferfi\");\n\tglad_glGetStringi = (PFNGLGETSTRINGIPROC)load(\"glGetStringi\");\n\tglad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load(\"glIsRenderbuffer\");\n\tglad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load(\"glBindRenderbuffer\");\n\tglad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load(\"glDeleteRenderbuffers\");\n\tglad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load(\"glGenRenderbuffers\");\n\tglad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load(\"glRenderbufferStorage\");\n\tglad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load(\"glGetRenderbufferParameteriv\");\n\tglad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load(\"glIsFramebuffer\");\n\tglad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load(\"glBindFramebuffer\");\n\tglad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load(\"glDeleteFramebuffers\");\n\tglad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load(\"glGenFramebuffers\");\n\tglad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load(\"glCheckFramebufferStatus\");\n\tglad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)load(\"glFramebufferTexture1D\");\n\tglad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load(\"glFramebufferTexture2D\");\n\tglad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)load(\"glFramebufferTexture3D\");\n\tglad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load(\"glFramebufferRenderbuffer\");\n\tglad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load(\"glGetFramebufferAttachmentParameteriv\");\n\tglad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load(\"glGenerateMipmap\");\n\tglad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load(\"glBlitFramebuffer\");\n\tglad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load(\"glRenderbufferStorageMultisample\");\n\tglad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load(\"glFramebufferTextureLayer\");\n\tglad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load(\"glMapBufferRange\");\n\tglad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load(\"glFlushMappedBufferRange\");\n\tglad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load(\"glBindVertexArray\");\n\tglad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load(\"glDeleteVertexArrays\");\n\tglad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load(\"glGenVertexArrays\");\n\tglad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load(\"glIsVertexArray\");\n}\nstatic void load_GL_VERSION_3_1(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_3_1) return;\n\tglad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load(\"glDrawArraysInstanced\");\n\tglad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load(\"glDrawElementsInstanced\");\n\tglad_glTexBuffer = (PFNGLTEXBUFFERPROC)load(\"glTexBuffer\");\n\tglad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)load(\"glPrimitiveRestartIndex\");\n\tglad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load(\"glCopyBufferSubData\");\n\tglad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load(\"glGetUniformIndices\");\n\tglad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load(\"glGetActiveUniformsiv\");\n\tglad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)load(\"glGetActiveUniformName\");\n\tglad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load(\"glGetUniformBlockIndex\");\n\tglad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load(\"glGetActiveUniformBlockiv\");\n\tglad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load(\"glGetActiveUniformBlockName\");\n\tglad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load(\"glUniformBlockBinding\");\n\tglad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load(\"glBindBufferRange\");\n\tglad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load(\"glBindBufferBase\");\n\tglad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load(\"glGetIntegeri_v\");\n}\nstatic void load_GL_VERSION_3_2(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_3_2) return;\n\tglad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)load(\"glDrawElementsBaseVertex\");\n\tglad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)load(\"glDrawRangeElementsBaseVertex\");\n\tglad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)load(\"glDrawElementsInstancedBaseVertex\");\n\tglad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)load(\"glMultiDrawElementsBaseVertex\");\n\tglad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)load(\"glProvokingVertex\");\n\tglad_glFenceSync = (PFNGLFENCESYNCPROC)load(\"glFenceSync\");\n\tglad_glIsSync = (PFNGLISSYNCPROC)load(\"glIsSync\");\n\tglad_glDeleteSync = (PFNGLDELETESYNCPROC)load(\"glDeleteSync\");\n\tglad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)load(\"glClientWaitSync\");\n\tglad_glWaitSync = (PFNGLWAITSYNCPROC)load(\"glWaitSync\");\n\tglad_glGetInteger64v = (PFNGLGETINTEGER64VPROC)load(\"glGetInteger64v\");\n\tglad_glGetSynciv = (PFNGLGETSYNCIVPROC)load(\"glGetSynciv\");\n\tglad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)load(\"glGetInteger64i_v\");\n\tglad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)load(\"glGetBufferParameteri64v\");\n\tglad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)load(\"glFramebufferTexture\");\n\tglad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)load(\"glTexImage2DMultisample\");\n\tglad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)load(\"glTexImage3DMultisample\");\n\tglad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)load(\"glGetMultisamplefv\");\n\tglad_glSampleMaski = (PFNGLSAMPLEMASKIPROC)load(\"glSampleMaski\");\n}\nstatic void load_GL_VERSION_3_3(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_3_3) return;\n\tglad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)load(\"glBindFragDataLocationIndexed\");\n\tglad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)load(\"glGetFragDataIndex\");\n\tglad_glGenSamplers = (PFNGLGENSAMPLERSPROC)load(\"glGenSamplers\");\n\tglad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load(\"glDeleteSamplers\");\n\tglad_glIsSampler = (PFNGLISSAMPLERPROC)load(\"glIsSampler\");\n\tglad_glBindSampler = (PFNGLBINDSAMPLERPROC)load(\"glBindSampler\");\n\tglad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load(\"glSamplerParameteri\");\n\tglad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load(\"glSamplerParameteriv\");\n\tglad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load(\"glSamplerParameterf\");\n\tglad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load(\"glSamplerParameterfv\");\n\tglad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)load(\"glSamplerParameterIiv\");\n\tglad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)load(\"glSamplerParameterIuiv\");\n\tglad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load(\"glGetSamplerParameteriv\");\n\tglad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)load(\"glGetSamplerParameterIiv\");\n\tglad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load(\"glGetSamplerParameterfv\");\n\tglad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)load(\"glGetSamplerParameterIuiv\");\n\tglad_glQueryCounter = (PFNGLQUERYCOUNTERPROC)load(\"glQueryCounter\");\n\tglad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)load(\"glGetQueryObjecti64v\");\n\tglad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)load(\"glGetQueryObjectui64v\");\n\tglad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)load(\"glVertexAttribDivisor\");\n\tglad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)load(\"glVertexAttribP1ui\");\n\tglad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)load(\"glVertexAttribP1uiv\");\n\tglad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)load(\"glVertexAttribP2ui\");\n\tglad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)load(\"glVertexAttribP2uiv\");\n\tglad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)load(\"glVertexAttribP3ui\");\n\tglad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)load(\"glVertexAttribP3uiv\");\n\tglad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)load(\"glVertexAttribP4ui\");\n\tglad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)load(\"glVertexAttribP4uiv\");\n\tglad_glVertexP2ui = (PFNGLVERTEXP2UIPROC)load(\"glVertexP2ui\");\n\tglad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)load(\"glVertexP2uiv\");\n\tglad_glVertexP3ui = (PFNGLVERTEXP3UIPROC)load(\"glVertexP3ui\");\n\tglad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)load(\"glVertexP3uiv\");\n\tglad_glVertexP4ui = (PFNGLVERTEXP4UIPROC)load(\"glVertexP4ui\");\n\tglad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)load(\"glVertexP4uiv\");\n\tglad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)load(\"glTexCoordP1ui\");\n\tglad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)load(\"glTexCoordP1uiv\");\n\tglad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)load(\"glTexCoordP2ui\");\n\tglad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)load(\"glTexCoordP2uiv\");\n\tglad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)load(\"glTexCoordP3ui\");\n\tglad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)load(\"glTexCoordP3uiv\");\n\tglad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)load(\"glTexCoordP4ui\");\n\tglad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)load(\"glTexCoordP4uiv\");\n\tglad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)load(\"glMultiTexCoordP1ui\");\n\tglad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)load(\"glMultiTexCoordP1uiv\");\n\tglad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)load(\"glMultiTexCoordP2ui\");\n\tglad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)load(\"glMultiTexCoordP2uiv\");\n\tglad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)load(\"glMultiTexCoordP3ui\");\n\tglad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)load(\"glMultiTexCoordP3uiv\");\n\tglad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)load(\"glMultiTexCoordP4ui\");\n\tglad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)load(\"glMultiTexCoordP4uiv\");\n\tglad_glNormalP3ui = (PFNGLNORMALP3UIPROC)load(\"glNormalP3ui\");\n\tglad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC)load(\"glNormalP3uiv\");\n\tglad_glColorP3ui = (PFNGLCOLORP3UIPROC)load(\"glColorP3ui\");\n\tglad_glColorP3uiv = (PFNGLCOLORP3UIVPROC)load(\"glColorP3uiv\");\n\tglad_glColorP4ui = (PFNGLCOLORP4UIPROC)load(\"glColorP4ui\");\n\tglad_glColorP4uiv = (PFNGLCOLORP4UIVPROC)load(\"glColorP4uiv\");\n\tglad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)load(\"glSecondaryColorP3ui\");\n\tglad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)load(\"glSecondaryColorP3uiv\");\n}\nstatic void load_GL_VERSION_4_0(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_0) return;\n\tglad_glMinSampleShading = (PFNGLMINSAMPLESHADINGPROC)load(\"glMinSampleShading\");\n\tglad_glBlendEquationi = (PFNGLBLENDEQUATIONIPROC)load(\"glBlendEquationi\");\n\tglad_glBlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC)load(\"glBlendEquationSeparatei\");\n\tglad_glBlendFunci = (PFNGLBLENDFUNCIPROC)load(\"glBlendFunci\");\n\tglad_glBlendFuncSeparatei = (PFNGLBLENDFUNCSEPARATEIPROC)load(\"glBlendFuncSeparatei\");\n\tglad_glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC)load(\"glDrawArraysIndirect\");\n\tglad_glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC)load(\"glDrawElementsIndirect\");\n\tglad_glUniform1d = (PFNGLUNIFORM1DPROC)load(\"glUniform1d\");\n\tglad_glUniform2d = (PFNGLUNIFORM2DPROC)load(\"glUniform2d\");\n\tglad_glUniform3d = (PFNGLUNIFORM3DPROC)load(\"glUniform3d\");\n\tglad_glUniform4d = (PFNGLUNIFORM4DPROC)load(\"glUniform4d\");\n\tglad_glUniform1dv = (PFNGLUNIFORM1DVPROC)load(\"glUniform1dv\");\n\tglad_glUniform2dv = (PFNGLUNIFORM2DVPROC)load(\"glUniform2dv\");\n\tglad_glUniform3dv = (PFNGLUNIFORM3DVPROC)load(\"glUniform3dv\");\n\tglad_glUniform4dv = (PFNGLUNIFORM4DVPROC)load(\"glUniform4dv\");\n\tglad_glUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC)load(\"glUniformMatrix2dv\");\n\tglad_glUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC)load(\"glUniformMatrix3dv\");\n\tglad_glUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC)load(\"glUniformMatrix4dv\");\n\tglad_glUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC)load(\"glUniformMatrix2x3dv\");\n\tglad_glUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC)load(\"glUniformMatrix2x4dv\");\n\tglad_glUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC)load(\"glUniformMatrix3x2dv\");\n\tglad_glUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC)load(\"glUniformMatrix3x4dv\");\n\tglad_glUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC)load(\"glUniformMatrix4x2dv\");\n\tglad_glUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC)load(\"glUniformMatrix4x3dv\");\n\tglad_glGetUniformdv = (PFNGLGETUNIFORMDVPROC)load(\"glGetUniformdv\");\n\tglad_glGetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)load(\"glGetSubroutineUniformLocation\");\n\tglad_glGetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC)load(\"glGetSubroutineIndex\");\n\tglad_glGetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)load(\"glGetActiveSubroutineUniformiv\");\n\tglad_glGetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)load(\"glGetActiveSubroutineUniformName\");\n\tglad_glGetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC)load(\"glGetActiveSubroutineName\");\n\tglad_glUniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC)load(\"glUniformSubroutinesuiv\");\n\tglad_glGetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC)load(\"glGetUniformSubroutineuiv\");\n\tglad_glGetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC)load(\"glGetProgramStageiv\");\n\tglad_glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)load(\"glPatchParameteri\");\n\tglad_glPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC)load(\"glPatchParameterfv\");\n\tglad_glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)load(\"glBindTransformFeedback\");\n\tglad_glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC)load(\"glDeleteTransformFeedbacks\");\n\tglad_glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)load(\"glGenTransformFeedbacks\");\n\tglad_glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC)load(\"glIsTransformFeedback\");\n\tglad_glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC)load(\"glPauseTransformFeedback\");\n\tglad_glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC)load(\"glResumeTransformFeedback\");\n\tglad_glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC)load(\"glDrawTransformFeedback\");\n\tglad_glDrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)load(\"glDrawTransformFeedbackStream\");\n\tglad_glBeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC)load(\"glBeginQueryIndexed\");\n\tglad_glEndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC)load(\"glEndQueryIndexed\");\n\tglad_glGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC)load(\"glGetQueryIndexediv\");\n}\nstatic void load_GL_VERSION_4_1(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_1) return;\n\tglad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)load(\"glReleaseShaderCompiler\");\n\tglad_glShaderBinary = (PFNGLSHADERBINARYPROC)load(\"glShaderBinary\");\n\tglad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)load(\"glGetShaderPrecisionFormat\");\n\tglad_glDepthRangef = (PFNGLDEPTHRANGEFPROC)load(\"glDepthRangef\");\n\tglad_glClearDepthf = (PFNGLCLEARDEPTHFPROC)load(\"glClearDepthf\");\n\tglad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)load(\"glGetProgramBinary\");\n\tglad_glProgramBinary = (PFNGLPROGRAMBINARYPROC)load(\"glProgramBinary\");\n\tglad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)load(\"glProgramParameteri\");\n\tglad_glUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC)load(\"glUseProgramStages\");\n\tglad_glActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC)load(\"glActiveShaderProgram\");\n\tglad_glCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC)load(\"glCreateShaderProgramv\");\n\tglad_glBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC)load(\"glBindProgramPipeline\");\n\tglad_glDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC)load(\"glDeleteProgramPipelines\");\n\tglad_glGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC)load(\"glGenProgramPipelines\");\n\tglad_glIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC)load(\"glIsProgramPipeline\");\n\tglad_glGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC)load(\"glGetProgramPipelineiv\");\n\tglad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)load(\"glProgramParameteri\");\n\tglad_glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)load(\"glProgramUniform1i\");\n\tglad_glProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC)load(\"glProgramUniform1iv\");\n\tglad_glProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC)load(\"glProgramUniform1f\");\n\tglad_glProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC)load(\"glProgramUniform1fv\");\n\tglad_glProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC)load(\"glProgramUniform1d\");\n\tglad_glProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC)load(\"glProgramUniform1dv\");\n\tglad_glProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC)load(\"glProgramUniform1ui\");\n\tglad_glProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC)load(\"glProgramUniform1uiv\");\n\tglad_glProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC)load(\"glProgramUniform2i\");\n\tglad_glProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC)load(\"glProgramUniform2iv\");\n\tglad_glProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC)load(\"glProgramUniform2f\");\n\tglad_glProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC)load(\"glProgramUniform2fv\");\n\tglad_glProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC)load(\"glProgramUniform2d\");\n\tglad_glProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC)load(\"glProgramUniform2dv\");\n\tglad_glProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC)load(\"glProgramUniform2ui\");\n\tglad_glProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC)load(\"glProgramUniform2uiv\");\n\tglad_glProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC)load(\"glProgramUniform3i\");\n\tglad_glProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC)load(\"glProgramUniform3iv\");\n\tglad_glProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC)load(\"glProgramUniform3f\");\n\tglad_glProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC)load(\"glProgramUniform3fv\");\n\tglad_glProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC)load(\"glProgramUniform3d\");\n\tglad_glProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC)load(\"glProgramUniform3dv\");\n\tglad_glProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC)load(\"glProgramUniform3ui\");\n\tglad_glProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC)load(\"glProgramUniform3uiv\");\n\tglad_glProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC)load(\"glProgramUniform4i\");\n\tglad_glProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC)load(\"glProgramUniform4iv\");\n\tglad_glProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC)load(\"glProgramUniform4f\");\n\tglad_glProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC)load(\"glProgramUniform4fv\");\n\tglad_glProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC)load(\"glProgramUniform4d\");\n\tglad_glProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC)load(\"glProgramUniform4dv\");\n\tglad_glProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC)load(\"glProgramUniform4ui\");\n\tglad_glProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC)load(\"glProgramUniform4uiv\");\n\tglad_glProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC)load(\"glProgramUniformMatrix2fv\");\n\tglad_glProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC)load(\"glProgramUniformMatrix3fv\");\n\tglad_glProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC)load(\"glProgramUniformMatrix4fv\");\n\tglad_glProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC)load(\"glProgramUniformMatrix2dv\");\n\tglad_glProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC)load(\"glProgramUniformMatrix3dv\");\n\tglad_glProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC)load(\"glProgramUniformMatrix4dv\");\n\tglad_glProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)load(\"glProgramUniformMatrix2x3fv\");\n\tglad_glProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)load(\"glProgramUniformMatrix3x2fv\");\n\tglad_glProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)load(\"glProgramUniformMatrix2x4fv\");\n\tglad_glProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)load(\"glProgramUniformMatrix4x2fv\");\n\tglad_glProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)load(\"glProgramUniformMatrix3x4fv\");\n\tglad_glProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)load(\"glProgramUniformMatrix4x3fv\");\n\tglad_glProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)load(\"glProgramUniformMatrix2x3dv\");\n\tglad_glProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)load(\"glProgramUniformMatrix3x2dv\");\n\tglad_glProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)load(\"glProgramUniformMatrix2x4dv\");\n\tglad_glProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)load(\"glProgramUniformMatrix4x2dv\");\n\tglad_glProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)load(\"glProgramUniformMatrix3x4dv\");\n\tglad_glProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)load(\"glProgramUniformMatrix4x3dv\");\n\tglad_glValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC)load(\"glValidateProgramPipeline\");\n\tglad_glGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC)load(\"glGetProgramPipelineInfoLog\");\n\tglad_glVertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC)load(\"glVertexAttribL1d\");\n\tglad_glVertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC)load(\"glVertexAttribL2d\");\n\tglad_glVertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC)load(\"glVertexAttribL3d\");\n\tglad_glVertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC)load(\"glVertexAttribL4d\");\n\tglad_glVertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC)load(\"glVertexAttribL1dv\");\n\tglad_glVertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC)load(\"glVertexAttribL2dv\");\n\tglad_glVertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC)load(\"glVertexAttribL3dv\");\n\tglad_glVertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC)load(\"glVertexAttribL4dv\");\n\tglad_glVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC)load(\"glVertexAttribLPointer\");\n\tglad_glGetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC)load(\"glGetVertexAttribLdv\");\n\tglad_glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC)load(\"glViewportArrayv\");\n\tglad_glViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC)load(\"glViewportIndexedf\");\n\tglad_glViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC)load(\"glViewportIndexedfv\");\n\tglad_glScissorArrayv = (PFNGLSCISSORARRAYVPROC)load(\"glScissorArrayv\");\n\tglad_glScissorIndexed = (PFNGLSCISSORINDEXEDPROC)load(\"glScissorIndexed\");\n\tglad_glScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC)load(\"glScissorIndexedv\");\n\tglad_glDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC)load(\"glDepthRangeArrayv\");\n\tglad_glDepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC)load(\"glDepthRangeIndexed\");\n\tglad_glGetFloati_v = (PFNGLGETFLOATI_VPROC)load(\"glGetFloati_v\");\n\tglad_glGetDoublei_v = (PFNGLGETDOUBLEI_VPROC)load(\"glGetDoublei_v\");\n}\nstatic void load_GL_VERSION_4_2(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_2) return;\n\tglad_glDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)load(\"glDrawArraysInstancedBaseInstance\");\n\tglad_glDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)load(\"glDrawElementsInstancedBaseInstance\");\n\tglad_glDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)load(\"glDrawElementsInstancedBaseVertexBaseInstance\");\n\tglad_glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)load(\"glGetInternalformativ\");\n\tglad_glGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)load(\"glGetActiveAtomicCounterBufferiv\");\n\tglad_glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)load(\"glBindImageTexture\");\n\tglad_glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)load(\"glMemoryBarrier\");\n\tglad_glTexStorage1D = (PFNGLTEXSTORAGE1DPROC)load(\"glTexStorage1D\");\n\tglad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)load(\"glTexStorage2D\");\n\tglad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)load(\"glTexStorage3D\");\n\tglad_glDrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)load(\"glDrawTransformFeedbackInstanced\");\n\tglad_glDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)load(\"glDrawTransformFeedbackStreamInstanced\");\n}\nstatic void load_GL_VERSION_4_3(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_3) return;\n\tglad_glClearBufferData = (PFNGLCLEARBUFFERDATAPROC)load(\"glClearBufferData\");\n\tglad_glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC)load(\"glClearBufferSubData\");\n\tglad_glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)load(\"glDispatchCompute\");\n\tglad_glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC)load(\"glDispatchComputeIndirect\");\n\tglad_glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC)load(\"glCopyImageSubData\");\n\tglad_glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC)load(\"glFramebufferParameteri\");\n\tglad_glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC)load(\"glGetFramebufferParameteriv\");\n\tglad_glGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC)load(\"glGetInternalformati64v\");\n\tglad_glInvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC)load(\"glInvalidateTexSubImage\");\n\tglad_glInvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC)load(\"glInvalidateTexImage\");\n\tglad_glInvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC)load(\"glInvalidateBufferSubData\");\n\tglad_glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC)load(\"glInvalidateBufferData\");\n\tglad_glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)load(\"glInvalidateFramebuffer\");\n\tglad_glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC)load(\"glInvalidateSubFramebuffer\");\n\tglad_glMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC)load(\"glMultiDrawArraysIndirect\");\n\tglad_glMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC)load(\"glMultiDrawElementsIndirect\");\n\tglad_glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)load(\"glGetProgramInterfaceiv\");\n\tglad_glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC)load(\"glGetProgramResourceIndex\");\n\tglad_glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)load(\"glGetProgramResourceName\");\n\tglad_glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)load(\"glGetProgramResourceiv\");\n\tglad_glGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC)load(\"glGetProgramResourceLocation\");\n\tglad_glGetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)load(\"glGetProgramResourceLocationIndex\");\n\tglad_glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC)load(\"glShaderStorageBlockBinding\");\n\tglad_glTexBufferRange = (PFNGLTEXBUFFERRANGEPROC)load(\"glTexBufferRange\");\n\tglad_glTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC)load(\"glTexStorage2DMultisample\");\n\tglad_glTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)load(\"glTexStorage3DMultisample\");\n\tglad_glTextureView = (PFNGLTEXTUREVIEWPROC)load(\"glTextureView\");\n\tglad_glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC)load(\"glBindVertexBuffer\");\n\tglad_glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC)load(\"glVertexAttribFormat\");\n\tglad_glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC)load(\"glVertexAttribIFormat\");\n\tglad_glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC)load(\"glVertexAttribLFormat\");\n\tglad_glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC)load(\"glVertexAttribBinding\");\n\tglad_glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC)load(\"glVertexBindingDivisor\");\n\tglad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)load(\"glDebugMessageControl\");\n\tglad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)load(\"glDebugMessageInsert\");\n\tglad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)load(\"glDebugMessageCallback\");\n\tglad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)load(\"glGetDebugMessageLog\");\n\tglad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)load(\"glPushDebugGroup\");\n\tglad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)load(\"glPopDebugGroup\");\n\tglad_glObjectLabel = (PFNGLOBJECTLABELPROC)load(\"glObjectLabel\");\n\tglad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)load(\"glGetObjectLabel\");\n\tglad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)load(\"glObjectPtrLabel\");\n\tglad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load(\"glGetObjectPtrLabel\");\n\tglad_glGetPointerv = (PFNGLGETPOINTERVPROC)load(\"glGetPointerv\");\n}\nstatic void load_GL_VERSION_4_4(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_4) return;\n\tglad_glBufferStorage = (PFNGLBUFFERSTORAGEPROC)load(\"glBufferStorage\");\n\tglad_glClearTexImage = (PFNGLCLEARTEXIMAGEPROC)load(\"glClearTexImage\");\n\tglad_glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC)load(\"glClearTexSubImage\");\n\tglad_glBindBuffersBase = (PFNGLBINDBUFFERSBASEPROC)load(\"glBindBuffersBase\");\n\tglad_glBindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC)load(\"glBindBuffersRange\");\n\tglad_glBindTextures = (PFNGLBINDTEXTURESPROC)load(\"glBindTextures\");\n\tglad_glBindSamplers = (PFNGLBINDSAMPLERSPROC)load(\"glBindSamplers\");\n\tglad_glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)load(\"glBindImageTextures\");\n\tglad_glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)load(\"glBindVertexBuffers\");\n}\nstatic void load_GL_VERSION_4_5(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_5) return;\n\tglad_glClipControl = (PFNGLCLIPCONTROLPROC)load(\"glClipControl\");\n\tglad_glCreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC)load(\"glCreateTransformFeedbacks\");\n\tglad_glTransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)load(\"glTransformFeedbackBufferBase\");\n\tglad_glTransformFeedbackBufferRange = (PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)load(\"glTransformFeedbackBufferRange\");\n\tglad_glGetTransformFeedbackiv = (PFNGLGETTRANSFORMFEEDBACKIVPROC)load(\"glGetTransformFeedbackiv\");\n\tglad_glGetTransformFeedbacki_v = (PFNGLGETTRANSFORMFEEDBACKI_VPROC)load(\"glGetTransformFeedbacki_v\");\n\tglad_glGetTransformFeedbacki64_v = (PFNGLGETTRANSFORMFEEDBACKI64_VPROC)load(\"glGetTransformFeedbacki64_v\");\n\tglad_glCreateBuffers = (PFNGLCREATEBUFFERSPROC)load(\"glCreateBuffers\");\n\tglad_glNamedBufferStorage = (PFNGLNAMEDBUFFERSTORAGEPROC)load(\"glNamedBufferStorage\");\n\tglad_glNamedBufferData = (PFNGLNAMEDBUFFERDATAPROC)load(\"glNamedBufferData\");\n\tglad_glNamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC)load(\"glNamedBufferSubData\");\n\tglad_glCopyNamedBufferSubData = (PFNGLCOPYNAMEDBUFFERSUBDATAPROC)load(\"glCopyNamedBufferSubData\");\n\tglad_glClearNamedBufferData = (PFNGLCLEARNAMEDBUFFERDATAPROC)load(\"glClearNamedBufferData\");\n\tglad_glClearNamedBufferSubData = (PFNGLCLEARNAMEDBUFFERSUBDATAPROC)load(\"glClearNamedBufferSubData\");\n\tglad_glMapNamedBuffer = (PFNGLMAPNAMEDBUFFERPROC)load(\"glMapNamedBuffer\");\n\tglad_glMapNamedBufferRange = (PFNGLMAPNAMEDBUFFERRANGEPROC)load(\"glMapNamedBufferRange\");\n\tglad_glUnmapNamedBuffer = (PFNGLUNMAPNAMEDBUFFERPROC)load(\"glUnmapNamedBuffer\");\n\tglad_glFlushMappedNamedBufferRange = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)load(\"glFlushMappedNamedBufferRange\");\n\tglad_glGetNamedBufferParameteriv = (PFNGLGETNAMEDBUFFERPARAMETERIVPROC)load(\"glGetNamedBufferParameteriv\");\n\tglad_glGetNamedBufferParameteri64v = (PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)load(\"glGetNamedBufferParameteri64v\");\n\tglad_glGetNamedBufferPointerv = (PFNGLGETNAMEDBUFFERPOINTERVPROC)load(\"glGetNamedBufferPointerv\");\n\tglad_glGetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC)load(\"glGetNamedBufferSubData\");\n\tglad_glCreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC)load(\"glCreateFramebuffers\");\n\tglad_glNamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)load(\"glNamedFramebufferRenderbuffer\");\n\tglad_glNamedFramebufferParameteri = (PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)load(\"glNamedFramebufferParameteri\");\n\tglad_glNamedFramebufferTexture = (PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)load(\"glNamedFramebufferTexture\");\n\tglad_glNamedFramebufferTextureLayer = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)load(\"glNamedFramebufferTextureLayer\");\n\tglad_glNamedFramebufferDrawBuffer = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)load(\"glNamedFramebufferDrawBuffer\");\n\tglad_glNamedFramebufferDrawBuffers = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)load(\"glNamedFramebufferDrawBuffers\");\n\tglad_glNamedFramebufferReadBuffer = (PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)load(\"glNamedFramebufferReadBuffer\");\n\tglad_glInvalidateNamedFramebufferData = (PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)load(\"glInvalidateNamedFramebufferData\");\n\tglad_glInvalidateNamedFramebufferSubData = (PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)load(\"glInvalidateNamedFramebufferSubData\");\n\tglad_glClearNamedFramebufferiv = (PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)load(\"glClearNamedFramebufferiv\");\n\tglad_glClearNamedFramebufferuiv = (PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)load(\"glClearNamedFramebufferuiv\");\n\tglad_glClearNamedFramebufferfv = (PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)load(\"glClearNamedFramebufferfv\");\n\tglad_glClearNamedFramebufferfi = (PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)load(\"glClearNamedFramebufferfi\");\n\tglad_glBlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC)load(\"glBlitNamedFramebuffer\");\n\tglad_glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)load(\"glCheckNamedFramebufferStatus\");\n\tglad_glGetNamedFramebufferParameteriv = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)load(\"glGetNamedFramebufferParameteriv\");\n\tglad_glGetNamedFramebufferAttachmentParameteriv = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load(\"glGetNamedFramebufferAttachmentParameteriv\");\n\tglad_glCreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC)load(\"glCreateRenderbuffers\");\n\tglad_glNamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC)load(\"glNamedRenderbufferStorage\");\n\tglad_glNamedRenderbufferStorageMultisample = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)load(\"glNamedRenderbufferStorageMultisample\");\n\tglad_glGetNamedRenderbufferParameteriv = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)load(\"glGetNamedRenderbufferParameteriv\");\n\tglad_glCreateTextures = (PFNGLCREATETEXTURESPROC)load(\"glCreateTextures\");\n\tglad_glTextureBuffer = (PFNGLTEXTUREBUFFERPROC)load(\"glTextureBuffer\");\n\tglad_glTextureBufferRange = (PFNGLTEXTUREBUFFERRANGEPROC)load(\"glTextureBufferRange\");\n\tglad_glTextureStorage1D = (PFNGLTEXTURESTORAGE1DPROC)load(\"glTextureStorage1D\");\n\tglad_glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC)load(\"glTextureStorage2D\");\n\tglad_glTextureStorage3D = (PFNGLTEXTURESTORAGE3DPROC)load(\"glTextureStorage3D\");\n\tglad_glTextureStorage2DMultisample = (PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)load(\"glTextureStorage2DMultisample\");\n\tglad_glTextureStorage3DMultisample = (PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)load(\"glTextureStorage3DMultisample\");\n\tglad_glTextureSubImage1D = (PFNGLTEXTURESUBIMAGE1DPROC)load(\"glTextureSubImage1D\");\n\tglad_glTextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC)load(\"glTextureSubImage2D\");\n\tglad_glTextureSubImage3D = (PFNGLTEXTURESUBIMAGE3DPROC)load(\"glTextureSubImage3D\");\n\tglad_glCompressedTextureSubImage1D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)load(\"glCompressedTextureSubImage1D\");\n\tglad_glCompressedTextureSubImage2D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)load(\"glCompressedTextureSubImage2D\");\n\tglad_glCompressedTextureSubImage3D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)load(\"glCompressedTextureSubImage3D\");\n\tglad_glCopyTextureSubImage1D = (PFNGLCOPYTEXTURESUBIMAGE1DPROC)load(\"glCopyTextureSubImage1D\");\n\tglad_glCopyTextureSubImage2D = (PFNGLCOPYTEXTURESUBIMAGE2DPROC)load(\"glCopyTextureSubImage2D\");\n\tglad_glCopyTextureSubImage3D = (PFNGLCOPYTEXTURESUBIMAGE3DPROC)load(\"glCopyTextureSubImage3D\");\n\tglad_glTextureParameterf = (PFNGLTEXTUREPARAMETERFPROC)load(\"glTextureParameterf\");\n\tglad_glTextureParameterfv = (PFNGLTEXTUREPARAMETERFVPROC)load(\"glTextureParameterfv\");\n\tglad_glTextureParameteri = (PFNGLTEXTUREPARAMETERIPROC)load(\"glTextureParameteri\");\n\tglad_glTextureParameterIiv = (PFNGLTEXTUREPARAMETERIIVPROC)load(\"glTextureParameterIiv\");\n\tglad_glTextureParameterIuiv = (PFNGLTEXTUREPARAMETERIUIVPROC)load(\"glTextureParameterIuiv\");\n\tglad_glTextureParameteriv = (PFNGLTEXTUREPARAMETERIVPROC)load(\"glTextureParameteriv\");\n\tglad_glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC)load(\"glGenerateTextureMipmap\");\n\tglad_glBindTextureUnit = (PFNGLBINDTEXTUREUNITPROC)load(\"glBindTextureUnit\");\n\tglad_glGetTextureImage = (PFNGLGETTEXTUREIMAGEPROC)load(\"glGetTextureImage\");\n\tglad_glGetCompressedTextureImage = (PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)load(\"glGetCompressedTextureImage\");\n\tglad_glGetTextureLevelParameterfv = (PFNGLGETTEXTURELEVELPARAMETERFVPROC)load(\"glGetTextureLevelParameterfv\");\n\tglad_glGetTextureLevelParameteriv = (PFNGLGETTEXTURELEVELPARAMETERIVPROC)load(\"glGetTextureLevelParameteriv\");\n\tglad_glGetTextureParameterfv = (PFNGLGETTEXTUREPARAMETERFVPROC)load(\"glGetTextureParameterfv\");\n\tglad_glGetTextureParameterIiv = (PFNGLGETTEXTUREPARAMETERIIVPROC)load(\"glGetTextureParameterIiv\");\n\tglad_glGetTextureParameterIuiv = (PFNGLGETTEXTUREPARAMETERIUIVPROC)load(\"glGetTextureParameterIuiv\");\n\tglad_glGetTextureParameteriv = (PFNGLGETTEXTUREPARAMETERIVPROC)load(\"glGetTextureParameteriv\");\n\tglad_glCreateVertexArrays = (PFNGLCREATEVERTEXARRAYSPROC)load(\"glCreateVertexArrays\");\n\tglad_glDisableVertexArrayAttrib = (PFNGLDISABLEVERTEXARRAYATTRIBPROC)load(\"glDisableVertexArrayAttrib\");\n\tglad_glEnableVertexArrayAttrib = (PFNGLENABLEVERTEXARRAYATTRIBPROC)load(\"glEnableVertexArrayAttrib\");\n\tglad_glVertexArrayElementBuffer = (PFNGLVERTEXARRAYELEMENTBUFFERPROC)load(\"glVertexArrayElementBuffer\");\n\tglad_glVertexArrayVertexBuffer = (PFNGLVERTEXARRAYVERTEXBUFFERPROC)load(\"glVertexArrayVertexBuffer\");\n\tglad_glVertexArrayVertexBuffers = (PFNGLVERTEXARRAYVERTEXBUFFERSPROC)load(\"glVertexArrayVertexBuffers\");\n\tglad_glVertexArrayAttribBinding = (PFNGLVERTEXARRAYATTRIBBINDINGPROC)load(\"glVertexArrayAttribBinding\");\n\tglad_glVertexArrayAttribFormat = (PFNGLVERTEXARRAYATTRIBFORMATPROC)load(\"glVertexArrayAttribFormat\");\n\tglad_glVertexArrayAttribIFormat = (PFNGLVERTEXARRAYATTRIBIFORMATPROC)load(\"glVertexArrayAttribIFormat\");\n\tglad_glVertexArrayAttribLFormat = (PFNGLVERTEXARRAYATTRIBLFORMATPROC)load(\"glVertexArrayAttribLFormat\");\n\tglad_glVertexArrayBindingDivisor = (PFNGLVERTEXARRAYBINDINGDIVISORPROC)load(\"glVertexArrayBindingDivisor\");\n\tglad_glGetVertexArrayiv = (PFNGLGETVERTEXARRAYIVPROC)load(\"glGetVertexArrayiv\");\n\tglad_glGetVertexArrayIndexediv = (PFNGLGETVERTEXARRAYINDEXEDIVPROC)load(\"glGetVertexArrayIndexediv\");\n\tglad_glGetVertexArrayIndexed64iv = (PFNGLGETVERTEXARRAYINDEXED64IVPROC)load(\"glGetVertexArrayIndexed64iv\");\n\tglad_glCreateSamplers = (PFNGLCREATESAMPLERSPROC)load(\"glCreateSamplers\");\n\tglad_glCreateProgramPipelines = (PFNGLCREATEPROGRAMPIPELINESPROC)load(\"glCreateProgramPipelines\");\n\tglad_glCreateQueries = (PFNGLCREATEQUERIESPROC)load(\"glCreateQueries\");\n\tglad_glGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECTI64VPROC)load(\"glGetQueryBufferObjecti64v\");\n\tglad_glGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECTIVPROC)load(\"glGetQueryBufferObjectiv\");\n\tglad_glGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECTUI64VPROC)load(\"glGetQueryBufferObjectui64v\");\n\tglad_glGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECTUIVPROC)load(\"glGetQueryBufferObjectuiv\");\n\tglad_glMemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC)load(\"glMemoryBarrierByRegion\");\n\tglad_glGetTextureSubImage = (PFNGLGETTEXTURESUBIMAGEPROC)load(\"glGetTextureSubImage\");\n\tglad_glGetCompressedTextureSubImage = (PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)load(\"glGetCompressedTextureSubImage\");\n\tglad_glGetGraphicsResetStatus = (PFNGLGETGRAPHICSRESETSTATUSPROC)load(\"glGetGraphicsResetStatus\");\n\tglad_glGetnCompressedTexImage = (PFNGLGETNCOMPRESSEDTEXIMAGEPROC)load(\"glGetnCompressedTexImage\");\n\tglad_glGetnTexImage = (PFNGLGETNTEXIMAGEPROC)load(\"glGetnTexImage\");\n\tglad_glGetnUniformdv = (PFNGLGETNUNIFORMDVPROC)load(\"glGetnUniformdv\");\n\tglad_glGetnUniformfv = (PFNGLGETNUNIFORMFVPROC)load(\"glGetnUniformfv\");\n\tglad_glGetnUniformiv = (PFNGLGETNUNIFORMIVPROC)load(\"glGetnUniformiv\");\n\tglad_glGetnUniformuiv = (PFNGLGETNUNIFORMUIVPROC)load(\"glGetnUniformuiv\");\n\tglad_glReadnPixels = (PFNGLREADNPIXELSPROC)load(\"glReadnPixels\");\n\tglad_glGetnMapdv = (PFNGLGETNMAPDVPROC)load(\"glGetnMapdv\");\n\tglad_glGetnMapfv = (PFNGLGETNMAPFVPROC)load(\"glGetnMapfv\");\n\tglad_glGetnMapiv = (PFNGLGETNMAPIVPROC)load(\"glGetnMapiv\");\n\tglad_glGetnPixelMapfv = (PFNGLGETNPIXELMAPFVPROC)load(\"glGetnPixelMapfv\");\n\tglad_glGetnPixelMapuiv = (PFNGLGETNPIXELMAPUIVPROC)load(\"glGetnPixelMapuiv\");\n\tglad_glGetnPixelMapusv = (PFNGLGETNPIXELMAPUSVPROC)load(\"glGetnPixelMapusv\");\n\tglad_glGetnPolygonStipple = (PFNGLGETNPOLYGONSTIPPLEPROC)load(\"glGetnPolygonStipple\");\n\tglad_glGetnColorTable = (PFNGLGETNCOLORTABLEPROC)load(\"glGetnColorTable\");\n\tglad_glGetnConvolutionFilter = (PFNGLGETNCONVOLUTIONFILTERPROC)load(\"glGetnConvolutionFilter\");\n\tglad_glGetnSeparableFilter = (PFNGLGETNSEPARABLEFILTERPROC)load(\"glGetnSeparableFilter\");\n\tglad_glGetnHistogram = (PFNGLGETNHISTOGRAMPROC)load(\"glGetnHistogram\");\n\tglad_glGetnMinmax = (PFNGLGETNMINMAXPROC)load(\"glGetnMinmax\");\n\tglad_glTextureBarrier = (PFNGLTEXTUREBARRIERPROC)load(\"glTextureBarrier\");\n}\nstatic void load_GL_VERSION_4_6(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_6) return;\n\tglad_glSpecializeShader = (PFNGLSPECIALIZESHADERPROC)load(\"glSpecializeShader\");\n\tglad_glMultiDrawArraysIndirectCount = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)load(\"glMultiDrawArraysIndirectCount\");\n\tglad_glMultiDrawElementsIndirectCount = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)load(\"glMultiDrawElementsIndirectCount\");\n\tglad_glPolygonOffsetClamp = (PFNGLPOLYGONOFFSETCLAMPPROC)load(\"glPolygonOffsetClamp\");\n}\nstatic int find_extensionsGL(void) {\n\tif (!get_exts()) return 0;\n\t(void)&has_ext;\n\tfree_exts();\n\treturn 1;\n}\n\nstatic void find_coreGL(void) {\n\n    /* Thank you @elmindreda\n     * https://github.com/elmindreda/greg/blob/master/templates/greg.c.in#L176\n     * https://github.com/glfw/glfw/blob/master/src/context.c#L36\n     */\n    int i, major, minor;\n\n    const char* version;\n    const char* prefixes[] = {\n        \"OpenGL ES-CM \",\n        \"OpenGL ES-CL \",\n        \"OpenGL ES \",\n        NULL\n    };\n\n    version = (const char*) glGetString(GL_VERSION);\n    if (!version) return;\n\n    for (i = 0;  prefixes[i];  i++) {\n        const size_t length = strlen(prefixes[i]);\n        if (strncmp(version, prefixes[i], length) == 0) {\n            version += length;\n            break;\n        }\n    }\n\n/* PR #18 */\n#ifdef _MSC_VER\n    sscanf_s(version, \"%d.%d\", &major, &minor);\n#else\n    sscanf(version, \"%d.%d\", &major, &minor);\n#endif\n\n    GLVersion.major = major; GLVersion.minor = minor;\n    max_loaded_major = major; max_loaded_minor = minor;\n\tGLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1;\n\tGLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1;\n\tGLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1;\n\tGLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1;\n\tGLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1;\n\tGLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1;\n\tGLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2;\n\tGLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2;\n\tGLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3;\n\tGLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3;\n\tGLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3;\n\tGLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3;\n\tGLAD_GL_VERSION_4_0 = (major == 4 && minor >= 0) || major > 4;\n\tGLAD_GL_VERSION_4_1 = (major == 4 && minor >= 1) || major > 4;\n\tGLAD_GL_VERSION_4_2 = (major == 4 && minor >= 2) || major > 4;\n\tGLAD_GL_VERSION_4_3 = (major == 4 && minor >= 3) || major > 4;\n\tGLAD_GL_VERSION_4_4 = (major == 4 && minor >= 4) || major > 4;\n\tGLAD_GL_VERSION_4_5 = (major == 4 && minor >= 5) || major > 4;\n\tGLAD_GL_VERSION_4_6 = (major == 4 && minor >= 6) || major > 4;\n\tif (GLVersion.major > 4 || (GLVersion.major >= 4 && GLVersion.minor >= 6)) {\n\t\tmax_loaded_major = 4;\n\t\tmax_loaded_minor = 6;\n\t}\n}\n\nint gladLoadGLLoader(GLADloadproc load) {\n\tGLVersion.major = 0; GLVersion.minor = 0;\n\tglGetString = (PFNGLGETSTRINGPROC)load(\"glGetString\");\n\tif(glGetString == NULL) return 0;\n\tif(glGetString(GL_VERSION) == NULL) return 0;\n\tfind_coreGL();\n\tload_GL_VERSION_1_0(load);\n\tload_GL_VERSION_1_1(load);\n\tload_GL_VERSION_1_2(load);\n\tload_GL_VERSION_1_3(load);\n\tload_GL_VERSION_1_4(load);\n\tload_GL_VERSION_1_5(load);\n\tload_GL_VERSION_2_0(load);\n\tload_GL_VERSION_2_1(load);\n\tload_GL_VERSION_3_0(load);\n\tload_GL_VERSION_3_1(load);\n\tload_GL_VERSION_3_2(load);\n\tload_GL_VERSION_3_3(load);\n\tload_GL_VERSION_4_0(load);\n\tload_GL_VERSION_4_1(load);\n\tload_GL_VERSION_4_2(load);\n\tload_GL_VERSION_4_3(load);\n\tload_GL_VERSION_4_4(load);\n\tload_GL_VERSION_4_5(load);\n\tload_GL_VERSION_4_6(load);\n\n\tif (!find_extensionsGL()) return 0;\n\treturn GLVersion.major != 0 || GLVersion.minor != 0;\n}\n\n"
  },
  {
    "path": "extern/jsmn/jsmn.h",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2010 Serge Zaitsev\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n#ifndef JSMN_H\n#define JSMN_H\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef JSMN_STATIC\n#define JSMN_API static\n#else\n#define JSMN_API extern\n#endif\n\n/**\n * JSON type identifier. Basic types are:\n * \to Object\n * \to Array\n * \to String\n * \to Other primitive: number, boolean (true/false) or null\n */\ntypedef enum {\n  JSMN_UNDEFINED = 0,\n  JSMN_OBJECT = 1 << 0,\n  JSMN_ARRAY = 1 << 1,\n  JSMN_STRING = 1 << 2,\n  JSMN_PRIMITIVE = 1 << 3\n} jsmntype_t;\n\nenum jsmnerr {\n  /* Not enough tokens were provided */\n  JSMN_ERROR_NOMEM = -1,\n  /* Invalid character inside JSON string */\n  JSMN_ERROR_INVAL = -2,\n  /* The string is not a full JSON packet, more bytes expected */\n  JSMN_ERROR_PART = -3\n};\n\n/**\n * JSON token description.\n * type\t\ttype (object, array, string etc.)\n * start\tstart position in JSON data string\n * end\t\tend position in JSON data string\n */\ntypedef struct jsmntok {\n  jsmntype_t type;\n  int start;\n  int end;\n  int size;\n#ifdef JSMN_PARENT_LINKS\n  int parent;\n#endif\n} jsmntok_t;\n\n/**\n * JSON parser. Contains an array of token blocks available. Also stores\n * the string being parsed now and current position in that string.\n */\ntypedef struct jsmn_parser {\n  unsigned int pos;     /* offset in the JSON string */\n  unsigned int toknext; /* next token to allocate */\n  int toksuper;         /* superior token node, e.g. parent object or array */\n} jsmn_parser;\n\n/**\n * Create JSON parser over an array of tokens\n */\nJSMN_API void jsmn_init(jsmn_parser *parser);\n\n/**\n * Run JSON parser. It parses a JSON data string into and array of tokens, each\n * describing\n * a single JSON object.\n */\nJSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,\n                        jsmntok_t *tokens, const unsigned int num_tokens);\n\n#ifndef JSMN_HEADER\n/**\n * Allocates a fresh unused token from the token pool.\n */\nstatic jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,\n                                   const size_t num_tokens) {\n  jsmntok_t *tok;\n  if (parser->toknext >= num_tokens) {\n    return NULL;\n  }\n  tok = &tokens[parser->toknext++];\n  tok->start = tok->end = -1;\n  tok->size = 0;\n#ifdef JSMN_PARENT_LINKS\n  tok->parent = -1;\n#endif\n  return tok;\n}\n\n/**\n * Fills token type and boundaries.\n */\nstatic void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,\n                            const int start, const int end) {\n  token->type = type;\n  token->start = start;\n  token->end = end;\n  token->size = 0;\n}\n\n/**\n * Fills next available token with JSON primitive.\n */\nstatic int jsmn_parse_primitive(jsmn_parser *parser, const char *js,\n                                const size_t len, jsmntok_t *tokens,\n                                const size_t num_tokens) {\n  jsmntok_t *token;\n  int start;\n\n  start = parser->pos;\n\n  for (; parser->pos < len && js[parser->pos] != '\\0'; parser->pos++) {\n    switch (js[parser->pos]) {\n#ifndef JSMN_STRICT\n    /* In strict mode primitive must be followed by \",\" or \"}\" or \"]\" */\n    case ':':\n#endif\n    case '\\t':\n    case '\\r':\n    case '\\n':\n    case ' ':\n    case ',':\n    case ']':\n    case '}':\n      goto found;\n    default:\n                   /* to quiet a warning from gcc*/\n      break;\n    }\n    if (js[parser->pos] < 32 || js[parser->pos] >= 127) {\n      parser->pos = start;\n      return JSMN_ERROR_INVAL;\n    }\n  }\n#ifdef JSMN_STRICT\n  /* In strict mode primitive must be followed by a comma/object/array */\n  parser->pos = start;\n  return JSMN_ERROR_PART;\n#endif\n\nfound:\n  if (tokens == NULL) {\n    parser->pos--;\n    return 0;\n  }\n  token = jsmn_alloc_token(parser, tokens, num_tokens);\n  if (token == NULL) {\n    parser->pos = start;\n    return JSMN_ERROR_NOMEM;\n  }\n  jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);\n#ifdef JSMN_PARENT_LINKS\n  token->parent = parser->toksuper;\n#endif\n  parser->pos--;\n  return 0;\n}\n\n/**\n * Fills next token with JSON string.\n */\nstatic int jsmn_parse_string(jsmn_parser *parser, const char *js,\n                             const size_t len, jsmntok_t *tokens,\n                             const size_t num_tokens) {\n  jsmntok_t *token;\n\n  int start = parser->pos;\n  \n  /* Skip starting quote */\n  parser->pos++;\n  \n  for (; parser->pos < len && js[parser->pos] != '\\0'; parser->pos++) {\n    char c = js[parser->pos];\n\n    /* Quote: end of string */\n    if (c == '\\\"') {\n      if (tokens == NULL) {\n        return 0;\n      }\n      token = jsmn_alloc_token(parser, tokens, num_tokens);\n      if (token == NULL) {\n        parser->pos = start;\n        return JSMN_ERROR_NOMEM;\n      }\n      jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);\n#ifdef JSMN_PARENT_LINKS\n      token->parent = parser->toksuper;\n#endif\n      return 0;\n    }\n\n    /* Backslash: Quoted symbol expected */\n    if (c == '\\\\' && parser->pos + 1 < len) {\n      int i;\n      parser->pos++;\n      switch (js[parser->pos]) {\n      /* Allowed escaped symbols */\n      case '\\\"':\n      case '/':\n      case '\\\\':\n      case 'b':\n      case 'f':\n      case 'r':\n      case 'n':\n      case 't':\n        break;\n      /* Allows escaped symbol \\uXXXX */\n      case 'u':\n        parser->pos++;\n        for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\\0';\n             i++) {\n          /* If it isn't a hex character we have an error */\n          if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) ||   /* 0-9 */\n                (js[parser->pos] >= 65 && js[parser->pos] <= 70) ||   /* A-F */\n                (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */\n            parser->pos = start;\n            return JSMN_ERROR_INVAL;\n          }\n          parser->pos++;\n        }\n        parser->pos--;\n        break;\n      /* Unexpected symbol */\n      default:\n        parser->pos = start;\n        return JSMN_ERROR_INVAL;\n      }\n    }\n  }\n  parser->pos = start;\n  return JSMN_ERROR_PART;\n}\n\n/**\n * Parse JSON string and fill tokens.\n */\nJSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,\n                        jsmntok_t *tokens, const unsigned int num_tokens) {\n  int r;\n  int i;\n  jsmntok_t *token;\n  int count = parser->toknext;\n\n  for (; parser->pos < len && js[parser->pos] != '\\0'; parser->pos++) {\n    char c;\n    jsmntype_t type;\n\n    c = js[parser->pos];\n    switch (c) {\n    case '{':\n    case '[':\n      count++;\n      if (tokens == NULL) {\n        break;\n      }\n      token = jsmn_alloc_token(parser, tokens, num_tokens);\n      if (token == NULL) {\n        return JSMN_ERROR_NOMEM;\n      }\n      if (parser->toksuper != -1) {\n        jsmntok_t *t = &tokens[parser->toksuper];\n#ifdef JSMN_STRICT\n        /* In strict mode an object or array can't become a key */\n        if (t->type == JSMN_OBJECT) {\n          return JSMN_ERROR_INVAL;\n        }\n#endif\n        t->size++;\n#ifdef JSMN_PARENT_LINKS\n        token->parent = parser->toksuper;\n#endif\n      }\n      token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);\n      token->start = parser->pos;\n      parser->toksuper = parser->toknext - 1;\n      break;\n    case '}':\n    case ']':\n      if (tokens == NULL) {\n        break;\n      }\n      type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);\n#ifdef JSMN_PARENT_LINKS\n      if (parser->toknext < 1) {\n        return JSMN_ERROR_INVAL;\n      }\n      token = &tokens[parser->toknext - 1];\n      for (;;) {\n        if (token->start != -1 && token->end == -1) {\n          if (token->type != type) {\n            return JSMN_ERROR_INVAL;\n          }\n          token->end = parser->pos + 1;\n          parser->toksuper = token->parent;\n          break;\n        }\n        if (token->parent == -1) {\n          if (token->type != type || parser->toksuper == -1) {\n            return JSMN_ERROR_INVAL;\n          }\n          break;\n        }\n        token = &tokens[token->parent];\n      }\n#else\n      for (i = parser->toknext - 1; i >= 0; i--) {\n        token = &tokens[i];\n        if (token->start != -1 && token->end == -1) {\n          if (token->type != type) {\n            return JSMN_ERROR_INVAL;\n          }\n          parser->toksuper = -1;\n          token->end = parser->pos + 1;\n          break;\n        }\n      }\n      /* Error if unmatched closing bracket */\n      if (i == -1) {\n        return JSMN_ERROR_INVAL;\n      }\n      for (; i >= 0; i--) {\n        token = &tokens[i];\n        if (token->start != -1 && token->end == -1) {\n          parser->toksuper = i;\n          break;\n        }\n      }\n#endif\n      break;\n    case '\\\"':\n      r = jsmn_parse_string(parser, js, len, tokens, num_tokens);\n      if (r < 0) {\n        return r;\n      }\n      count++;\n      if (parser->toksuper != -1 && tokens != NULL) {\n        tokens[parser->toksuper].size++;\n      }\n      break;\n    case '\\t':\n    case '\\r':\n    case '\\n':\n    case ' ':\n      break;\n    case ':':\n      parser->toksuper = parser->toknext - 1;\n      break;\n    case ',':\n      if (tokens != NULL && parser->toksuper != -1 &&\n          tokens[parser->toksuper].type != JSMN_ARRAY &&\n          tokens[parser->toksuper].type != JSMN_OBJECT) {\n#ifdef JSMN_PARENT_LINKS\n        parser->toksuper = tokens[parser->toksuper].parent;\n#else\n        for (i = parser->toknext - 1; i >= 0; i--) {\n          if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {\n            if (tokens[i].start != -1 && tokens[i].end == -1) {\n              parser->toksuper = i;\n              break;\n            }\n          }\n        }\n#endif\n      }\n      break;\n#ifdef JSMN_STRICT\n    /* In strict mode primitives are: numbers and booleans */\n    case '-':\n    case '0':\n    case '1':\n    case '2':\n    case '3':\n    case '4':\n    case '5':\n    case '6':\n    case '7':\n    case '8':\n    case '9':\n    case 't':\n    case 'f':\n    case 'n':\n      /* And they must not be keys of the object */\n      if (tokens != NULL && parser->toksuper != -1) {\n        const jsmntok_t *t = &tokens[parser->toksuper];\n        if (t->type == JSMN_OBJECT ||\n            (t->type == JSMN_STRING && t->size != 0)) {\n          return JSMN_ERROR_INVAL;\n        }\n      }\n#else\n    /* In non-strict mode every unquoted value is a primitive */\n    default:\n#endif\n      r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);\n      if (r < 0) {\n        return r;\n      }\n      count++;\n      if (parser->toksuper != -1 && tokens != NULL) {\n        tokens[parser->toksuper].size++;\n      }\n      break;\n\n#ifdef JSMN_STRICT\n    /* Unexpected char in strict mode */\n    default:\n      return JSMN_ERROR_INVAL;\n#endif\n    }\n  }\n\n  if (tokens != NULL) {\n    for (i = parser->toknext - 1; i >= 0; i--) {\n      /* Unmatched opened object or array */\n      if (tokens[i].start != -1 && tokens[i].end == -1) {\n        return JSMN_ERROR_PART;\n      }\n    }\n  }\n\n  return count;\n}\n\n/**\n * Creates a new parser based over a given buffer with an array of tokens\n * available.\n */\nJSMN_API void jsmn_init(jsmn_parser *parser) {\n  parser->pos = 0;\n  parser->toknext = 0;\n  parser->toksuper = -1;\n}\n\n#endif /* JSMN_HEADER */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* JSMN_H */\n"
  },
  {
    "path": "include/box2d/base.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <stdint.h>\n\n// clang-format off\n// \n// Shared library macros\n#if defined( _MSC_VER ) && defined( box2d_EXPORTS )\n\t// build the Windows DLL\n\t#define BOX2D_EXPORT __declspec( dllexport )\n#elif defined( _MSC_VER ) && defined( BOX2D_DLL )\n\t// using the Windows DLL\n\t#define BOX2D_EXPORT __declspec( dllimport )\n#elif defined( box2d_EXPORTS )\n\t// building or using the shared library\n\t#define BOX2D_EXPORT __attribute__( ( visibility( \"default\" ) ) )\n#else\n\t// static library\n\t#define BOX2D_EXPORT\n#endif\n\n// C++ macros\n#ifdef __cplusplus\n\t#define B2_API extern \"C\" BOX2D_EXPORT\n\t#define B2_INLINE inline\n\t#define B2_LITERAL(T) T\n\t#define B2_ZERO_INIT {}\n#else\n\t#define B2_API BOX2D_EXPORT\n\t#define B2_INLINE static inline\n\t/// Used for C literals like (b2Vec2){1.0f, 2.0f} where C++ requires b2Vec2{1.0f, 2.0f}\n\t#define B2_LITERAL(T) (T)\n\t#define B2_ZERO_INIT {0}\n#endif\n// clang-format on\n\n/**\n * @defgroup base Base\n * Base functionality\n * @{\n */\n\n/// Prototype for user allocation function\n/// @param size the allocation size in bytes\n/// @param alignment the required alignment, guaranteed to be a power of 2\ntypedef void* b2AllocFcn( unsigned int size, int alignment );\n\n/// Prototype for user free function\n/// @param mem the memory previously allocated through `b2AllocFcn`\n/// @param size the allocation size in bytes\ntypedef void b2FreeFcn( void* mem, unsigned int size );\n\n/// Prototype for the user assert callback. Return 0 to skip the debugger break.\ntypedef int b2AssertFcn( const char* condition, const char* fileName, int lineNumber );\n\n/// Prototype for user log callback. Used to log warnings.\ntypedef void b2LogFcn( const char* message );\n\n/// This allows the user to override the allocation functions. These should be\n/// set during application startup.\nB2_API void b2SetAllocator( b2AllocFcn* allocFcn, b2FreeFcn* freeFcn );\n\n/// @return the total bytes allocated by Box2D\nB2_API int b2GetByteCount( void );\n\n/// Override the default assert function\n/// @param assertFcn a non-null assert callback\nB2_API void b2SetAssertFcn( b2AssertFcn* assertFcn );\n\n/// Override the default log function\n/// @param logFcn a non-null log callback\nB2_API void b2SetLogFcn( b2LogFcn* logFcn );\n\n/// Version numbering scheme.\n/// See https://semver.org/\ntypedef struct b2Version\n{\n\t/// Significant changes\n\tint major;\n\n\t/// Incremental changes\n\tint minor;\n\n\t/// Bug fixes\n\tint revision;\n} b2Version;\n\n/// Get the current version of Box2D\nB2_API b2Version b2GetVersion( void );\n\n/**@}*/\n\n//! @cond\n\n// see https://github.com/scottt/debugbreak\n#if defined( _MSC_VER )\n#define B2_BREAKPOINT __debugbreak()\n#elif defined( __GNUC__ ) || defined( __clang__ )\n#define B2_BREAKPOINT __builtin_trap()\n#else\n// Unknown compiler\n#include <assert.h>\n#define B2_BREAKPOINT assert( 0 )\n#endif\n\n#if !defined( NDEBUG ) || defined( B2_ENABLE_ASSERT )\nB2_API int b2InternalAssertFcn( const char* condition, const char* fileName, int lineNumber );\n#define B2_ASSERT( condition )                                                                                                   \\\n\tdo                                                                                                                           \\\n\t{                                                                                                                            \\\n\t\tif ( !( condition ) && b2InternalAssertFcn( #condition, __FILE__, (int)__LINE__ ) )                                          \\\n\t\t\tB2_BREAKPOINT;                                                                                                       \\\n\t}                                                                                                                            \\\n\twhile ( 0 )\n#else\n#define B2_ASSERT( ... ) ( (void)0 )\n#endif\n\n/// Get the absolute number of system ticks. The value is platform specific.\nB2_API uint64_t b2GetTicks( void );\n\n/// Get the milliseconds passed from an initial tick value.\nB2_API float b2GetMilliseconds( uint64_t ticks );\n\n/// Get the milliseconds passed from an initial tick value. Resets the passed in\n/// value to the current tick value.\nB2_API float b2GetMillisecondsAndReset( uint64_t* ticks );\n\n/// Yield to be used in a busy loop.\nB2_API void b2Yield( void );\n\n/// Simple djb2 hash function for determinism testing\n#define B2_HASH_INIT 5381\nB2_API uint32_t b2Hash( uint32_t hash, const uint8_t* data, int count );\n\n//! @endcond\n"
  },
  {
    "path": "include/box2d/box2d.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"base.h\"\n#include \"collision.h\"\n#include \"id.h\"\n#include \"types.h\"\n\n#include <stdbool.h>\n\n/**\n * @defgroup world World\n * These functions allow you to create a simulation world.\n *\n * You can add rigid bodies and joint constraints to the world and run the simulation. You can get contact\n * information to get contact points and normals as well as events. You can query to world, checking for overlaps and casting rays\n * or shapes. There is also debugging information such as debug draw, timing information, and counters. You can find documentation\n * here: https://box2d.org/\n * @{\n */\n\n/// Create a world for rigid body simulation. A world contains bodies, shapes, and constraints. You make create\n/// up to 128 worlds. Each world is completely independent and may be simulated in parallel.\n/// @return the world id.\nB2_API b2WorldId b2CreateWorld( const b2WorldDef* def );\n\n/// Destroy a world\nB2_API void b2DestroyWorld( b2WorldId worldId );\n\n/// World id validation. Provides validation for up to 64K allocations.\nB2_API bool b2World_IsValid( b2WorldId id );\n\n/// Simulate a world for one time step. This performs collision detection, integration, and constraint solution.\n/// @param worldId The world to simulate\n/// @param timeStep The amount of time to simulate, this should be a fixed number. Usually 1/60.\n/// @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Usually 4.\nB2_API void b2World_Step( b2WorldId worldId, float timeStep, int subStepCount );\n\n/// Call this to draw shapes and other debug draw data\nB2_API void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw );\n\n/// Get the body events for the current time step. The event data is transient. Do not store a reference to this data.\nB2_API b2BodyEvents b2World_GetBodyEvents( b2WorldId worldId );\n\n/// Get sensor events for the current time step. The event data is transient. Do not store a reference to this data.\nB2_API b2SensorEvents b2World_GetSensorEvents( b2WorldId worldId );\n\n/// Get contact events for this current time step. The event data is transient. Do not store a reference to this data.\nB2_API b2ContactEvents b2World_GetContactEvents( b2WorldId worldId );\n\n/// Get the joint events for the current time step. The event data is transient. Do not store a reference to this data.\nB2_API b2JointEvents b2World_GetJointEvents( b2WorldId worldId );\n\n/// Overlap test for all shapes that *potentially* overlap the provided AABB\nB2_API b2TreeStats b2World_OverlapAABB( b2WorldId worldId, b2AABB aabb, b2QueryFilter filter, b2OverlapResultFcn* fcn,\n\t\t\t\t\t\t\t\t\t\tvoid* context );\n\n/// Overlap test for all shapes that overlap the provided shape proxy.\nB2_API b2TreeStats b2World_OverlapShape( b2WorldId worldId, const b2ShapeProxy* proxy, b2QueryFilter filter,\n\t\t\t\t\t\t\t\t\t\t b2OverlapResultFcn* fcn, void* context );\n\n/// Cast a ray into the world to collect shapes in the path of the ray.\n/// Your callback function controls whether you get the closest point, any point, or n-points.\n/// @note The callback function may receive shapes in any order\n/// @param worldId The world to cast the ray against\n/// @param origin The start point of the ray\n/// @param translation The translation of the ray from the start point to the end point\n/// @param filter Contains bit flags to filter unwanted shapes from the results\n/// @param fcn A user implemented callback function\n/// @param context A user context that is passed along to the callback function\n///\t@return traversal performance counters\nB2_API b2TreeStats b2World_CastRay( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter,\n\t\t\t\t\t\t\t\t\tb2CastResultFcn* fcn, void* context );\n\n/// Cast a ray into the world to collect the closest hit. This is a convenience function. Ignores initial overlap.\n/// This is less general than b2World_CastRay() and does not allow for custom filtering.\nB2_API b2RayResult b2World_CastRayClosest( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter );\n\n/// Cast a shape through the world. Similar to a cast ray except that a shape is cast instead of a point.\n///\t@see b2World_CastRay\nB2_API b2TreeStats b2World_CastShape( b2WorldId worldId, const b2ShapeProxy* proxy, b2Vec2 translation, b2QueryFilter filter,\n\t\t\t\t\t\t\t\t\t  b2CastResultFcn* fcn, void* context );\n\n/// Cast a capsule mover through the world. This is a special shape cast that handles sliding along other shapes while reducing\n/// clipping.\nB2_API float b2World_CastMover( b2WorldId worldId, const b2Capsule* mover, b2Vec2 translation, b2QueryFilter filter );\n\n/// Collide a capsule mover with the world, gathering collision planes that can be fed to b2SolvePlanes. Useful for\n/// kinematic character movement.\nB2_API void b2World_CollideMover( b2WorldId worldId, const b2Capsule* mover, b2QueryFilter filter, b2PlaneResultFcn* fcn,\n\t\t\t\t\t\t\t\t  void* context );\n\n/// Enable/disable sleep. If your application does not need sleeping, you can gain some performance\n/// by disabling sleep completely at the world level.\n/// @see b2WorldDef\nB2_API void b2World_EnableSleeping( b2WorldId worldId, bool flag );\n\n/// Is body sleeping enabled?\nB2_API bool b2World_IsSleepingEnabled( b2WorldId worldId );\n\n/// Enable/disable continuous collision between dynamic and static bodies. Generally you should keep continuous\n/// collision enabled to prevent fast moving objects from going through static objects. The performance gain from\n/// disabling continuous collision is minor.\n/// @see b2WorldDef\nB2_API void b2World_EnableContinuous( b2WorldId worldId, bool flag );\n\n/// Is continuous collision enabled?\nB2_API bool b2World_IsContinuousEnabled( b2WorldId worldId );\n\n/// Adjust the restitution threshold. It is recommended not to make this value very small\n/// because it will prevent bodies from sleeping. Usually in meters per second.\n/// @see b2WorldDef\nB2_API void b2World_SetRestitutionThreshold( b2WorldId worldId, float value );\n\n/// Get the the restitution speed threshold. Usually in meters per second.\nB2_API float b2World_GetRestitutionThreshold( b2WorldId worldId );\n\n/// Adjust the hit event threshold. This controls the collision speed needed to generate a b2ContactHitEvent.\n/// Usually in meters per second.\n/// @see b2WorldDef::hitEventThreshold\nB2_API void b2World_SetHitEventThreshold( b2WorldId worldId, float value );\n\n/// Get the the hit event speed threshold. Usually in meters per second.\nB2_API float b2World_GetHitEventThreshold( b2WorldId worldId );\n\n/// Register the custom filter callback. This is optional.\nB2_API void b2World_SetCustomFilterCallback( b2WorldId worldId, b2CustomFilterFcn* fcn, void* context );\n\n/// Register the pre-solve callback. This is optional.\nB2_API void b2World_SetPreSolveCallback( b2WorldId worldId, b2PreSolveFcn* fcn, void* context );\n\n/// Set the gravity vector for the entire world. Box2D has no concept of an up direction and this\n/// is left as a decision for the application. Usually in m/s^2.\n/// @see b2WorldDef\nB2_API void b2World_SetGravity( b2WorldId worldId, b2Vec2 gravity );\n\n/// Get the gravity vector\nB2_API b2Vec2 b2World_GetGravity( b2WorldId worldId );\n\n/// Apply a radial explosion\n/// @param worldId The world id\n/// @param explosionDef The explosion definition\nB2_API void b2World_Explode( b2WorldId worldId, const b2ExplosionDef* explosionDef );\n\n/// Adjust contact tuning parameters\n/// @param worldId The world id\n/// @param hertz The contact stiffness (cycles per second)\n/// @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional)\n/// @param pushSpeed The maximum contact constraint push out speed (meters per second)\n/// @note Advanced feature\nB2_API void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushSpeed );\n\n/// Set the maximum linear speed. Usually in m/s.\nB2_API void b2World_SetMaximumLinearSpeed( b2WorldId worldId, float maximumLinearSpeed );\n\n/// Get the maximum linear speed. Usually in m/s.\nB2_API float b2World_GetMaximumLinearSpeed( b2WorldId worldId );\n\n/// Enable/disable constraint warm starting. Advanced feature for testing. Disabling\n/// warm starting greatly reduces stability and provides no performance gain.\nB2_API void b2World_EnableWarmStarting( b2WorldId worldId, bool flag );\n\n/// Is constraint warm starting enabled?\nB2_API bool b2World_IsWarmStartingEnabled( b2WorldId worldId );\n\n/// Get the number of awake bodies.\nB2_API int b2World_GetAwakeBodyCount( b2WorldId worldId );\n\n/// Get the current world performance profile\nB2_API b2Profile b2World_GetProfile( b2WorldId worldId );\n\n/// Get world counters and sizes\nB2_API b2Counters b2World_GetCounters( b2WorldId worldId );\n\n/// Set the user data pointer.\nB2_API void b2World_SetUserData( b2WorldId worldId, void* userData );\n\n/// Get the user data pointer.\nB2_API void* b2World_GetUserData( b2WorldId worldId );\n\n/// Set the friction callback. Passing NULL resets to default.\nB2_API void b2World_SetFrictionCallback( b2WorldId worldId, b2FrictionCallback* callback );\n\n/// Set the restitution callback. Passing NULL resets to default.\nB2_API void b2World_SetRestitutionCallback( b2WorldId worldId, b2RestitutionCallback* callback );\n\n/// Dump memory stats to box2d_memory.txt\nB2_API void b2World_DumpMemoryStats( b2WorldId worldId );\n\n/// This is for internal testing\nB2_API void b2World_RebuildStaticTree( b2WorldId worldId );\n\n/// This is for internal testing\nB2_API void b2World_EnableSpeculative( b2WorldId worldId, bool flag );\n\n/** @} */\n\n/**\n * @defgroup body Body\n * This is the body API.\n * @{\n */\n\n/// Create a rigid body given a definition. No reference to the definition is retained. So you can create the definition\n/// on the stack and pass it as a pointer.\n/// @code{.c}\n/// b2BodyDef bodyDef = b2DefaultBodyDef();\n/// b2BodyId myBodyId = b2CreateBody(myWorldId, &bodyDef);\n/// @endcode\n/// @warning This function is locked during callbacks.\nB2_API b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def );\n\n/// Destroy a rigid body given an id. This destroys all shapes and joints attached to the body.\n/// Do not keep references to the associated shapes and joints.\nB2_API void b2DestroyBody( b2BodyId bodyId );\n\n/// Body identifier validation. A valid body exists in a world and is non-null.\n/// This can be used to detect orphaned ids. Provides validation for up to 64K allocations.\nB2_API bool b2Body_IsValid( b2BodyId id );\n\n/// Get the body type: static, kinematic, or dynamic\nB2_API b2BodyType b2Body_GetType( b2BodyId bodyId );\n\n/// Change the body type. This is an expensive operation. This automatically updates the mass\n/// properties regardless of the automatic mass setting.\nB2_API void b2Body_SetType( b2BodyId bodyId, b2BodyType type );\n\n/// Set the body name. Up to 31 characters excluding 0 termination.\nB2_API void b2Body_SetName( b2BodyId bodyId, const char* name );\n\n/// Get the body name.\nB2_API const char* b2Body_GetName( b2BodyId bodyId );\n\n/// Set the user data for a body\nB2_API void b2Body_SetUserData( b2BodyId bodyId, void* userData );\n\n/// Get the user data stored in a body\nB2_API void* b2Body_GetUserData( b2BodyId bodyId );\n\n/// Get the world position of a body. This is the location of the body origin.\nB2_API b2Vec2 b2Body_GetPosition( b2BodyId bodyId );\n\n/// Get the world rotation of a body as a cosine/sine pair (complex number)\nB2_API b2Rot b2Body_GetRotation( b2BodyId bodyId );\n\n/// Get the world transform of a body.\nB2_API b2Transform b2Body_GetTransform( b2BodyId bodyId );\n\n/// Set the world transform of a body. This acts as a teleport and is fairly expensive.\n/// @note Generally you should create a body with then intended transform.\n/// @see b2BodyDef::position and b2BodyDef::rotation\nB2_API void b2Body_SetTransform( b2BodyId bodyId, b2Vec2 position, b2Rot rotation );\n\n/// Get a local point on a body given a world point\nB2_API b2Vec2 b2Body_GetLocalPoint( b2BodyId bodyId, b2Vec2 worldPoint );\n\n/// Get a world point on a body given a local point\nB2_API b2Vec2 b2Body_GetWorldPoint( b2BodyId bodyId, b2Vec2 localPoint );\n\n/// Get a local vector on a body given a world vector\nB2_API b2Vec2 b2Body_GetLocalVector( b2BodyId bodyId, b2Vec2 worldVector );\n\n/// Get a world vector on a body given a local vector\nB2_API b2Vec2 b2Body_GetWorldVector( b2BodyId bodyId, b2Vec2 localVector );\n\n/// Get the linear velocity of a body's center of mass. Usually in meters per second.\nB2_API b2Vec2 b2Body_GetLinearVelocity( b2BodyId bodyId );\n\n/// Get the angular velocity of a body in radians per second\nB2_API float b2Body_GetAngularVelocity( b2BodyId bodyId );\n\n/// Set the linear velocity of a body. Usually in meters per second.\nB2_API void b2Body_SetLinearVelocity( b2BodyId bodyId, b2Vec2 linearVelocity );\n\n/// Set the angular velocity of a body in radians per second\nB2_API void b2Body_SetAngularVelocity( b2BodyId bodyId, float angularVelocity );\n\n/// Set the velocity to reach the given transform after a given time step.\n/// The result will be close but maybe not exact. This is meant for kinematic bodies.\n/// The target is not applied if the velocity would be below the sleep threshold and\n/// the body is currently asleep.\n/// @param bodyId The body id\n/// @param target The target transform for the body\n/// @param timeStep The time step of the next call to b2World_Step\n/// @param wake Option to wake the body or not\nB2_API void b2Body_SetTargetTransform( b2BodyId bodyId, b2Transform target, float timeStep, bool wake );\n\n/// Get the linear velocity of a local point attached to a body. Usually in meters per second.\nB2_API b2Vec2 b2Body_GetLocalPointVelocity( b2BodyId bodyId, b2Vec2 localPoint );\n\n/// Get the linear velocity of a world point attached to a body. Usually in meters per second.\nB2_API b2Vec2 b2Body_GetWorldPointVelocity( b2BodyId bodyId, b2Vec2 worldPoint );\n\n/// Apply a force at a world point. If the force is not applied at the center of mass,\n/// it will generate a torque and affect the angular velocity. This optionally wakes up the body.\n/// The force is ignored if the body is not awake.\n/// @param bodyId The body id\n/// @param force The world force vector, usually in newtons (N)\n/// @param point The world position of the point of application\n/// @param wake Option to wake up the body\nB2_API void b2Body_ApplyForce( b2BodyId bodyId, b2Vec2 force, b2Vec2 point, bool wake );\n\n/// Apply a force to the center of mass. This optionally wakes up the body.\n/// The force is ignored if the body is not awake.\n/// @param bodyId The body id\n/// @param force the world force vector, usually in newtons (N).\n/// @param wake also wake up the body\nB2_API void b2Body_ApplyForceToCenter( b2BodyId bodyId, b2Vec2 force, bool wake );\n\n/// Apply a torque. This affects the angular velocity without affecting the linear velocity.\n/// This optionally wakes the body. The torque is ignored if the body is not awake.\n/// @param bodyId The body id\n/// @param torque about the z-axis (out of the screen), usually in N*m.\n/// @param wake also wake up the body\nB2_API void b2Body_ApplyTorque( b2BodyId bodyId, float torque, bool wake );\n\n/// Clear the force and torque on this body. Forces and torques are automatically cleared after each world\n/// step. So this only needs to be called if the application wants to remove the effect of previous\n/// calls to apply forces and torques before the world step is called.\n/// @param bodyId The body id\nB2_API void b2Body_ClearForces( b2BodyId bodyId );\n\n/// Apply an impulse at a point. This immediately modifies the velocity.\n/// It also modifies the angular velocity if the point of application\n/// is not at the center of mass. This optionally wakes the body.\n/// The impulse is ignored if the body is not awake.\n/// @param bodyId The body id\n/// @param impulse the world impulse vector, usually in N*s or kg*m/s.\n/// @param point the world position of the point of application.\n/// @param wake also wake up the body\n/// @warning This should be used for one-shot impulses. If you need a steady force,\n/// use a force instead, which will work better with the sub-stepping solver.\nB2_API void b2Body_ApplyLinearImpulse( b2BodyId bodyId, b2Vec2 impulse, b2Vec2 point, bool wake );\n\n/// Apply an impulse to the center of mass. This immediately modifies the velocity.\n/// The impulse is ignored if the body is not awake. This optionally wakes the body.\n/// @param bodyId The body id\n/// @param impulse the world impulse vector, usually in N*s or kg*m/s.\n/// @param wake also wake up the body\n/// @warning This should be used for one-shot impulses. If you need a steady force,\n/// use a force instead, which will work better with the sub-stepping solver.\nB2_API void b2Body_ApplyLinearImpulseToCenter( b2BodyId bodyId, b2Vec2 impulse, bool wake );\n\n/// Apply an angular impulse. The impulse is ignored if the body is not awake.\n/// This optionally wakes the body.\n/// @param bodyId The body id\n/// @param impulse the angular impulse, usually in units of kg*m*m/s\n/// @param wake also wake up the body\n/// @warning This should be used for one-shot impulses. If you need a steady torque,\n/// use a torque instead, which will work better with the sub-stepping solver.\nB2_API void b2Body_ApplyAngularImpulse( b2BodyId bodyId, float impulse, bool wake );\n\n/// Get the mass of the body, usually in kilograms\nB2_API float b2Body_GetMass( b2BodyId bodyId );\n\n/// Get the rotational inertia of the body, usually in kg*m^2\nB2_API float b2Body_GetRotationalInertia( b2BodyId bodyId );\n\n/// Get the center of mass position of the body in local space\nB2_API b2Vec2 b2Body_GetLocalCenterOfMass( b2BodyId bodyId );\n\n/// Get the center of mass position of the body in world space\nB2_API b2Vec2 b2Body_GetWorldCenterOfMass( b2BodyId bodyId );\n\n/// Override the body's mass properties. Normally this is computed automatically using the\n/// shape geometry and density. This information is lost if a shape is added or removed or if the\n/// body type changes.\nB2_API void b2Body_SetMassData( b2BodyId bodyId, b2MassData massData );\n\n/// Get the mass data for a body\nB2_API b2MassData b2Body_GetMassData( b2BodyId bodyId );\n\n/// This updates the mass properties to the sum of the mass properties of the shapes.\n/// This normally does not need to be called unless you called SetMassData to override\n/// the mass and you later want to reset the mass.\n/// You may also use this when automatic mass computation has been disabled.\n/// You should call this regardless of body type.\n/// Note that sensor shapes may have mass.\nB2_API void b2Body_ApplyMassFromShapes( b2BodyId bodyId );\n\n/// Adjust the linear damping. Normally this is set in b2BodyDef before creation.\nB2_API void b2Body_SetLinearDamping( b2BodyId bodyId, float linearDamping );\n\n/// Get the current linear damping.\nB2_API float b2Body_GetLinearDamping( b2BodyId bodyId );\n\n/// Adjust the angular damping. Normally this is set in b2BodyDef before creation.\nB2_API void b2Body_SetAngularDamping( b2BodyId bodyId, float angularDamping );\n\n/// Get the current angular damping.\nB2_API float b2Body_GetAngularDamping( b2BodyId bodyId );\n\n/// Adjust the gravity scale. Normally this is set in b2BodyDef before creation.\n/// @see b2BodyDef::gravityScale\nB2_API void b2Body_SetGravityScale( b2BodyId bodyId, float gravityScale );\n\n/// Get the current gravity scale\nB2_API float b2Body_GetGravityScale( b2BodyId bodyId );\n\n/// @return true if this body is awake\nB2_API bool b2Body_IsAwake( b2BodyId bodyId );\n\n/// Wake a body from sleep. This wakes the entire island the body is touching.\n/// @warning Putting a body to sleep will put the entire island of bodies touching this body to sleep,\n/// which can be expensive and possibly unintuitive.\nB2_API void b2Body_SetAwake( b2BodyId bodyId, bool awake );\n\n/// Wake bodies touching this body. Works for static bodies.\nB2_API void b2Body_WakeTouching( b2BodyId bodyId );\n\n/// Enable or disable sleeping for this body. If sleeping is disabled the body will wake.\nB2_API void b2Body_EnableSleep( b2BodyId bodyId, bool enableSleep );\n\n/// Returns true if sleeping is enabled for this body\nB2_API bool b2Body_IsSleepEnabled( b2BodyId bodyId );\n\n/// Set the sleep threshold, usually in meters per second\nB2_API void b2Body_SetSleepThreshold( b2BodyId bodyId, float sleepThreshold );\n\n/// Get the sleep threshold, usually in meters per second.\nB2_API float b2Body_GetSleepThreshold( b2BodyId bodyId );\n\n/// Returns true if this body is enabled\nB2_API bool b2Body_IsEnabled( b2BodyId bodyId );\n\n/// Disable a body by removing it completely from the simulation. This is expensive.\nB2_API void b2Body_Disable( b2BodyId bodyId );\n\n/// Enable a body by adding it to the simulation. This is expensive.\nB2_API void b2Body_Enable( b2BodyId bodyId );\n\n/// Set the motion locks on this body.\nB2_API void b2Body_SetMotionLocks( b2BodyId bodyId, b2MotionLocks locks );\n\n/// Get the motion locks for this body.\nB2_API b2MotionLocks b2Body_GetMotionLocks( b2BodyId bodyId );\n\n/// Set this body to be a bullet. A bullet does continuous collision detection\n/// against dynamic bodies (but not other bullets).\nB2_API void b2Body_SetBullet( b2BodyId bodyId, bool flag );\n\n/// Is this body a bullet?\nB2_API bool b2Body_IsBullet( b2BodyId bodyId );\n\n/// Enable/disable contact events on all shapes.\n/// @see b2ShapeDef::enableContactEvents\n/// @warning changing this at runtime may cause mismatched begin/end touch events\nB2_API void b2Body_EnableContactEvents( b2BodyId bodyId, bool flag );\n\n/// Enable/disable hit events on all shapes\n/// @see b2ShapeDef::enableHitEvents\nB2_API void b2Body_EnableHitEvents( b2BodyId bodyId, bool flag );\n\n/// Get the world that owns this body\nB2_API b2WorldId b2Body_GetWorld( b2BodyId bodyId );\n\n/// Get the number of shapes on this body\nB2_API int b2Body_GetShapeCount( b2BodyId bodyId );\n\n/// Get the shape ids for all shapes on this body, up to the provided capacity.\n/// @returns the number of shape ids stored in the user array\nB2_API int b2Body_GetShapes( b2BodyId bodyId, b2ShapeId* shapeArray, int capacity );\n\n/// Get the number of joints on this body\nB2_API int b2Body_GetJointCount( b2BodyId bodyId );\n\n/// Get the joint ids for all joints on this body, up to the provided capacity\n/// @returns the number of joint ids stored in the user array\nB2_API int b2Body_GetJoints( b2BodyId bodyId, b2JointId* jointArray, int capacity );\n\n/// Get the maximum capacity required for retrieving all the touching contacts on a body\nB2_API int b2Body_GetContactCapacity( b2BodyId bodyId );\n\n/// Get the touching contact data for a body.\n/// @note Box2D uses speculative collision so some contact points may be separated.\n/// @returns the number of elements filled in the provided array\n/// @warning do not ignore the return value, it specifies the valid number of elements\nB2_API int b2Body_GetContactData( b2BodyId bodyId, b2ContactData* contactData, int capacity );\n\n/// Get the current world AABB that contains all the attached shapes. Note that this may not encompass the body origin.\n/// If there are no shapes attached then the returned AABB is empty and centered on the body origin.\nB2_API b2AABB b2Body_ComputeAABB( b2BodyId bodyId );\n\n/** @} */\n\n/**\n * @defgroup shape Shape\n * Functions to create, destroy, and access.\n * Shapes bind raw geometry to bodies and hold material properties including friction and restitution.\n * @{\n */\n\n/// Create a circle shape and attach it to a body. The shape definition and geometry are fully cloned.\n/// Contacts are not created until the next time step.\n/// @return the shape id for accessing the shape\nB2_API b2ShapeId b2CreateCircleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Circle* circle );\n\n/// Create a line segment shape and attach it to a body. The shape definition and geometry are fully cloned.\n/// Contacts are not created until the next time step.\n/// @return the shape id for accessing the shape\nB2_API b2ShapeId b2CreateSegmentShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Segment* segment );\n\n/// Create a capsule shape and attach it to a body. The shape definition and geometry are fully cloned.\n/// Contacts are not created until the next time step.\n/// @return the shape id for accessing the shape, this will be b2_nullShapeId if the length is too small.\nB2_API b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Capsule* capsule );\n\n/// Create a polygon shape and attach it to a body. The shape definition and geometry are fully cloned.\n/// Contacts are not created until the next time step.\n/// @return the shape id for accessing the shape\nB2_API b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon );\n\n/// Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a\n///\tbody are destroyed at once.\n///\t@see b2Body_ApplyMassFromShapes\nB2_API void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass );\n\n/// Shape identifier validation. Provides validation for up to 64K allocations.\nB2_API bool b2Shape_IsValid( b2ShapeId id );\n\n/// Get the type of a shape\nB2_API b2ShapeType b2Shape_GetType( b2ShapeId shapeId );\n\n/// Get the id of the body that a shape is attached to\nB2_API b2BodyId b2Shape_GetBody( b2ShapeId shapeId );\n\n/// Get the world that owns this shape\nB2_API b2WorldId b2Shape_GetWorld( b2ShapeId shapeId );\n\n/// Returns true if the shape is a sensor. It is not possible to change a shape\n/// from sensor to solid dynamically because this breaks the contract for\n/// sensor events.\nB2_API bool b2Shape_IsSensor( b2ShapeId shapeId );\n\n/// Set the user data for a shape\nB2_API void b2Shape_SetUserData( b2ShapeId shapeId, void* userData );\n\n/// Get the user data for a shape. This is useful when you get a shape id\n/// from an event or query.\nB2_API void* b2Shape_GetUserData( b2ShapeId shapeId );\n\n/// Set the mass density of a shape, usually in kg/m^2.\n/// This will optionally update the mass properties on the parent body.\n/// @see b2ShapeDef::density, b2Body_ApplyMassFromShapes\nB2_API void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass );\n\n/// Get the density of a shape, usually in kg/m^2\nB2_API float b2Shape_GetDensity( b2ShapeId shapeId );\n\n/// Set the friction on a shape\nB2_API void b2Shape_SetFriction( b2ShapeId shapeId, float friction );\n\n/// Get the friction of a shape\nB2_API float b2Shape_GetFriction( b2ShapeId shapeId );\n\n/// Set the shape restitution (bounciness)\nB2_API void b2Shape_SetRestitution( b2ShapeId shapeId, float restitution );\n\n/// Get the shape restitution\nB2_API float b2Shape_GetRestitution( b2ShapeId shapeId );\n\n/// Set the user material identifier\nB2_API void b2Shape_SetUserMaterial( b2ShapeId shapeId, uint64_t material );\n\n/// Get the user material identifier\nB2_API uint64_t b2Shape_GetUserMaterial( b2ShapeId shapeId );\n\n/// Set the shape surface material\nB2_API void b2Shape_SetSurfaceMaterial( b2ShapeId shapeId, const b2SurfaceMaterial* surfaceMaterial );\n\n/// Get the shape surface material\nB2_API b2SurfaceMaterial b2Shape_GetSurfaceMaterial( b2ShapeId shapeId );\n\n/// Get the shape filter\nB2_API b2Filter b2Shape_GetFilter( b2ShapeId shapeId );\n\n/// Set the current filter. This is almost as expensive as recreating the shape. This may cause\n/// contacts to be immediately destroyed. However contacts are not created until the next world step.\n/// Sensor overlap state is also not updated until the next world step.\n/// @see b2ShapeDef::filter\nB2_API void b2Shape_SetFilter( b2ShapeId shapeId, b2Filter filter );\n\n/// Enable sensor events for this shape.\n/// @see b2ShapeDef::enableSensorEvents\nB2_API void b2Shape_EnableSensorEvents( b2ShapeId shapeId, bool flag );\n\n/// Returns true if sensor events are enabled.\nB2_API bool b2Shape_AreSensorEventsEnabled( b2ShapeId shapeId );\n\n/// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.\n/// @see b2ShapeDef::enableContactEvents\n/// @warning changing this at run-time may lead to lost begin/end events\nB2_API void b2Shape_EnableContactEvents( b2ShapeId shapeId, bool flag );\n\n/// Returns true if contact events are enabled\nB2_API bool b2Shape_AreContactEventsEnabled( b2ShapeId shapeId );\n\n/// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive\n/// and must be carefully handled due to multithreading. Ignored for sensors.\n/// @see b2PreSolveFcn\nB2_API void b2Shape_EnablePreSolveEvents( b2ShapeId shapeId, bool flag );\n\n/// Returns true if pre-solve events are enabled\nB2_API bool b2Shape_ArePreSolveEventsEnabled( b2ShapeId shapeId );\n\n/// Enable contact hit events for this shape. Ignored for sensors.\n/// @see b2WorldDef.hitEventThreshold\nB2_API void b2Shape_EnableHitEvents( b2ShapeId shapeId, bool flag );\n\n/// Returns true if hit events are enabled\nB2_API bool b2Shape_AreHitEventsEnabled( b2ShapeId shapeId );\n\n/// Test a point for overlap with a shape\nB2_API bool b2Shape_TestPoint( b2ShapeId shapeId, b2Vec2 point );\n\n/// Ray cast a shape directly\nB2_API b2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input );\n\n/// Get a copy of the shape's circle. Asserts the type is correct.\nB2_API b2Circle b2Shape_GetCircle( b2ShapeId shapeId );\n\n/// Get a copy of the shape's line segment. Asserts the type is correct.\nB2_API b2Segment b2Shape_GetSegment( b2ShapeId shapeId );\n\n/// Get a copy of the shape's chain segment. These come from chain shapes.\n/// Asserts the type is correct.\nB2_API b2ChainSegment b2Shape_GetChainSegment( b2ShapeId shapeId );\n\n/// Get a copy of the shape's capsule. Asserts the type is correct.\nB2_API b2Capsule b2Shape_GetCapsule( b2ShapeId shapeId );\n\n/// Get a copy of the shape's convex polygon. Asserts the type is correct.\nB2_API b2Polygon b2Shape_GetPolygon( b2ShapeId shapeId );\n\n/// Allows you to change a shape to be a circle or update the current circle.\n/// This does not modify the mass properties.\n/// @see b2Body_ApplyMassFromShapes\nB2_API void b2Shape_SetCircle( b2ShapeId shapeId, const b2Circle* circle );\n\n/// Allows you to change a shape to be a capsule or update the current capsule.\n/// This does not modify the mass properties.\n/// @see b2Body_ApplyMassFromShapes\nB2_API void b2Shape_SetCapsule( b2ShapeId shapeId, const b2Capsule* capsule );\n\n/// Allows you to change a shape to be a segment or update the current segment.\nB2_API void b2Shape_SetSegment( b2ShapeId shapeId, const b2Segment* segment );\n\n/// Allows you to change a shape to be a polygon or update the current polygon.\n/// This does not modify the mass properties.\n/// @see b2Body_ApplyMassFromShapes\nB2_API void b2Shape_SetPolygon( b2ShapeId shapeId, const b2Polygon* polygon );\n\n/// Get the parent chain id if the shape type is a chain segment, otherwise\n/// returns b2_nullChainId.\nB2_API b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId );\n\n/// Get the maximum capacity required for retrieving all the touching contacts on a shape\nB2_API int b2Shape_GetContactCapacity( b2ShapeId shapeId );\n\n/// Get the touching contact data for a shape. The provided shapeId will be either shapeIdA or shapeIdB on the contact data.\n/// @note Box2D uses speculative collision so some contact points may be separated.\n/// @returns the number of elements filled in the provided array\n/// @warning do not ignore the return value, it specifies the valid number of elements\nB2_API int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int capacity );\n\n/// Get the maximum capacity required for retrieving all the overlapped shapes on a sensor shape.\n/// This returns 0 if the provided shape is not a sensor.\n/// @param shapeId the id of a sensor shape\n/// @returns the required capacity to get all the overlaps in b2Shape_GetSensorOverlaps\nB2_API int b2Shape_GetSensorCapacity( b2ShapeId shapeId );\n\n/// Get the overlap data for a sensor shape.\n/// @param shapeId the id of a sensor shape\n/// @param visitorIds a user allocated array that is filled with the overlapping shapes (visitors)\n/// @param capacity the capacity of overlappedShapes\n/// @returns the number of elements filled in the provided array\n/// @warning do not ignore the return value, it specifies the valid number of elements\n/// @warning overlaps may contain destroyed shapes so use b2Shape_IsValid to confirm each overlap\nB2_API int b2Shape_GetSensorData( b2ShapeId shapeId, b2ShapeId* visitorIds, int capacity );\n\n/// Get the current world AABB\nB2_API b2AABB b2Shape_GetAABB( b2ShapeId shapeId );\n\n/// Compute the mass data for a shape\nB2_API b2MassData b2Shape_ComputeMassData( b2ShapeId shapeId );\n\n/// Get the closest point on a shape to a target point. Target and result are in world space.\n/// todo need sample\nB2_API b2Vec2 b2Shape_GetClosestPoint( b2ShapeId shapeId, b2Vec2 target );\n\n/// Apply a wind force to the body for this shape using the density of air. This considers\n/// the projected area of the shape in the wind direction. This also considers\n/// the relative velocity of the shape.\n/// @param shapeId the shape id\n/// @param wind the wind velocity in world space\n/// @param drag the drag coefficient, the force that opposes the relative velocity\n/// @param lift the lift coefficient, the force that is perpendicular to the relative velocity\n/// @param wake should this wake the body\nB2_API void b2Shape_ApplyWind( b2ShapeId shapeId, b2Vec2 wind, float drag, float lift, bool wake );\n\n/// Chain Shape\n\n/// Create a chain shape\n/// @see b2ChainDef for details\nB2_API b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def );\n\n/// Destroy a chain shape\nB2_API void b2DestroyChain( b2ChainId chainId );\n\n/// Get the world that owns this chain shape\nB2_API b2WorldId b2Chain_GetWorld( b2ChainId chainId );\n\n/// Get the number of segments on this chain\nB2_API int b2Chain_GetSegmentCount( b2ChainId chainId );\n\n/// Fill a user array with chain segment shape ids up to the specified capacity. Returns\n/// the actual number of segments returned.\nB2_API int b2Chain_GetSegments( b2ChainId chainId, b2ShapeId* segmentArray, int capacity );\n\n/// Get the number of materials used on this chain. Must be 1 or the number of segments.\nB2_API int b2Chain_GetSurfaceMaterialCount( b2ChainId chainId );\n\n/// Set a chain material. If the chain has only one material, this material is applied to all\n/// segments. Otherwise it is applied to a single segment.\nB2_API void b2Chain_SetSurfaceMaterial( b2ChainId chainId, const b2SurfaceMaterial* material, int materialIndex );\n\n/// Get a chain material by index.\nB2_API b2SurfaceMaterial b2Chain_GetSurfaceMaterial( b2ChainId chainId, int materialIndex );\n\n/// Chain identifier validation. Provides validation for up to 64K allocations.\nB2_API bool b2Chain_IsValid( b2ChainId id );\n\n/** @} */\n\n/**\n * @defgroup joint Joint\n * @brief Joints allow you to connect rigid bodies together while allowing various forms of relative motions.\n * @{\n */\n\n/// Destroy a joint. Optionally wake attached bodies.\nB2_API void b2DestroyJoint( b2JointId jointId, bool wakeAttached );\n\n/// Joint identifier validation. Provides validation for up to 64K allocations.\nB2_API bool b2Joint_IsValid( b2JointId id );\n\n/// Get the joint type\nB2_API b2JointType b2Joint_GetType( b2JointId jointId );\n\n/// Get body A id on a joint\nB2_API b2BodyId b2Joint_GetBodyA( b2JointId jointId );\n\n/// Get body B id on a joint\nB2_API b2BodyId b2Joint_GetBodyB( b2JointId jointId );\n\n/// Get the world that owns this joint\nB2_API b2WorldId b2Joint_GetWorld( b2JointId jointId );\n\n/// Set the local frame on bodyA\nB2_API void b2Joint_SetLocalFrameA( b2JointId jointId, b2Transform localFrame );\n\n/// Get the local frame on bodyA\nB2_API b2Transform b2Joint_GetLocalFrameA( b2JointId jointId );\n\n/// Set the local frame on bodyB\nB2_API void b2Joint_SetLocalFrameB( b2JointId jointId, b2Transform localFrame );\n\n/// Get the local frame on bodyB\nB2_API b2Transform b2Joint_GetLocalFrameB( b2JointId jointId );\n\n/// Toggle collision between connected bodies\nB2_API void b2Joint_SetCollideConnected( b2JointId jointId, bool shouldCollide );\n\n/// Is collision allowed between connected bodies?\nB2_API bool b2Joint_GetCollideConnected( b2JointId jointId );\n\n/// Set the user data on a joint\nB2_API void b2Joint_SetUserData( b2JointId jointId, void* userData );\n\n/// Get the user data on a joint\nB2_API void* b2Joint_GetUserData( b2JointId jointId );\n\n/// Wake the bodies connect to this joint\nB2_API void b2Joint_WakeBodies( b2JointId jointId );\n\n/// Get the current constraint force for this joint. Usually in Newtons.\nB2_API b2Vec2 b2Joint_GetConstraintForce( b2JointId jointId );\n\n/// Get the current constraint torque for this joint. Usually in Newton * meters.\nB2_API float b2Joint_GetConstraintTorque( b2JointId jointId );\n\n/// Get the current linear separation error for this joint. Does not consider admissible movement. Usually in meters.\nB2_API float b2Joint_GetLinearSeparation( b2JointId jointId );\n\n/// Get the current angular separation error for this joint. Does not consider admissible movement. Usually in meters.\nB2_API float b2Joint_GetAngularSeparation( b2JointId jointId );\n\n/// Set the joint constraint tuning. Advanced feature.\n/// @param jointId the joint\n/// @param hertz the stiffness in Hertz (cycles per second)\n/// @param dampingRatio the non-dimensional damping ratio (one for critical damping)\nB2_API void b2Joint_SetConstraintTuning( b2JointId jointId, float hertz, float dampingRatio );\n\n/// Get the joint constraint tuning. Advanced feature.\nB2_API void b2Joint_GetConstraintTuning( b2JointId jointId, float* hertz, float* dampingRatio );\n\n/// Set the force threshold for joint events (Newtons)\nB2_API void b2Joint_SetForceThreshold( b2JointId jointId, float threshold );\n\n/// Get the force threshold for joint events (Newtons)\nB2_API float b2Joint_GetForceThreshold( b2JointId jointId );\n\n/// Set the torque threshold for joint events (N-m)\nB2_API void b2Joint_SetTorqueThreshold( b2JointId jointId, float threshold );\n\n/// Get the torque threshold for joint events (N-m)\nB2_API float b2Joint_GetTorqueThreshold( b2JointId jointId );\n\n/**\n * @defgroup distance_joint Distance Joint\n * @brief Functions for the distance joint.\n * @{\n */\n\n/// Create a distance joint\n/// @see b2DistanceJointDef for details\nB2_API b2JointId b2CreateDistanceJoint( b2WorldId worldId, const b2DistanceJointDef* def );\n\n/// Set the rest length of a distance joint\n/// @param jointId The id for a distance joint\n/// @param length The new distance joint length\nB2_API void b2DistanceJoint_SetLength( b2JointId jointId, float length );\n\n/// Get the rest length of a distance joint\nB2_API float b2DistanceJoint_GetLength( b2JointId jointId );\n\n/// Enable/disable the distance joint spring. When disabled the distance joint is rigid.\nB2_API void b2DistanceJoint_EnableSpring( b2JointId jointId, bool enableSpring );\n\n/// Is the distance joint spring enabled?\nB2_API bool b2DistanceJoint_IsSpringEnabled( b2JointId jointId );\n\n/// Set the force range for the spring.\nB2_API void b2DistanceJoint_SetSpringForceRange( b2JointId jointId, float lowerForce, float upperForce );\n\n/// Get the force range for the spring.\nB2_API void b2DistanceJoint_GetSpringForceRange( b2JointId jointId, float* lowerForce, float* upperForce );\n\n/// Set the spring stiffness in Hertz\nB2_API void b2DistanceJoint_SetSpringHertz( b2JointId jointId, float hertz );\n\n/// Set the spring damping ratio, non-dimensional\nB2_API void b2DistanceJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio );\n\n/// Get the spring Hertz\nB2_API float b2DistanceJoint_GetSpringHertz( b2JointId jointId );\n\n/// Get the spring damping ratio\nB2_API float b2DistanceJoint_GetSpringDampingRatio( b2JointId jointId );\n\n/// Enable joint limit. The limit only works if the joint spring is enabled. Otherwise the joint is rigid\n/// and the limit has no effect.\nB2_API void b2DistanceJoint_EnableLimit( b2JointId jointId, bool enableLimit );\n\n/// Is the distance joint limit enabled?\nB2_API bool b2DistanceJoint_IsLimitEnabled( b2JointId jointId );\n\n/// Set the minimum and maximum length parameters of a distance joint\nB2_API void b2DistanceJoint_SetLengthRange( b2JointId jointId, float minLength, float maxLength );\n\n/// Get the distance joint minimum length\nB2_API float b2DistanceJoint_GetMinLength( b2JointId jointId );\n\n/// Get the distance joint maximum length\nB2_API float b2DistanceJoint_GetMaxLength( b2JointId jointId );\n\n/// Get the current length of a distance joint\nB2_API float b2DistanceJoint_GetCurrentLength( b2JointId jointId );\n\n/// Enable/disable the distance joint motor\nB2_API void b2DistanceJoint_EnableMotor( b2JointId jointId, bool enableMotor );\n\n/// Is the distance joint motor enabled?\nB2_API bool b2DistanceJoint_IsMotorEnabled( b2JointId jointId );\n\n/// Set the distance joint motor speed, usually in meters per second\nB2_API void b2DistanceJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed );\n\n/// Get the distance joint motor speed, usually in meters per second\nB2_API float b2DistanceJoint_GetMotorSpeed( b2JointId jointId );\n\n/// Set the distance joint maximum motor force, usually in newtons\nB2_API void b2DistanceJoint_SetMaxMotorForce( b2JointId jointId, float force );\n\n/// Get the distance joint maximum motor force, usually in newtons\nB2_API float b2DistanceJoint_GetMaxMotorForce( b2JointId jointId );\n\n/// Get the distance joint current motor force, usually in newtons\nB2_API float b2DistanceJoint_GetMotorForce( b2JointId jointId );\n\n/** @} */\n\n/**\n * @defgroup motor_joint Motor Joint\n * @brief Functions for the motor joint.\n *\n * The motor joint is designed to control the movement of a body while still being\n * responsive to collisions. A spring controls the position and rotation. A velocity motor\n * can be used to control velocity and allows for friction in top-down games. Both types\n * of control can be combined. For example, you can have a spring with friction.\n * Position and velocity control have force and torque limits.\n * @{\n */\n\n/// Create a motor joint\n/// @see b2MotorJointDef for details\nB2_API b2JointId b2CreateMotorJoint( b2WorldId worldId, const b2MotorJointDef* def );\n\n/// Set the desired relative linear velocity in meters per second\nB2_API void b2MotorJoint_SetLinearVelocity( b2JointId jointId, b2Vec2 velocity );\n\n/// Get the desired relative linear velocity in meters per second\nB2_API b2Vec2 b2MotorJoint_GetLinearVelocity( b2JointId jointId );\n\n/// Set the desired relative angular velocity in radians per second\nB2_API void b2MotorJoint_SetAngularVelocity( b2JointId jointId, float velocity );\n\n/// Get the desired relative angular velocity in radians per second\nB2_API float b2MotorJoint_GetAngularVelocity( b2JointId jointId );\n\n/// Set the motor joint maximum force, usually in newtons\nB2_API void b2MotorJoint_SetMaxVelocityForce( b2JointId jointId, float maxForce );\n\n/// Get the motor joint maximum force, usually in newtons\nB2_API float b2MotorJoint_GetMaxVelocityForce( b2JointId jointId );\n\n/// Set the motor joint maximum torque, usually in newton-meters\nB2_API void b2MotorJoint_SetMaxVelocityTorque( b2JointId jointId, float maxTorque );\n\n/// Get the motor joint maximum torque, usually in newton-meters\nB2_API float b2MotorJoint_GetMaxVelocityTorque( b2JointId jointId );\n\n/// Set the spring linear hertz stiffness\nB2_API void b2MotorJoint_SetLinearHertz( b2JointId jointId, float hertz );\n\n/// Get the spring linear hertz stiffness\nB2_API float b2MotorJoint_GetLinearHertz( b2JointId jointId );\n\n/// Set the spring linear damping ratio. Use 1.0 for critical damping.\nB2_API void b2MotorJoint_SetLinearDampingRatio( b2JointId jointId, float damping );\n\n/// Get the spring linear damping ratio.\nB2_API float b2MotorJoint_GetLinearDampingRatio( b2JointId jointId );\n\n/// Set the spring angular hertz stiffness\nB2_API void b2MotorJoint_SetAngularHertz( b2JointId jointId, float hertz );\n\n/// Get the spring angular hertz stiffness\nB2_API float b2MotorJoint_GetAngularHertz( b2JointId jointId );\n\n/// Set the spring angular damping ratio. Use 1.0 for critical damping.\nB2_API void b2MotorJoint_SetAngularDampingRatio( b2JointId jointId, float damping );\n\n/// Get the spring angular damping ratio.\nB2_API float b2MotorJoint_GetAngularDampingRatio( b2JointId jointId );\n\n/// Set the maximum spring force in newtons.\nB2_API void b2MotorJoint_SetMaxSpringForce( b2JointId jointId, float maxForce );\n\n/// Get the maximum spring force in newtons.\nB2_API float b2MotorJoint_GetMaxSpringForce( b2JointId jointId );\n\n/// Set the maximum spring torque in newtons * meters\nB2_API void b2MotorJoint_SetMaxSpringTorque( b2JointId jointId, float maxTorque );\n\n/// Get the maximum spring torque in newtons * meters\nB2_API float b2MotorJoint_GetMaxSpringTorque( b2JointId jointId );\n\n/**@}*/\n\n/**\n * @defgroup filter_joint Filter Joint\n * @brief Functions for the filter joint.\n *\n * The filter joint is used to disable collision between two bodies. As a side effect of being a joint, it also\n * keeps the two bodies in the same simulation island.\n * @{\n */\n\n/// Create a filter joint.\n/// @see b2FilterJointDef for details\nB2_API b2JointId b2CreateFilterJoint( b2WorldId worldId, const b2FilterJointDef* def );\n\n/**@}*/\n\n/**\n * @defgroup prismatic_joint Prismatic Joint\n * @brief A prismatic joint allows for translation along a single axis with no rotation.\n *\n * The prismatic joint is useful for things like pistons and moving platforms, where you want a body to translate\n * along an axis and have no rotation. Also called a *slider* joint.\n * @{\n */\n\n/// Create a prismatic (slider) joint.\n/// @see b2PrismaticJointDef for details\nB2_API b2JointId b2CreatePrismaticJoint( b2WorldId worldId, const b2PrismaticJointDef* def );\n\n/// Enable/disable the joint spring.\nB2_API void b2PrismaticJoint_EnableSpring( b2JointId jointId, bool enableSpring );\n\n/// Is the prismatic joint spring enabled or not?\nB2_API bool b2PrismaticJoint_IsSpringEnabled( b2JointId jointId );\n\n/// Set the prismatic joint stiffness in Hertz.\n/// This should usually be less than a quarter of the simulation rate. For example, if the simulation\n/// runs at 60Hz then the joint stiffness should be 15Hz or less.\nB2_API void b2PrismaticJoint_SetSpringHertz( b2JointId jointId, float hertz );\n\n/// Get the prismatic joint stiffness in Hertz\nB2_API float b2PrismaticJoint_GetSpringHertz( b2JointId jointId );\n\n/// Set the prismatic joint damping ratio (non-dimensional)\nB2_API void b2PrismaticJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio );\n\n/// Get the prismatic spring damping ratio (non-dimensional)\nB2_API float b2PrismaticJoint_GetSpringDampingRatio( b2JointId jointId );\n\n/// Set the prismatic joint spring target angle, usually in meters\nB2_API void b2PrismaticJoint_SetTargetTranslation( b2JointId jointId, float translation );\n\n/// Get the prismatic joint spring target translation, usually in meters\nB2_API float b2PrismaticJoint_GetTargetTranslation( b2JointId jointId );\n\n/// Enable/disable a prismatic joint limit\nB2_API void b2PrismaticJoint_EnableLimit( b2JointId jointId, bool enableLimit );\n\n/// Is the prismatic joint limit enabled?\nB2_API bool b2PrismaticJoint_IsLimitEnabled( b2JointId jointId );\n\n/// Get the prismatic joint lower limit\nB2_API float b2PrismaticJoint_GetLowerLimit( b2JointId jointId );\n\n/// Get the prismatic joint upper limit\nB2_API float b2PrismaticJoint_GetUpperLimit( b2JointId jointId );\n\n/// Set the prismatic joint limits\nB2_API void b2PrismaticJoint_SetLimits( b2JointId jointId, float lower, float upper );\n\n/// Enable/disable a prismatic joint motor\nB2_API void b2PrismaticJoint_EnableMotor( b2JointId jointId, bool enableMotor );\n\n/// Is the prismatic joint motor enabled?\nB2_API bool b2PrismaticJoint_IsMotorEnabled( b2JointId jointId );\n\n/// Set the prismatic joint motor speed, usually in meters per second\nB2_API void b2PrismaticJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed );\n\n/// Get the prismatic joint motor speed, usually in meters per second\nB2_API float b2PrismaticJoint_GetMotorSpeed( b2JointId jointId );\n\n/// Set the prismatic joint maximum motor force, usually in newtons\nB2_API void b2PrismaticJoint_SetMaxMotorForce( b2JointId jointId, float force );\n\n/// Get the prismatic joint maximum motor force, usually in newtons\nB2_API float b2PrismaticJoint_GetMaxMotorForce( b2JointId jointId );\n\n/// Get the prismatic joint current motor force, usually in newtons\nB2_API float b2PrismaticJoint_GetMotorForce( b2JointId jointId );\n\n/// Get the current joint translation, usually in meters.\nB2_API float b2PrismaticJoint_GetTranslation( b2JointId jointId );\n\n/// Get the current joint translation speed, usually in meters per second.\nB2_API float b2PrismaticJoint_GetSpeed( b2JointId jointId );\n\n/** @} */\n\n/**\n * @defgroup revolute_joint Revolute Joint\n * @brief A revolute joint allows for relative rotation in the 2D plane with no relative translation.\n *\n * The revolute joint is probably the most common joint. It can be used for ragdolls and chains.\n * Also called a *hinge* or *pin* joint.\n * @{\n */\n\n/// Create a revolute joint\n/// @see b2RevoluteJointDef for details\nB2_API b2JointId b2CreateRevoluteJoint( b2WorldId worldId, const b2RevoluteJointDef* def );\n\n/// Enable/disable the revolute joint spring\nB2_API void b2RevoluteJoint_EnableSpring( b2JointId jointId, bool enableSpring );\n\n/// It the revolute angular spring enabled?\nB2_API bool b2RevoluteJoint_IsSpringEnabled( b2JointId jointId );\n\n/// Set the revolute joint spring stiffness in Hertz\nB2_API void b2RevoluteJoint_SetSpringHertz( b2JointId jointId, float hertz );\n\n/// Get the revolute joint spring stiffness in Hertz\nB2_API float b2RevoluteJoint_GetSpringHertz( b2JointId jointId );\n\n/// Set the revolute joint spring damping ratio, non-dimensional\nB2_API void b2RevoluteJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio );\n\n/// Get the revolute joint spring damping ratio, non-dimensional\nB2_API float b2RevoluteJoint_GetSpringDampingRatio( b2JointId jointId );\n\n/// Set the revolute joint spring target angle, radians\nB2_API void b2RevoluteJoint_SetTargetAngle( b2JointId jointId, float angle );\n\n/// Get the revolute joint spring target angle, radians\nB2_API float b2RevoluteJoint_GetTargetAngle( b2JointId jointId );\n\n/// Get the revolute joint current angle in radians relative to the reference angle\n/// @see b2RevoluteJointDef::referenceAngle\nB2_API float b2RevoluteJoint_GetAngle( b2JointId jointId );\n\n/// Enable/disable the revolute joint limit\nB2_API void b2RevoluteJoint_EnableLimit( b2JointId jointId, bool enableLimit );\n\n/// Is the revolute joint limit enabled?\nB2_API bool b2RevoluteJoint_IsLimitEnabled( b2JointId jointId );\n\n/// Get the revolute joint lower limit in radians\nB2_API float b2RevoluteJoint_GetLowerLimit( b2JointId jointId );\n\n/// Get the revolute joint upper limit in radians\nB2_API float b2RevoluteJoint_GetUpperLimit( b2JointId jointId );\n\n/// Set the revolute joint limits in radians. It is expected that lower <= upper\n/// and that -0.99 * B2_PI <= lower && upper <= -0.99 * B2_PI.\nB2_API void b2RevoluteJoint_SetLimits( b2JointId jointId, float lower, float upper );\n\n/// Enable/disable a revolute joint motor\nB2_API void b2RevoluteJoint_EnableMotor( b2JointId jointId, bool enableMotor );\n\n/// Is the revolute joint motor enabled?\nB2_API bool b2RevoluteJoint_IsMotorEnabled( b2JointId jointId );\n\n/// Set the revolute joint motor speed in radians per second\nB2_API void b2RevoluteJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed );\n\n/// Get the revolute joint motor speed in radians per second\nB2_API float b2RevoluteJoint_GetMotorSpeed( b2JointId jointId );\n\n/// Get the revolute joint current motor torque, usually in newton-meters\nB2_API float b2RevoluteJoint_GetMotorTorque( b2JointId jointId );\n\n/// Set the revolute joint maximum motor torque, usually in newton-meters\nB2_API void b2RevoluteJoint_SetMaxMotorTorque( b2JointId jointId, float torque );\n\n/// Get the revolute joint maximum motor torque, usually in newton-meters\nB2_API float b2RevoluteJoint_GetMaxMotorTorque( b2JointId jointId );\n\n/**@}*/\n\n/**\n * @defgroup weld_joint Weld Joint\n * @brief A weld joint fully constrains the relative transform between two bodies while allowing for springiness\n *\n * A weld joint constrains the relative rotation and translation between two bodies. Both rotation and translation\n * can have damped springs.\n *\n * @note The accuracy of weld joint is limited by the accuracy of the solver. Long chains of weld joints may flex.\n * @{\n */\n\n/// Create a weld joint\n/// @see b2WeldJointDef for details\nB2_API b2JointId b2CreateWeldJoint( b2WorldId worldId, const b2WeldJointDef* def );\n\n/// Set the weld joint linear stiffness in Hertz. 0 is rigid.\nB2_API void b2WeldJoint_SetLinearHertz( b2JointId jointId, float hertz );\n\n/// Get the weld joint linear stiffness in Hertz\nB2_API float b2WeldJoint_GetLinearHertz( b2JointId jointId );\n\n/// Set the weld joint linear damping ratio (non-dimensional)\nB2_API void b2WeldJoint_SetLinearDampingRatio( b2JointId jointId, float dampingRatio );\n\n/// Get the weld joint linear damping ratio (non-dimensional)\nB2_API float b2WeldJoint_GetLinearDampingRatio( b2JointId jointId );\n\n/// Set the weld joint angular stiffness in Hertz. 0 is rigid.\nB2_API void b2WeldJoint_SetAngularHertz( b2JointId jointId, float hertz );\n\n/// Get the weld joint angular stiffness in Hertz\nB2_API float b2WeldJoint_GetAngularHertz( b2JointId jointId );\n\n/// Set weld joint angular damping ratio, non-dimensional\nB2_API void b2WeldJoint_SetAngularDampingRatio( b2JointId jointId, float dampingRatio );\n\n/// Get the weld joint angular damping ratio, non-dimensional\nB2_API float b2WeldJoint_GetAngularDampingRatio( b2JointId jointId );\n\n/** @} */\n\n/**\n * @defgroup wheel_joint Wheel Joint\n * The wheel joint can be used to simulate wheels on vehicles.\n *\n * The wheel joint restricts body B to move along a local axis in body A. Body B is free to\n * rotate. Supports a linear spring, linear limits, and a rotational motor.\n *\n * @{\n */\n\n/// Create a wheel joint\n/// @see b2WheelJointDef for details\nB2_API b2JointId b2CreateWheelJoint( b2WorldId worldId, const b2WheelJointDef* def );\n\n/// Enable/disable the wheel joint spring\nB2_API void b2WheelJoint_EnableSpring( b2JointId jointId, bool enableSpring );\n\n/// Is the wheel joint spring enabled?\nB2_API bool b2WheelJoint_IsSpringEnabled( b2JointId jointId );\n\n/// Set the wheel joint stiffness in Hertz\nB2_API void b2WheelJoint_SetSpringHertz( b2JointId jointId, float hertz );\n\n/// Get the wheel joint stiffness in Hertz\nB2_API float b2WheelJoint_GetSpringHertz( b2JointId jointId );\n\n/// Set the wheel joint damping ratio, non-dimensional\nB2_API void b2WheelJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio );\n\n/// Get the wheel joint damping ratio, non-dimensional\nB2_API float b2WheelJoint_GetSpringDampingRatio( b2JointId jointId );\n\n/// Enable/disable the wheel joint limit\nB2_API void b2WheelJoint_EnableLimit( b2JointId jointId, bool enableLimit );\n\n/// Is the wheel joint limit enabled?\nB2_API bool b2WheelJoint_IsLimitEnabled( b2JointId jointId );\n\n/// Get the wheel joint lower limit\nB2_API float b2WheelJoint_GetLowerLimit( b2JointId jointId );\n\n/// Get the wheel joint upper limit\nB2_API float b2WheelJoint_GetUpperLimit( b2JointId jointId );\n\n/// Set the wheel joint limits\nB2_API void b2WheelJoint_SetLimits( b2JointId jointId, float lower, float upper );\n\n/// Enable/disable the wheel joint motor\nB2_API void b2WheelJoint_EnableMotor( b2JointId jointId, bool enableMotor );\n\n/// Is the wheel joint motor enabled?\nB2_API bool b2WheelJoint_IsMotorEnabled( b2JointId jointId );\n\n/// Set the wheel joint motor speed in radians per second\nB2_API void b2WheelJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed );\n\n/// Get the wheel joint motor speed in radians per second\nB2_API float b2WheelJoint_GetMotorSpeed( b2JointId jointId );\n\n/// Set the wheel joint maximum motor torque, usually in newton-meters\nB2_API void b2WheelJoint_SetMaxMotorTorque( b2JointId jointId, float torque );\n\n/// Get the wheel joint maximum motor torque, usually in newton-meters\nB2_API float b2WheelJoint_GetMaxMotorTorque( b2JointId jointId );\n\n/// Get the wheel joint current motor torque, usually in newton-meters\nB2_API float b2WheelJoint_GetMotorTorque( b2JointId jointId );\n\n/**@}*/\n\n/**@}*/\n\n/**\n * @defgroup contact Contact\n * Access to contacts\n * @{\n */\n\n/// Contact identifier validation. Provides validation for up to 2^32 allocations.\nB2_API bool b2Contact_IsValid( b2ContactId id );\n\n/// Get the data for a contact. The manifold may have no points if the contact is not touching.\nB2_API b2ContactData b2Contact_GetData( b2ContactId contactId );\n\n/**@}*/\n"
  },
  {
    "path": "include/box2d/collision.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"base.h\"\n#include \"math_functions.h\"\n\n#include <stdbool.h>\n\ntypedef struct b2SimplexCache b2SimplexCache;\ntypedef struct b2Hull b2Hull;\n\n/**\n * @defgroup geometry Geometry\n * @brief Geometry types and algorithms\n *\n * Definitions of circles, capsules, segments, and polygons. Various algorithms to compute hulls, mass properties, and so on.\n * Functions should take the shape as the first argument to assist editor auto-complete.\n * @{\n */\n\n/// The maximum number of vertices on a convex polygon. Changing this affects performance even if you\n/// don't use more vertices.\n#define B2_MAX_POLYGON_VERTICES 8\n\n/// Low level ray cast input data\ntypedef struct b2RayCastInput\n{\n\t/// Start point of the ray cast\n\tb2Vec2 origin;\n\n\t/// Translation of the ray cast\n\tb2Vec2 translation;\n\n\t/// The maximum fraction of the translation to consider, typically 1\n\tfloat maxFraction;\n} b2RayCastInput;\n\n/// A distance proxy is used by the GJK algorithm. It encapsulates any shape.\n/// You can provide between 1 and B2_MAX_POLYGON_VERTICES and a radius.\ntypedef struct b2ShapeProxy\n{\n\t/// The point cloud\n\tb2Vec2 points[B2_MAX_POLYGON_VERTICES];\n\n\t/// The number of points. Must be greater than 0.\n\tint count;\n\n\t/// The external radius of the point cloud. May be zero.\n\tfloat radius;\n} b2ShapeProxy;\n\n/// Low level shape cast input in generic form. This allows casting an arbitrary point\n/// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius.\n/// A capsule is two points with a non-zero radius. A box is four points with a zero radius.\ntypedef struct b2ShapeCastInput\n{\n\t/// A generic shape\n\tb2ShapeProxy proxy;\n\n\t/// The translation of the shape cast\n\tb2Vec2 translation;\n\n\t/// The maximum fraction of the translation to consider, typically 1\n\tfloat maxFraction;\n\n\t/// Allow shape cast to encroach when initially touching. This only works if the radius is greater than zero.\n\tbool canEncroach;\n} b2ShapeCastInput;\n\n/// Low level ray cast or shape-cast output data. Returns a zero fraction and normal in the case of initial overlap.\ntypedef struct b2CastOutput\n{\n\t/// The surface normal at the hit point\n\tb2Vec2 normal;\n\n\t/// The surface hit point\n\tb2Vec2 point;\n\n\t/// The fraction of the input translation at collision\n\tfloat fraction;\n\n\t/// The number of iterations used\n\tint iterations;\n\n\t/// Did the cast hit?\n\tbool hit;\n} b2CastOutput;\n\n/// This holds the mass data computed for a shape.\ntypedef struct b2MassData\n{\n\t/// The mass of the shape, usually in kilograms.\n\tfloat mass;\n\n\t/// The position of the shape's centroid relative to the shape's origin.\n\tb2Vec2 center;\n\n\t/// The rotational inertia of the shape about the shape center.\n\tfloat rotationalInertia;\n} b2MassData;\n\n/// A solid circle\ntypedef struct b2Circle\n{\n\t/// The local center\n\tb2Vec2 center;\n\n\t/// The radius\n\tfloat radius;\n} b2Circle;\n\n/// A solid capsule can be viewed as two semicircles connected\n/// by a rectangle.\ntypedef struct b2Capsule\n{\n\t/// Local center of the first semicircle\n\tb2Vec2 center1;\n\n\t/// Local center of the second semicircle\n\tb2Vec2 center2;\n\n\t/// The radius of the semicircles\n\tfloat radius;\n} b2Capsule;\n\n/// A solid convex polygon. It is assumed that the interior of the polygon is to\n/// the left of each edge.\n/// Polygons have a maximum number of vertices equal to B2_MAX_POLYGON_VERTICES.\n/// In most cases you should not need many vertices for a convex polygon.\n/// @warning DO NOT fill this out manually, instead use a helper function like\n/// b2MakePolygon or b2MakeBox.\ntypedef struct b2Polygon\n{\n\t/// The polygon vertices\n\tb2Vec2 vertices[B2_MAX_POLYGON_VERTICES];\n\n\t/// The outward normal vectors of the polygon sides\n\tb2Vec2 normals[B2_MAX_POLYGON_VERTICES];\n\n\t/// The centroid of the polygon\n\tb2Vec2 centroid;\n\n\t/// The external radius for rounded polygons\n\tfloat radius;\n\n\t/// The number of polygon vertices\n\tint count;\n} b2Polygon;\n\n/// A line segment with two-sided collision.\ntypedef struct b2Segment\n{\n\t/// The first point\n\tb2Vec2 point1;\n\n\t/// The second point\n\tb2Vec2 point2;\n} b2Segment;\n\n/// A line segment with one-sided collision. Only collides on the right side.\n/// Several of these are generated for a chain shape.\n/// ghost1 -> point1 -> point2 -> ghost2\ntypedef struct b2ChainSegment\n{\n\t/// The tail ghost vertex\n\tb2Vec2 ghost1;\n\n\t/// The line segment\n\tb2Segment segment;\n\n\t/// The head ghost vertex\n\tb2Vec2 ghost2;\n\n\t/// The owning chain shape index (internal usage only)\n\tint chainId;\n} b2ChainSegment;\n\n/// Validate ray cast input data (NaN, etc)\nB2_API bool b2IsValidRay( const b2RayCastInput* input );\n\n/// Make a convex polygon from a convex hull. This will assert if the hull is not valid.\n/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull\nB2_API b2Polygon b2MakePolygon( const b2Hull* hull, float radius );\n\n/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.\n/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull\nB2_API b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation );\n\n/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid.\n/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull\nB2_API b2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation, float radius );\n\n/// Make a square polygon, bypassing the need for a convex hull.\n/// @param halfWidth the half-width\nB2_API b2Polygon b2MakeSquare( float halfWidth );\n\n/// Make a box (rectangle) polygon, bypassing the need for a convex hull.\n/// @param halfWidth the half-width (x-axis)\n/// @param halfHeight the half-height (y-axis)\nB2_API b2Polygon b2MakeBox( float halfWidth, float halfHeight );\n\n/// Make a rounded box, bypassing the need for a convex hull.\n/// @param halfWidth the half-width (x-axis)\n/// @param halfHeight the half-height (y-axis)\n/// @param radius the radius of the rounded extension\nB2_API b2Polygon b2MakeRoundedBox( float halfWidth, float halfHeight, float radius );\n\n/// Make an offset box, bypassing the need for a convex hull.\n/// @param halfWidth the half-width (x-axis)\n/// @param halfHeight the half-height (y-axis)\n/// @param center the local center of the box\n/// @param rotation the local rotation of the box\nB2_API b2Polygon b2MakeOffsetBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation );\n\n/// Make an offset rounded box, bypassing the need for a convex hull.\n/// @param halfWidth the half-width (x-axis)\n/// @param halfHeight the half-height (y-axis)\n/// @param center the local center of the box\n/// @param rotation the local rotation of the box\n/// @param radius the radius of the rounded extension\nB2_API b2Polygon b2MakeOffsetRoundedBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation, float radius );\n\n/// Transform a polygon. This is useful for transferring a shape from one body to another.\nB2_API b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon );\n\n/// Compute mass properties of a circle\nB2_API b2MassData b2ComputeCircleMass( const b2Circle* shape, float density );\n\n/// Compute mass properties of a capsule\nB2_API b2MassData b2ComputeCapsuleMass( const b2Capsule* shape, float density );\n\n/// Compute mass properties of a polygon\nB2_API b2MassData b2ComputePolygonMass( const b2Polygon* shape, float density );\n\n/// Compute the bounding box of a transformed circle\nB2_API b2AABB b2ComputeCircleAABB( const b2Circle* shape, b2Transform transform );\n\n/// Compute the bounding box of a transformed capsule\nB2_API b2AABB b2ComputeCapsuleAABB( const b2Capsule* shape, b2Transform transform );\n\n/// Compute the bounding box of a transformed polygon\nB2_API b2AABB b2ComputePolygonAABB( const b2Polygon* shape, b2Transform transform );\n\n/// Compute the bounding box of a transformed line segment\nB2_API b2AABB b2ComputeSegmentAABB( const b2Segment* shape, b2Transform transform );\n\n/// Test a point for overlap with a circle in local space\nB2_API bool b2PointInCircle( const b2Circle* shape, b2Vec2 point );\n\n/// Test a point for overlap with a capsule in local space\nB2_API bool b2PointInCapsule( const b2Capsule* shape, b2Vec2 point );\n\n/// Test a point for overlap with a convex polygon in local space\nB2_API bool b2PointInPolygon( const b2Polygon* shape, b2Vec2 point );\n\n/// Ray cast versus circle shape in local space.\nB2_API b2CastOutput b2RayCastCircle( const b2Circle* shape, const b2RayCastInput* input );\n\n/// Ray cast versus capsule shape in local space.\nB2_API b2CastOutput b2RayCastCapsule( const b2Capsule* shape, const b2RayCastInput* input );\n\n/// Ray cast versus segment shape in local space. Optionally treat the segment as one-sided with hits from\n/// the left side being treated as a miss.\nB2_API b2CastOutput b2RayCastSegment( const b2Segment* shape, const b2RayCastInput* input, bool oneSided );\n\n/// Ray cast versus polygon shape in local space.\nB2_API b2CastOutput b2RayCastPolygon( const b2Polygon* shape, const b2RayCastInput* input );\n\n/// Shape cast versus a circle.\nB2_API b2CastOutput b2ShapeCastCircle(const b2Circle* shape,  const b2ShapeCastInput* input );\n\n/// Shape cast versus a capsule.\nB2_API b2CastOutput b2ShapeCastCapsule( const b2Capsule* shape, const b2ShapeCastInput* input);\n\n/// Shape cast versus a line segment.\nB2_API b2CastOutput b2ShapeCastSegment( const b2Segment* shape, const b2ShapeCastInput* input );\n\n/// Shape cast versus a convex polygon.\nB2_API b2CastOutput b2ShapeCastPolygon( const b2Polygon* shape, const b2ShapeCastInput* input );\n\n/// A convex hull. Used to create convex polygons.\n/// @warning Do not modify these values directly, instead use b2ComputeHull()\ntypedef struct b2Hull\n{\n\t/// The final points of the hull\n\tb2Vec2 points[B2_MAX_POLYGON_VERTICES];\n\n\t/// The number of points\n\tint count;\n} b2Hull;\n\n/// Compute the convex hull of a set of points. Returns an empty hull if it fails.\n/// Some failure cases:\n/// - all points very close together\n/// - all points on a line\n/// - less than 3 points\n/// - more than B2_MAX_POLYGON_VERTICES points\n/// This welds close points and removes collinear points.\n/// @warning Do not modify a hull once it has been computed\nB2_API b2Hull b2ComputeHull( const b2Vec2* points, int count );\n\n/// This determines if a hull is valid. Checks for:\n/// - convexity\n/// - collinear points\n/// This is expensive and should not be called at runtime.\nB2_API bool b2ValidateHull( const b2Hull* hull );\n\n/**@}*/\n\n/**\n * @defgroup distance Distance\n * Functions for computing the distance between shapes.\n *\n * These are advanced functions you can use to perform distance calculations. There\n * are functions for computing the closest points between shapes, doing linear shape casts,\n * and doing rotational shape casts. The latter is called time of impact (TOI).\n * @{\n */\n\n/// Result of computing the distance between two line segments\ntypedef struct b2SegmentDistanceResult\n{\n\t/// The closest point on the first segment\n\tb2Vec2 closest1;\n\n\t/// The closest point on the second segment\n\tb2Vec2 closest2;\n\n\t/// The barycentric coordinate on the first segment\n\tfloat fraction1;\n\n\t/// The barycentric coordinate on the second segment\n\tfloat fraction2;\n\n\t/// The squared distance between the closest points\n\tfloat distanceSquared;\n} b2SegmentDistanceResult;\n\n/// Compute the distance between two line segments, clamping at the end points if needed.\nB2_API b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 );\n\n/// Used to warm start the GJK simplex. If you call this function multiple times with nearby\n/// transforms this might improve performance. Otherwise you can zero initialize this.\n/// The distance cache must be initialized to zero on the first call.\n/// Users should generally just zero initialize this structure for each call.\ntypedef struct b2SimplexCache\n{\n\t/// The number of stored simplex points\n\tuint16_t count;\n\n\t/// The cached simplex indices on shape A\n\tuint8_t indexA[3];\n\n\t/// The cached simplex indices on shape B\n\tuint8_t indexB[3];\n} b2SimplexCache;\n\nstatic const b2SimplexCache b2_emptySimplexCache = B2_ZERO_INIT;\n\n/// Input for b2ShapeDistance\ntypedef struct b2DistanceInput\n{\n\t/// The proxy for shape A\n\tb2ShapeProxy proxyA;\n\n\t/// The proxy for shape B\n\tb2ShapeProxy proxyB;\n\n\t/// The world transform for shape A\n\tb2Transform transformA;\n\n\t/// The world transform for shape B\n\tb2Transform transformB;\n\n\t/// Should the proxy radius be considered?\n\tbool useRadii;\n} b2DistanceInput;\n\n/// Output for b2ShapeDistance\ntypedef struct b2DistanceOutput\n{\n\tb2Vec2 pointA;\t  ///< Closest point on shapeA\n\tb2Vec2 pointB;\t  ///< Closest point on shapeB\n\tb2Vec2 normal;\t  ///< Normal vector that points from A to B. Invalid if distance is zero.\n\tfloat distance;\t  ///< The final distance, zero if overlapped\n\tint iterations;\t  ///< Number of GJK iterations used\n\tint simplexCount; ///< The number of simplexes stored in the simplex array\n} b2DistanceOutput;\n\n/// Simplex vertex for debugging the GJK algorithm\ntypedef struct b2SimplexVertex\n{\n\tb2Vec2 wA;\t///< support point in proxyA\n\tb2Vec2 wB;\t///< support point in proxyB\n\tb2Vec2 w;\t///< wB - wA\n\tfloat a;\t///< barycentric coordinate for closest point\n\tint indexA; ///< wA index\n\tint indexB; ///< wB index\n} b2SimplexVertex;\n\n/// Simplex from the GJK algorithm\ntypedef struct b2Simplex\n{\n\tb2SimplexVertex v1, v2, v3; ///< vertices\n\tint count;\t\t\t\t\t///< number of valid vertices\n} b2Simplex;\n\n/// Compute the closest points between two shapes represented as point clouds.\n/// b2SimplexCache cache is input/output. On the first call set b2SimplexCache.count to zero.\n/// The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these.\nB2_API b2DistanceOutput b2ShapeDistance( const b2DistanceInput* input, b2SimplexCache* cache, b2Simplex* simplexes,\n\t\t\t\t\t\t\t\t\t\t int simplexCapacity );\n\n/// Input parameters for b2ShapeCast\ntypedef struct b2ShapeCastPairInput\n{\n\tb2ShapeProxy proxyA;\t///< The proxy for shape A\n\tb2ShapeProxy proxyB;\t///< The proxy for shape B\n\tb2Transform transformA; ///< The world transform for shape A\n\tb2Transform transformB; ///< The world transform for shape B\n\tb2Vec2 translationB;\t///< The translation of shape B\n\tfloat maxFraction;\t\t///< The fraction of the translation to consider, typically 1\n\tbool canEncroach;\t\t///< Allows shapes with a radius to move slightly closer if already touching\n} b2ShapeCastPairInput;\n\n/// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction.\n/// Initially touching shapes are treated as a miss.\nB2_API b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input );\n\n/// Make a proxy for use in overlap, shape cast, and related functions. This is a deep copy of the points.\nB2_API b2ShapeProxy b2MakeProxy( const b2Vec2* points, int count, float radius );\n\n/// Make a proxy with a transform. This is a deep copy of the points.\nB2_API b2ShapeProxy b2MakeOffsetProxy( const b2Vec2* points, int count, float radius, b2Vec2 position, b2Rot rotation );\n\n/// This describes the motion of a body/shape for TOI computation. Shapes are defined with respect to the body origin,\n/// which may not coincide with the center of mass. However, to support dynamics we must interpolate the center of mass\n/// position.\ntypedef struct b2Sweep\n{\n\tb2Vec2 localCenter; ///< Local center of mass position\n\tb2Vec2 c1;\t\t\t///< Starting center of mass world position\n\tb2Vec2 c2;\t\t\t///< Ending center of mass world position\n\tb2Rot q1;\t\t\t///< Starting world rotation\n\tb2Rot q2;\t\t\t///< Ending world rotation\n} b2Sweep;\n\n/// Evaluate the transform sweep at a specific time.\nB2_API b2Transform b2GetSweepTransform( const b2Sweep* sweep, float time );\n\n/// Time of impact input\ntypedef struct b2TOIInput\n{\n\tb2ShapeProxy proxyA; ///< The proxy for shape A\n\tb2ShapeProxy proxyB; ///< The proxy for shape B\n\tb2Sweep sweepA;\t\t ///< The movement of shape A\n\tb2Sweep sweepB;\t\t ///< The movement of shape B\n\tfloat maxFraction;\t ///< Defines the sweep interval [0, maxFraction]\n} b2TOIInput;\n\n/// Describes the TOI output\ntypedef enum b2TOIState\n{\n\tb2_toiStateUnknown,\n\tb2_toiStateFailed,\n\tb2_toiStateOverlapped,\n\tb2_toiStateHit,\n\tb2_toiStateSeparated\n} b2TOIState;\n\n/// Time of impact output\ntypedef struct b2TOIOutput\n{\n\t/// The type of result\n\tb2TOIState state;\n\n\t/// The hit point\n\tb2Vec2 point;\n\n\t/// The hit normal\n\tb2Vec2 normal;\n\n\t/// The sweep time of the collision \n\tfloat fraction;\n} b2TOIOutput;\n\n/// Compute the upper bound on time before two shapes penetrate. Time is represented as\n/// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate,\n/// non-tunneling collisions. If you change the time interval, you should call this function\n/// again.\nB2_API b2TOIOutput b2TimeOfImpact( const b2TOIInput* input );\n\n/**@}*/\n\n/**\n * @defgroup collision Collision\n * @brief Functions for colliding pairs of shapes\n * @{\n */\n\n/// A manifold point is a contact point belonging to a contact manifold.\n/// It holds details related to the geometry and dynamics of the contact points.\n/// Box2D uses speculative collision so some contact points may be separated.\n/// You may use the totalNormalImpulse to determine if there was an interaction during\n/// the time step.\ntypedef struct b2ManifoldPoint\n{\n\t/// Location of the contact point in world space. Subject to precision loss at large coordinates.\n\t/// @note Should only be used for debugging.\n\tb2Vec2 point;\n\n\t/// Location of the contact point relative to shapeA's origin in world space\n\t/// @note When used internally to the Box2D solver, this is relative to the body center of mass.\n\tb2Vec2 anchorA;\n\n\t/// Location of the contact point relative to shapeB's origin in world space\n\t/// @note When used internally to the Box2D solver, this is relative to the body center of mass.\n\tb2Vec2 anchorB;\n\n\t/// The separation of the contact point, negative if penetrating\n\tfloat separation;\n\n\t/// The impulse along the manifold normal vector.\n\tfloat normalImpulse;\n\n\t/// The friction impulse\n\tfloat tangentImpulse;\n\n\t/// The total normal impulse applied across sub-stepping and restitution. This is important\n\t/// to identify speculative contact points that had an interaction in the time step.\n\t/// This includes the warm starting impulse, the sub-step delta impulse, and the restitution\n\t/// impulse.\n\tfloat totalNormalImpulse;\n\n\t/// Relative normal velocity pre-solve. Used for hit events. If the normal impulse is\n\t/// zero then there was no hit. Negative means shapes are approaching.\n\tfloat normalVelocity;\n\n\t/// Uniquely identifies a contact point between two shapes\n\tuint16_t id;\n\n\t/// Did this contact point exist the previous step?\n\tbool persisted;\n} b2ManifoldPoint;\n\n/// A contact manifold describes the contact points between colliding shapes.\n/// @note Box2D uses speculative collision so some contact points may be separated.\ntypedef struct b2Manifold\n{\n\t/// The unit normal vector in world space, points from shape A to bodyB\n\tb2Vec2 normal;\n\n\t/// Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s\n\tfloat rollingImpulse;\n\n\t/// The manifold points, up to two are possible in 2D\n\tb2ManifoldPoint points[2];\n\n\t/// The number of contacts points, will be 0, 1, or 2\n\tint pointCount;\n\n} b2Manifold;\n\n/// Compute the contact manifold between two circles\nB2_API b2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB );\n\n/// Compute the contact manifold between a capsule and circle\nB2_API b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA, const b2Circle* circleB,\n\t\t\t\t\t\t\t\t\t\t\t b2Transform xfB );\n\n/// Compute the contact manifold between an segment and a circle\nB2_API b2Manifold b2CollideSegmentAndCircle( const b2Segment* segmentA, b2Transform xfA, const b2Circle* circleB,\n\t\t\t\t\t\t\t\t\t\t\t b2Transform xfB );\n\n/// Compute the contact manifold between a polygon and a circle\nB2_API b2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB,\n\t\t\t\t\t\t\t\t\t\t\t b2Transform xfB );\n\n/// Compute the contact manifold between a capsule and circle\nB2_API b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB );\n\n/// Compute the contact manifold between an segment and a capsule\nB2_API b2Manifold b2CollideSegmentAndCapsule( const b2Segment* segmentA, b2Transform xfA, const b2Capsule* capsuleB,\n\t\t\t\t\t\t\t\t\t\t\t  b2Transform xfB );\n\n/// Compute the contact manifold between a polygon and capsule\nB2_API b2Manifold b2CollidePolygonAndCapsule( const b2Polygon* polygonA, b2Transform xfA, const b2Capsule* capsuleB,\n\t\t\t\t\t\t\t\t\t\t\t  b2Transform xfB );\n\n/// Compute the contact manifold between two polygons\nB2_API b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB );\n\n/// Compute the contact manifold between an segment and a polygon\nB2_API b2Manifold b2CollideSegmentAndPolygon( const b2Segment* segmentA, b2Transform xfA, const b2Polygon* polygonB,\n\t\t\t\t\t\t\t\t\t\t\t  b2Transform xfB );\n\n/// Compute the contact manifold between a chain segment and a circle\nB2_API b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Transform xfA, const b2Circle* circleB,\n\t\t\t\t\t\t\t\t\t\t\t\t  b2Transform xfB );\n\n/// Compute the contact manifold between a chain segment and a capsule\nB2_API b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB,\n\t\t\t\t\t\t\t\t\t\t\t\t   b2Transform xfB, b2SimplexCache* cache );\n\n/// Compute the contact manifold between a chain segment and a rounded polygon\nB2_API b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB,\n\t\t\t\t\t\t\t\t\t\t\t\t   b2Transform xfB, b2SimplexCache* cache );\n\n/**@}*/\n\n/**\n * @defgroup tree Dynamic Tree\n * The dynamic tree is a binary AABB tree to organize and query large numbers of geometric objects\n *\n * Box2D uses the dynamic tree internally to sort collision shapes into a binary bounding volume hierarchy.\n * This data structure may have uses in games for organizing other geometry data and may be used independently\n * of Box2D rigid body simulation.\n *\n * A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt.\n * A dynamic tree arranges data in a binary tree to accelerate\n * queries such as AABB queries and ray casts. Leaf nodes are proxies\n * with an AABB. These are used to hold a user collision object.\n * Nodes are pooled and relocatable, so I use node indices rather than pointers.\n * The dynamic tree is made available for advanced users that would like to use it to organize\n * spatial game data besides rigid bodies.\n * @{\n */\n\n/// The dynamic tree structure. This should be considered private data.\n/// It is placed here for performance reasons.\ntypedef struct b2DynamicTree\n{\n\t/// The tree nodes\n\tstruct b2TreeNode* nodes;\n\n\t/// The root index\n\tint32_t root;\n\n\t/// The number of nodes\n\tint32_t nodeCount;\n\n\t/// The allocated node space\n\tint32_t nodeCapacity;\n\n\t/// Node free list\n\tint32_t freeList;\n\n\t/// Number of proxies created\n\tint32_t proxyCount;\n\n\t/// Leaf indices for rebuild\n\tint32_t* leafIndices;\n\n\t/// Leaf bounding boxes for rebuild\n\tb2AABB* leafBoxes;\n\n\t/// Leaf bounding box centers for rebuild\n\tb2Vec2* leafCenters;\n\n\t/// Bins for sorting during rebuild\n\tint32_t* binIndices;\n\n\t/// Allocated space for rebuilding\n\tint32_t rebuildCapacity;\n} b2DynamicTree;\n\n/// These are performance results returned by dynamic tree queries.\ntypedef struct b2TreeStats\n{\n\t/// Number of internal nodes visited during the query\n\tint nodeVisits;\n\n\t/// Number of leaf nodes visited during the query\n\tint leafVisits;\n} b2TreeStats;\n\n/// Constructing the tree initializes the node pool.\nB2_API b2DynamicTree b2DynamicTree_Create( void );\n\n/// Destroy the tree, freeing the node pool.\nB2_API void b2DynamicTree_Destroy( b2DynamicTree* tree );\n\n/// Create a proxy. Provide an AABB and a userData value.\nB2_API int b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, uint64_t userData );\n\n/// Destroy a proxy. This asserts if the id is invalid.\nB2_API void b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int proxyId );\n\n/// Move a proxy to a new AABB by removing and reinserting into the tree.\nB2_API void b2DynamicTree_MoveProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb );\n\n/// Enlarge a proxy and enlarge ancestors as necessary.\nB2_API void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb );\n\n/// Modify the category bits on a proxy. This is an expensive operation.\nB2_API void b2DynamicTree_SetCategoryBits( b2DynamicTree* tree, int proxyId, uint64_t categoryBits );\n\n/// Get the category bits on a proxy.\nB2_API uint64_t b2DynamicTree_GetCategoryBits( b2DynamicTree* tree, int proxyId );\n\n/// This function receives proxies found in the AABB query.\n/// @return true if the query should continue\ntypedef bool b2TreeQueryCallbackFcn( int proxyId, uint64_t userData, void* context );\n\n/// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB.\n///\t@return performance data\nB2_API b2TreeStats b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits,\n\t\t\t\t\t\t\t\t\t\tb2TreeQueryCallbackFcn* callback, void* context );\n\n/// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB.\n/// No filtering is performed.\n///\t@return performance data\nB2_API b2TreeStats b2DynamicTree_QueryAll( const b2DynamicTree* tree, b2AABB aabb, b2TreeQueryCallbackFcn* callback,\n\t\t\t\t\t\t\t\t\t\t   void* context );\n\n/// This function receives clipped ray cast input for a proxy. The function\n/// returns the new ray fraction.\n/// - return a value of 0 to terminate the ray cast\n/// - return a value less than input->maxFraction to clip the ray\n/// - return a value of input->maxFraction to continue the ray cast without clipping\ntypedef float b2TreeRayCastCallbackFcn( const b2RayCastInput* input, int proxyId, uint64_t userData, void* context );\n\n/// Ray cast against the proxies in the tree. This relies on the callback\n/// to perform a exact ray cast in the case were the proxy contains a shape.\n/// The callback also performs the any collision filtering. This has performance\n/// roughly equal to k * log(n), where k is the number of collisions and n is the\n/// number of proxies in the tree.\n/// Bit-wise filtering using mask bits can greatly improve performance in some scenarios.\n///\tHowever, this filtering may be approximate, so the user should still apply filtering to results.\n/// @param tree the dynamic tree to ray cast\n/// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1)\n/// @param maskBits mask bit hint: `bool accept = (maskBits & node->categoryBits) != 0;`\n/// @param callback a callback class that is called for each proxy that is hit by the ray\n/// @param context user context that is passed to the callback\n///\t@return performance data\nB2_API b2TreeStats b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits,\n\t\t\t\t\t\t\t\t\t\t  b2TreeRayCastCallbackFcn* callback, void* context );\n\n/// This function receives clipped ray cast input for a proxy. The function\n/// returns the new ray fraction.\n/// - return a value of 0 to terminate the ray cast\n/// - return a value less than input->maxFraction to clip the ray\n/// - return a value of input->maxFraction to continue the ray cast without clipping\ntypedef float b2TreeShapeCastCallbackFcn( const b2ShapeCastInput* input, int proxyId, uint64_t userData, void* context );\n\n/// Ray cast against the proxies in the tree. This relies on the callback\n/// to perform a exact ray cast in the case were the proxy contains a shape.\n/// The callback also performs the any collision filtering. This has performance\n/// roughly equal to k * log(n), where k is the number of collisions and n is the\n/// number of proxies in the tree.\n/// @param tree the dynamic tree to ray cast\n/// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).\n/// @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0;`\n/// @param callback a callback class that is called for each proxy that is hit by the shape\n/// @param context user context that is passed to the callback\n///\t@return performance data\nB2_API b2TreeStats b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits,\n\t\t\t\t\t\t\t\t\t\t\tb2TreeShapeCastCallbackFcn* callback, void* context );\n\n/// Get the height of the binary tree.\nB2_API int b2DynamicTree_GetHeight( const b2DynamicTree* tree );\n\n/// Get the ratio of the sum of the node areas to the root area.\nB2_API float b2DynamicTree_GetAreaRatio( const b2DynamicTree* tree );\n\n/// Get the bounding box that contains the entire tree\nB2_API b2AABB b2DynamicTree_GetRootBounds( const b2DynamicTree* tree );\n\n/// Get the number of proxies created\nB2_API int b2DynamicTree_GetProxyCount( const b2DynamicTree* tree );\n\n/// Rebuild the tree while retaining subtrees that haven't changed. Returns the number of boxes sorted.\nB2_API int b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild );\n\n/// Get the number of bytes used by this tree\nB2_API int b2DynamicTree_GetByteCount( const b2DynamicTree* tree );\n\n/// Get proxy user data\nB2_API uint64_t b2DynamicTree_GetUserData( const b2DynamicTree* tree, int proxyId );\n\n/// Get the AABB of a proxy\nB2_API b2AABB b2DynamicTree_GetAABB( const b2DynamicTree* tree, int proxyId );\n\n/// Validate this tree. For testing.\nB2_API void b2DynamicTree_Validate( const b2DynamicTree* tree );\n\n/// Validate this tree has no enlarged AABBs. For testing.\nB2_API void b2DynamicTree_ValidateNoEnlarged( const b2DynamicTree* tree );\n\n/**@}*/\n\n/**\n * @defgroup character Character mover\n * Character movement solver\n * @{\n */\n\n/// These are the collision planes returned from b2World_CollideMover\ntypedef struct b2PlaneResult\n{\n\t/// The collision plane between the mover and a convex shape\n\tb2Plane plane;\n\n\t// The collision point on the shape.\n\tb2Vec2 point;\n\n\t/// Did the collision register a hit? If not this plane should be ignored.\n\tbool hit;\n} b2PlaneResult;\n\n/// These are collision planes that can be fed to b2SolvePlanes. Normally\n/// this is assembled by the user from plane results in b2PlaneResult\ntypedef struct b2CollisionPlane\n{\n\t/// The collision plane between the mover and some shape\n\tb2Plane plane;\n\n\t/// Setting this to FLT_MAX makes the plane as rigid as possible. Lower values can\n\t/// make the plane collision soft. Usually in meters.\n\tfloat pushLimit;\n\n\t/// The push on the mover determined by b2SolvePlanes. Usually in meters.\n\tfloat push;\n\n\t/// Indicates if b2ClipVector should clip against this plane. Should be false for soft collision.\n\tbool clipVelocity;\n} b2CollisionPlane;\n\n/// Result returned by b2SolvePlanes\ntypedef struct b2PlaneSolverResult\n{\n\t/// The translation of the mover\n\tb2Vec2 translation;\n\n\t/// The number of iterations used by the plane solver. For diagnostics.\n\tint iterationCount;\n} b2PlaneSolverResult;\n\n/// Solves the position of a mover that satisfies the given collision planes.\n/// @param targetDelta the desired movement from the position used to generate the collision planes\n/// @param planes the collision planes\n/// @param count the number of collision planes\nB2_API b2PlaneSolverResult b2SolvePlanes( b2Vec2 targetDelta, b2CollisionPlane* planes, int count );\n\n/// Clips the velocity against the given collision planes. Planes with zero push or clipVelocity\n/// set to false are skipped.\nB2_API b2Vec2 b2ClipVector( b2Vec2 vector, const b2CollisionPlane* planes, int count );\n\n/**@}*/\n"
  },
  {
    "path": "include/box2d/id.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <stdint.h>\n\n// Note: this file should be stand-alone\n\n/**\n * @defgroup id Ids\n * These ids serve as handles to internal Box2D objects.\n * These should be considered opaque data and passed by value.\n * Include this header if you need the id types and not the whole Box2D API.\n * All ids are considered null if initialized to zero.\n *\n * For example in C++:\n *\n * @code{.cxx}\n * b2WorldId worldId = {};\n * @endcode\n *\n * Or in C:\n *\n * @code{.c}\n * b2WorldId worldId = {0};\n * @endcode\n *\n * These are both considered null.\n *\n * @warning Do not use the internals of these ids. They are subject to change. Ids should be treated as opaque objects.\n * @warning You should use ids to access objects in Box2D. Do not access files within the src folder. Such usage is unsupported.\n * @{\n */\n\n/// World id references a world instance. This should be treated as an opaque handle.\ntypedef struct b2WorldId\n{\n\tuint16_t index1;\n\tuint16_t generation;\n} b2WorldId;\n\n/// Body id references a body instance. This should be treated as an opaque handle.\ntypedef struct b2BodyId\n{\n\tint32_t index1;\n\tuint16_t world0;\n\tuint16_t generation;\n} b2BodyId;\n\n/// Shape id references a shape instance. This should be treated as an opaque handle.\ntypedef struct b2ShapeId\n{\n\tint32_t index1;\n\tuint16_t world0;\n\tuint16_t generation;\n} b2ShapeId;\n\n/// Chain id references a chain instances. This should be treated as an opaque handle.\ntypedef struct b2ChainId\n{\n\tint32_t index1;\n\tuint16_t world0;\n\tuint16_t generation;\n} b2ChainId;\n\n/// Joint id references a joint instance. This should be treated as an opaque handle.\ntypedef struct b2JointId\n{\n\tint32_t index1;\n\tuint16_t world0;\n\tuint16_t generation;\n} b2JointId;\n\n/// Contact id references a contact instance. This should be treated as an opaque handled.\ntypedef struct b2ContactId\n{\n\tint32_t index1;\n\tuint16_t world0;\n\tint16_t padding;\n\tuint32_t generation;\n} b2ContactId;\n\n#ifdef __cplusplus\n\t#define B2_NULL_ID {}\n\t#define B2_ID_INLINE inline\n#else\n\t#define B2_NULL_ID { 0 }\n\t#define B2_ID_INLINE static inline\n#endif\n\n/// Use these to make your identifiers null.\n/// You may also use zero initialization to get null.\nstatic const b2WorldId b2_nullWorldId = B2_NULL_ID;\nstatic const b2BodyId b2_nullBodyId = B2_NULL_ID;\nstatic const b2ShapeId b2_nullShapeId = B2_NULL_ID;\nstatic const b2ChainId b2_nullChainId = B2_NULL_ID;\nstatic const b2JointId b2_nullJointId = B2_NULL_ID;\nstatic const b2ContactId b2_nullContactId = B2_NULL_ID;\n\n/// Macro to determine if any id is null.\n#define B2_IS_NULL( id ) ( (id).index1 == 0 )\n\n/// Macro to determine if any id is non-null.\n#define B2_IS_NON_NULL( id ) ( (id).index1 != 0 )\n\n/// Compare two ids for equality. Doesn't work for b2WorldId. Don't mix types.\n#define B2_ID_EQUALS( id1, id2 ) ( (id1).index1 == (id2).index1 && (id1).world0 == (id2).world0 && (id1).generation == (id2).generation )\n\n/// Store a world id into a uint32_t.\nB2_ID_INLINE uint32_t b2StoreWorldId( b2WorldId id )\n{\n\treturn ( (uint32_t)id.index1 << 16 ) | (uint32_t)id.generation;\n}\n\n/// Load a uint32_t into a world id.\nB2_ID_INLINE b2WorldId b2LoadWorldId( uint32_t x )\n{\n\tb2WorldId id = { (uint16_t)( x >> 16 ), (uint16_t)( x ) };\n\treturn id;\n}\n\n/// Store a body id into a uint64_t.\nB2_ID_INLINE uint64_t b2StoreBodyId( b2BodyId id )\n{\n\treturn ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;\n}\n\n/// Load a uint64_t into a body id.\nB2_ID_INLINE b2BodyId b2LoadBodyId( uint64_t x )\n{\n\tb2BodyId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };\n\treturn id;\n}\n\n/// Store a shape id into a uint64_t.\nB2_ID_INLINE uint64_t b2StoreShapeId( b2ShapeId id )\n{\n\treturn ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;\n}\n\n/// Load a uint64_t into a shape id.\nB2_ID_INLINE b2ShapeId b2LoadShapeId( uint64_t x )\n{\n\tb2ShapeId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };\n\treturn id;\n}\n\n/// Store a chain id into a uint64_t.\nB2_ID_INLINE uint64_t b2StoreChainId( b2ChainId id )\n{\n\treturn ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;\n}\n\n/// Load a uint64_t into a chain id.\nB2_ID_INLINE b2ChainId b2LoadChainId( uint64_t x )\n{\n\tb2ChainId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };\n\treturn id;\n}\n\n/// Store a joint id into a uint64_t.\nB2_ID_INLINE uint64_t b2StoreJointId( b2JointId id )\n{\n\treturn ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;\n}\n\n/// Load a uint64_t into a joint id.\nB2_ID_INLINE b2JointId b2LoadJointId( uint64_t x )\n{\n\tb2JointId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };\n\treturn id;\n}\n\n/// Store a contact id into 16 bytes\nB2_ID_INLINE void b2StoreContactId( b2ContactId id, uint32_t values[3] )\n{\n\tvalues[0] = (uint32_t)id.index1;\n\tvalues[1] = (uint32_t)id.world0;\n\tvalues[2] = (uint32_t)id.generation;\n}\n\n/// Load a two uint64_t into a contact id.\nB2_ID_INLINE b2ContactId b2LoadContactId( uint32_t values[3] )\n{\n\tb2ContactId id;\n\tid.index1 = (int32_t)values[0];\n\tid.world0 = (uint16_t)values[1];\n\tid.padding = 0;\n\tid.generation = (uint32_t)values[2];\n\treturn id;\n}\n\n/**@}*/\n"
  },
  {
    "path": "include/box2d/math_functions.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"base.h\"\n\n#include <float.h>\n#include <math.h>\n#include <stdbool.h>\n\n/**\n * @defgroup math Math\n * @brief Vector math types and functions\n * @{\n */\n\n/// 2D vector\n/// This can be used to represent a point or free vector\ntypedef struct b2Vec2\n{\n\t/// coordinates\n\tfloat x, y;\n} b2Vec2;\n\n/// Cosine and sine pair\n/// This uses a custom implementation designed for cross-platform determinism\ntypedef struct b2CosSin\n{\n\t/// cosine and sine\n\tfloat cosine;\n\tfloat sine;\n} b2CosSin;\n\n/// 2D rotation\n/// This is similar to using a complex number for rotation\ntypedef struct b2Rot\n{\n\t/// cosine and sine\n\tfloat c, s;\n} b2Rot;\n\n/// A 2D rigid transform\ntypedef struct b2Transform\n{\n\tb2Vec2 p;\n\tb2Rot q;\n} b2Transform;\n\n/// A 2-by-2 Matrix\ntypedef struct b2Mat22\n{\n\t/// columns\n\tb2Vec2 cx, cy;\n} b2Mat22;\n\n/// Axis-aligned bounding box\ntypedef struct b2AABB\n{\n\tb2Vec2 lowerBound;\n\tb2Vec2 upperBound;\n} b2AABB;\n\n/// separation = dot(normal, point) - offset\ntypedef struct b2Plane\n{\n\tb2Vec2 normal;\n\tfloat offset;\n} b2Plane;\n\n/**@}*/\n\n/**\n * @addtogroup math\n * @{\n */\n\n/// https://en.wikipedia.org/wiki/Pi\n#define B2_PI 3.14159265359f\n\nstatic const b2Vec2 b2Vec2_zero = { 0.0f, 0.0f };\nstatic const b2Rot b2Rot_identity = { 1.0f, 0.0f };\nstatic const b2Transform b2Transform_identity = { { 0.0f, 0.0f }, { 1.0f, 0.0f } };\nstatic const b2Mat22 b2Mat22_zero = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };\n\n/// Is this a valid number? Not NaN or infinity.\nB2_API bool b2IsValidFloat( float a );\n\n/// Is this a valid vector? Not NaN or infinity.\nB2_API bool b2IsValidVec2( b2Vec2 v );\n\n/// Is this a valid rotation? Not NaN or infinity. Is normalized.\nB2_API bool b2IsValidRotation( b2Rot q );\n\n/// Is this a valid transform? Not NaN or infinity. Rotation is normalized.\nB2_API bool b2IsValidTransform( b2Transform t );\n\n/// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound.\nB2_API bool b2IsValidAABB( b2AABB aabb );\n\n/// Is this a valid plane? Normal is a unit vector. Not Nan or infinity.\nB2_API bool b2IsValidPlane( b2Plane a );\n\n/// @return the minimum of two integers\nB2_INLINE int b2MinInt( int a, int b )\n{\n\treturn a < b ? a : b;\n}\n\n/// @return the maximum of two integers\nB2_INLINE int b2MaxInt( int a, int b )\n{\n\treturn a > b ? a : b;\n}\n\n/// @return the absolute value of an integer\nB2_INLINE int b2AbsInt( int a )\n{\n\treturn a < 0 ? -a : a;\n}\n\n/// @return an integer clamped between a lower and upper bound\nB2_INLINE int b2ClampInt( int a, int lower, int upper )\n{\n\treturn a < lower ? lower : ( a > upper ? upper : a );\n}\n\n/// @return the minimum of two floats\nB2_INLINE float b2MinFloat( float a, float b )\n{\n\treturn a < b ? a : b;\n}\n\n/// @return the maximum of two floats\nB2_INLINE float b2MaxFloat( float a, float b )\n{\n\treturn a > b ? a : b;\n}\n\n/// @return the absolute value of a float\nB2_INLINE float b2AbsFloat( float a )\n{\n\treturn a < 0 ? -a : a;\n}\n\n/// @return a float clamped between a lower and upper bound\nB2_INLINE float b2ClampFloat( float a, float lower, float upper )\n{\n\treturn a < lower ? lower : ( a > upper ? upper : a );\n}\n\n/// Compute an approximate arctangent in the range [-pi, pi]\n/// This is hand coded for cross-platform determinism. The atan2f\n/// function in the standard library is not cross-platform deterministic.\n///\tAccurate to around 0.0023 degrees\nB2_API float b2Atan2( float y, float x );\n\n/// Compute the cosine and sine of an angle in radians. Implemented\n/// for cross-platform determinism.\nB2_API b2CosSin b2ComputeCosSin( float radians );\n\n/// Vector dot product\nB2_INLINE float b2Dot( b2Vec2 a, b2Vec2 b )\n{\n\treturn a.x * b.x + a.y * b.y;\n}\n\n/// Vector cross product. In 2D this yields a scalar.\nB2_INLINE float b2Cross( b2Vec2 a, b2Vec2 b )\n{\n\treturn a.x * b.y - a.y * b.x;\n}\n\n/// Perform the cross product on a vector and a scalar. In 2D this produces a vector.\nB2_INLINE b2Vec2 b2CrossVS( b2Vec2 v, float s )\n{\n\treturn B2_LITERAL( b2Vec2 ){ s * v.y, -s * v.x };\n}\n\n/// Perform the cross product on a scalar and a vector. In 2D this produces a vector.\nB2_INLINE b2Vec2 b2CrossSV( float s, b2Vec2 v )\n{\n\treturn B2_LITERAL( b2Vec2 ){ -s * v.y, s * v.x };\n}\n\n/// Get a left pointing perpendicular vector. Equivalent to b2CrossSV(1.0f, v)\nB2_INLINE b2Vec2 b2LeftPerp( b2Vec2 v )\n{\n\treturn B2_LITERAL( b2Vec2 ){ -v.y, v.x };\n}\n\n/// Get a right pointing perpendicular vector. Equivalent to b2CrossVS(v, 1.0f)\nB2_INLINE b2Vec2 b2RightPerp( b2Vec2 v )\n{\n\treturn B2_LITERAL( b2Vec2 ){ v.y, -v.x };\n}\n\n/// Vector addition\nB2_INLINE b2Vec2 b2Add( b2Vec2 a, b2Vec2 b )\n{\n\treturn B2_LITERAL( b2Vec2 ){ a.x + b.x, a.y + b.y };\n}\n\n/// Vector subtraction\nB2_INLINE b2Vec2 b2Sub( b2Vec2 a, b2Vec2 b )\n{\n\treturn B2_LITERAL( b2Vec2 ){ a.x - b.x, a.y - b.y };\n}\n\n/// Vector negation\nB2_INLINE b2Vec2 b2Neg( b2Vec2 a )\n{\n\treturn B2_LITERAL( b2Vec2 ){ -a.x, -a.y };\n}\n\n/// Vector linear interpolation\n/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/\nB2_INLINE b2Vec2 b2Lerp( b2Vec2 a, b2Vec2 b, float t )\n{\n\treturn B2_LITERAL( b2Vec2 ){ ( 1.0f - t ) * a.x + t * b.x, ( 1.0f - t ) * a.y + t * b.y };\n}\n\n/// Component-wise multiplication\nB2_INLINE b2Vec2 b2Mul( b2Vec2 a, b2Vec2 b )\n{\n\treturn B2_LITERAL( b2Vec2 ){ a.x * b.x, a.y * b.y };\n}\n\n/// Multiply a scalar and vector\nB2_INLINE b2Vec2 b2MulSV( float s, b2Vec2 v )\n{\n\treturn B2_LITERAL( b2Vec2 ){ s * v.x, s * v.y };\n}\n\n/// a + s * b\nB2_INLINE b2Vec2 b2MulAdd( b2Vec2 a, float s, b2Vec2 b )\n{\n\treturn B2_LITERAL( b2Vec2 ){ a.x + s * b.x, a.y + s * b.y };\n}\n\n/// a - s * b\nB2_INLINE b2Vec2 b2MulSub( b2Vec2 a, float s, b2Vec2 b )\n{\n\treturn B2_LITERAL( b2Vec2 ){ a.x - s * b.x, a.y - s * b.y };\n}\n\n/// Component-wise absolute vector\nB2_INLINE b2Vec2 b2Abs( b2Vec2 a )\n{\n\tb2Vec2 b;\n\tb.x = b2AbsFloat( a.x );\n\tb.y = b2AbsFloat( a.y );\n\treturn b;\n}\n\n/// Component-wise minimum vector\nB2_INLINE b2Vec2 b2Min( b2Vec2 a, b2Vec2 b )\n{\n\tb2Vec2 c;\n\tc.x = b2MinFloat( a.x, b.x );\n\tc.y = b2MinFloat( a.y, b.y );\n\treturn c;\n}\n\n/// Component-wise maximum vector\nB2_INLINE b2Vec2 b2Max( b2Vec2 a, b2Vec2 b )\n{\n\tb2Vec2 c;\n\tc.x = b2MaxFloat( a.x, b.x );\n\tc.y = b2MaxFloat( a.y, b.y );\n\treturn c;\n}\n\n/// Component-wise clamp vector v into the range [a, b]\nB2_INLINE b2Vec2 b2Clamp( b2Vec2 v, b2Vec2 a, b2Vec2 b )\n{\n\tb2Vec2 c;\n\tc.x = b2ClampFloat( v.x, a.x, b.x );\n\tc.y = b2ClampFloat( v.y, a.y, b.y );\n\treturn c;\n}\n\n/// Get the length of this vector (the norm)\nB2_INLINE float b2Length( b2Vec2 v )\n{\n\treturn sqrtf( v.x * v.x + v.y * v.y );\n}\n\n/// Get the distance between two points\nB2_INLINE float b2Distance( b2Vec2 a, b2Vec2 b )\n{\n\tfloat dx = b.x - a.x;\n\tfloat dy = b.y - a.y;\n\treturn sqrtf( dx * dx + dy * dy );\n}\n\n/// Convert a vector into a unit vector if possible, otherwise returns the zero vector.\n/// todo MSVC is not inlining this function in several places per warning 4710\nB2_INLINE b2Vec2 b2Normalize( b2Vec2 v )\n{\n\tfloat length = sqrtf( v.x * v.x + v.y * v.y );\n\tif ( length < FLT_EPSILON )\n\t{\n\t\treturn B2_LITERAL( b2Vec2 ){ 0.0f, 0.0f };\n\t}\n\n\tfloat invLength = 1.0f / length;\n\tb2Vec2 n = { invLength * v.x, invLength * v.y };\n\treturn n;\n}\n\n/// Determines if the provided vector is normalized (norm(a) == 1).\nB2_INLINE bool b2IsNormalized( b2Vec2 a )\n{\n\tfloat aa = b2Dot( a, a );\n\treturn b2AbsFloat( 1.0f - aa ) < 100.0f * FLT_EPSILON;\n}\n\n/// Convert a vector into a unit vector if possible, otherwise returns the zero vector. Also\n/// outputs the length.\nB2_INLINE b2Vec2 b2GetLengthAndNormalize( float* length, b2Vec2 v )\n{\n\t*length = sqrtf( v.x * v.x + v.y * v.y );\n\tif ( *length < FLT_EPSILON )\n\t{\n\t\treturn B2_LITERAL( b2Vec2 ){ 0.0f, 0.0f };\n\t}\n\n\tfloat invLength = 1.0f / *length;\n\tb2Vec2 n = { invLength * v.x, invLength * v.y };\n\treturn n;\n}\n\n/// Normalize rotation\nB2_INLINE b2Rot b2NormalizeRot( b2Rot q )\n{\n\tfloat mag = sqrtf( q.s * q.s + q.c * q.c );\n\tfloat invMag = mag > 0.0f ? 1.0f / mag : 0.0f;\n\tb2Rot qn = { q.c * invMag, q.s * invMag };\n\treturn qn;\n}\n\n/// Integrate rotation from angular velocity\n/// @param q1 initial rotation\n/// @param deltaAngle the angular displacement in radians\nB2_INLINE b2Rot b2IntegrateRotation( b2Rot q1, float deltaAngle )\n{\n\t// dc/dt = -omega * sin(t)\n\t// ds/dt = omega * cos(t)\n\t// c2 = c1 - omega * h * s1\n\t// s2 = s1 + omega * h * c1\n\tb2Rot q2 = { q1.c - deltaAngle * q1.s, q1.s + deltaAngle * q1.c };\n\tfloat mag = sqrtf( q2.s * q2.s + q2.c * q2.c );\n\tfloat invMag = mag > 0.0f ? 1.0f / mag : 0.0f;\n\tb2Rot qn = { q2.c * invMag, q2.s * invMag };\n\treturn qn;\n}\n\n/// Get the length squared of this vector\nB2_INLINE float b2LengthSquared( b2Vec2 v )\n{\n\treturn v.x * v.x + v.y * v.y;\n}\n\n/// Get the distance squared between points\nB2_INLINE float b2DistanceSquared( b2Vec2 a, b2Vec2 b )\n{\n\tb2Vec2 c = { b.x - a.x, b.y - a.y };\n\treturn c.x * c.x + c.y * c.y;\n}\n\n/// Make a rotation using an angle in radians\nB2_INLINE b2Rot b2MakeRot( float radians )\n{\n\tb2CosSin cs = b2ComputeCosSin( radians );\n\treturn B2_LITERAL( b2Rot ){ cs.cosine, cs.sine };\n}\n\n/// Make a rotation using a unit vector\nB2_INLINE b2Rot b2MakeRotFromUnitVector( b2Vec2 unitVector )\n{\n\tB2_ASSERT( b2IsNormalized( unitVector ) );\n\treturn B2_LITERAL( b2Rot ){ unitVector.x, unitVector.y };\n}\n\n/// Compute the rotation between two unit vectors\nB2_API b2Rot b2ComputeRotationBetweenUnitVectors( b2Vec2 v1, b2Vec2 v2 );\n\n/// Is this rotation normalized?\nB2_INLINE bool b2IsNormalizedRot( b2Rot q )\n{\n\t// larger tolerance due to failure on mingw 32-bit\n\tfloat qq = q.s * q.s + q.c * q.c;\n\treturn 1.0f - 0.0006f < qq && qq < 1.0f + 0.0006f;\n}\n\n/// Get the inverse of a rotation\nB2_INLINE b2Rot b2InvertRot( b2Rot a )\n{\n\treturn B2_LITERAL( b2Rot ){\n\t\t.c = a.c,\n\t\t.s = -a.s,\n\t};\n}\n\n/// Normalized linear interpolation\n/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/\n///\thttps://web.archive.org/web/20170825184056/http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/\nB2_INLINE b2Rot b2NLerp( b2Rot q1, b2Rot q2, float t )\n{\n\tfloat omt = 1.0f - t;\n\tb2Rot q = {\n\t\tomt * q1.c + t * q2.c,\n\t\tomt * q1.s + t * q2.s,\n\t};\n\n\tfloat mag = sqrtf( q.s * q.s + q.c * q.c );\n\tfloat invMag = mag > 0.0f ? 1.0f / mag : 0.0f;\n\tb2Rot qn = { q.c * invMag, q.s * invMag };\n\treturn qn;\n}\n\n/// Compute the angular velocity necessary to rotate between two rotations over a give time\n/// @param q1 initial rotation\n/// @param q2 final rotation\n/// @param inv_h inverse time step\nB2_INLINE float b2ComputeAngularVelocity( b2Rot q1, b2Rot q2, float inv_h )\n{\n\t// ds/dt = omega * cos(t)\n\t// dc/dt = -omega * sin(t)\n\t// s2 = s1 + omega * h * c1\n\t// c2 = c1 - omega * h * s1\n\n\t// omega * h * s1 = c1 - c2\n\t// omega * h * c1 = s2 - s1\n\t// omega * h = (c1 - c2) * s1 + (s2 - s1) * c1;\n\t// omega * h = s1 * c1 - c2 * s1 + s2 * c1 - s1 * c1\n\t// omega * h = s2 * c1 - c2 * s1 = sin(a2 - a1) ~= a2 - a1 for small delta\n\tfloat omega = inv_h * ( q2.s * q1.c - q2.c * q1.s );\n\treturn omega;\n}\n\n/// Get the angle in radians in the range [-pi, pi]\nB2_INLINE float b2Rot_GetAngle( b2Rot q )\n{\n\treturn b2Atan2( q.s, q.c );\n}\n\n/// Get the x-axis\nB2_INLINE b2Vec2 b2Rot_GetXAxis( b2Rot q )\n{\n\tb2Vec2 v = { q.c, q.s };\n\treturn v;\n}\n\n/// Get the y-axis\nB2_INLINE b2Vec2 b2Rot_GetYAxis( b2Rot q )\n{\n\tb2Vec2 v = { -q.s, q.c };\n\treturn v;\n}\n\n/// Multiply two rotations: q * r\nB2_INLINE b2Rot b2MulRot( b2Rot q, b2Rot r )\n{\n\t// [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc]\n\t// [qs  qc]   [rs  rc]   [qs*rc+qc*rs -qs*rs+qc*rc]\n\t// s(q + r) = qs * rc + qc * rs\n\t// c(q + r) = qc * rc - qs * rs\n\tb2Rot qr;\n\tqr.s = q.s * r.c + q.c * r.s;\n\tqr.c = q.c * r.c - q.s * r.s;\n\treturn qr;\n}\n\n/// Transpose multiply two rotations: inv(a) * b\n/// This rotates a vector local in frame b into a vector local in frame a\nB2_INLINE b2Rot b2InvMulRot( b2Rot a, b2Rot b )\n{\n\t// [ ac as] * [bc -bs] = [ac*bc+qs*bs -ac*bs+as*bc]\n\t// [-as ac]   [bs  bc]   [-as*bc+ac*bs as*bs+ac*bc]\n\t// s(a - b) = ac * bs - as * bc\n\t// c(a - b) = ac * bc + as * bs\n\tb2Rot r;\n\tr.s = a.c * b.s - a.s * b.c;\n\tr.c = a.c * b.c + a.s * b.s;\n\treturn r;\n}\n\n/// Relative angle between a and b\nB2_INLINE float b2RelativeAngle( b2Rot a, b2Rot b )\n{\n\t// sin(b - a) = bs * ac - bc * as\n\t// cos(b - a) = bc * ac + bs * as\n\tfloat s = a.c * b.s - a.s * b.c;\n\tfloat c = a.c * b.c + a.s * b.s;\n\treturn b2Atan2( s, c );\n}\n\n/// Convert any angle into the range [-pi, pi]\nB2_INLINE float b2UnwindAngle( float radians )\n{\n\t// Assuming this is deterministic\n\treturn remainderf( radians, 2.0f * B2_PI );\n}\n\n/// Rotate a vector\nB2_INLINE b2Vec2 b2RotateVector( b2Rot q, b2Vec2 v )\n{\n\treturn B2_LITERAL( b2Vec2 ){ q.c * v.x - q.s * v.y, q.s * v.x + q.c * v.y };\n}\n\n/// Inverse rotate a vector\nB2_INLINE b2Vec2 b2InvRotateVector( b2Rot q, b2Vec2 v )\n{\n\treturn B2_LITERAL( b2Vec2 ){ q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y };\n}\n\n/// Transform a point (e.g. local space to world space)\nB2_INLINE b2Vec2 b2TransformPoint( b2Transform t, const b2Vec2 p )\n{\n\tfloat x = ( t.q.c * p.x - t.q.s * p.y ) + t.p.x;\n\tfloat y = ( t.q.s * p.x + t.q.c * p.y ) + t.p.y;\n\n\treturn B2_LITERAL( b2Vec2 ){ x, y };\n}\n\n/// Inverse transform a point (e.g. world space to local space)\nB2_INLINE b2Vec2 b2InvTransformPoint( b2Transform t, const b2Vec2 p )\n{\n\tfloat vx = p.x - t.p.x;\n\tfloat vy = p.y - t.p.y;\n\treturn B2_LITERAL( b2Vec2 ){ t.q.c * vx + t.q.s * vy, -t.q.s * vx + t.q.c * vy };\n}\n\n/// Multiply two transforms. If the result is applied to a point p local to frame B,\n/// the transform would first convert p to a point local to frame A, then into a point\n/// in the world frame.\n/// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p\n///    = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p\nB2_INLINE b2Transform b2MulTransforms( b2Transform A, b2Transform B )\n{\n\tb2Transform C;\n\tC.q = b2MulRot( A.q, B.q );\n\tC.p = b2Add( b2RotateVector( A.q, B.p ), A.p );\n\treturn C;\n}\n\n/// Creates a transform that converts a local point in frame B to a local point in frame A.\n/// v2 = A.q' * (B.q * v1 + B.p - A.p)\n///    = A.q' * B.q * v1 + A.q' * (B.p - A.p)\nB2_INLINE b2Transform b2InvMulTransforms( b2Transform A, b2Transform B )\n{\n\tb2Transform C;\n\tC.q = b2InvMulRot( A.q, B.q );\n\tC.p = b2InvRotateVector( A.q, b2Sub( B.p, A.p ) );\n\treturn C;\n}\n\n/// Multiply a 2-by-2 matrix times a 2D vector\nB2_INLINE b2Vec2 b2MulMV( b2Mat22 A, b2Vec2 v )\n{\n\tb2Vec2 u = {\n\t\tA.cx.x * v.x + A.cy.x * v.y,\n\t\tA.cx.y * v.x + A.cy.y * v.y,\n\t};\n\treturn u;\n}\n\n/// Get the inverse of a 2-by-2 matrix\nB2_INLINE b2Mat22 b2GetInverse22( b2Mat22 A )\n{\n\tfloat a = A.cx.x, b = A.cy.x, c = A.cx.y, d = A.cy.y;\n\tfloat det = a * d - b * c;\n\tif ( det != 0.0f )\n\t{\n\t\tdet = 1.0f / det;\n\t}\n\n\tb2Mat22 B = {\n\t\t{ det * d, -det * c },\n\t\t{ -det * b, det * a },\n\t};\n\treturn B;\n}\n\n/// Solve A * x = b, where b is a column vector. This is more efficient\n/// than computing the inverse in one-shot cases.\nB2_INLINE b2Vec2 b2Solve22( b2Mat22 A, b2Vec2 b )\n{\n\tfloat a11 = A.cx.x, a12 = A.cy.x, a21 = A.cx.y, a22 = A.cy.y;\n\tfloat det = a11 * a22 - a12 * a21;\n\tif ( det != 0.0f )\n\t{\n\t\tdet = 1.0f / det;\n\t}\n\tb2Vec2 x = { det * ( a22 * b.x - a12 * b.y ), det * ( a11 * b.y - a21 * b.x ) };\n\treturn x;\n}\n\n/// Does a fully contain b\nB2_INLINE bool b2AABB_Contains( b2AABB a, b2AABB b )\n{\n\tbool s = true;\n\ts = s && a.lowerBound.x <= b.lowerBound.x;\n\ts = s && a.lowerBound.y <= b.lowerBound.y;\n\ts = s && b.upperBound.x <= a.upperBound.x;\n\ts = s && b.upperBound.y <= a.upperBound.y;\n\treturn s;\n}\n\n/// Get the center of the AABB.\nB2_INLINE b2Vec2 b2AABB_Center( b2AABB a )\n{\n\tb2Vec2 b = { 0.5f * ( a.lowerBound.x + a.upperBound.x ), 0.5f * ( a.lowerBound.y + a.upperBound.y ) };\n\treturn b;\n}\n\n/// Get the extents of the AABB (half-widths).\nB2_INLINE b2Vec2 b2AABB_Extents( b2AABB a )\n{\n\tb2Vec2 b = { 0.5f * ( a.upperBound.x - a.lowerBound.x ), 0.5f * ( a.upperBound.y - a.lowerBound.y ) };\n\treturn b;\n}\n\n/// Union of two AABBs\nB2_INLINE b2AABB b2AABB_Union( b2AABB a, b2AABB b )\n{\n\tb2AABB c;\n\tc.lowerBound.x = b2MinFloat( a.lowerBound.x, b.lowerBound.x );\n\tc.lowerBound.y = b2MinFloat( a.lowerBound.y, b.lowerBound.y );\n\tc.upperBound.x = b2MaxFloat( a.upperBound.x, b.upperBound.x );\n\tc.upperBound.y = b2MaxFloat( a.upperBound.y, b.upperBound.y );\n\treturn c;\n}\n\n/// Do a and b overlap\nB2_INLINE bool b2AABB_Overlaps( b2AABB a, b2AABB b )\n{\n\treturn !( b.lowerBound.x > a.upperBound.x || b.lowerBound.y > a.upperBound.y || a.lowerBound.x > b.upperBound.x ||\n\t\t\t  a.lowerBound.y > b.upperBound.y );\n}\n\n/// Compute the bounding box of an array of circles\nB2_INLINE b2AABB b2MakeAABB( const b2Vec2* points, int count, float radius )\n{\n\tB2_ASSERT( count > 0 );\n\tb2AABB a = { points[0], points[0] };\n\tfor ( int i = 1; i < count; ++i )\n\t{\n\t\ta.lowerBound = b2Min( a.lowerBound, points[i] );\n\t\ta.upperBound = b2Max( a.upperBound, points[i] );\n\t}\n\n\tb2Vec2 r = { radius, radius };\n\ta.lowerBound = b2Sub( a.lowerBound, r );\n\ta.upperBound = b2Add( a.upperBound, r );\n\n\treturn a;\n}\n\n/// Signed separation of a point from a plane\nB2_INLINE float b2PlaneSeparation( b2Plane plane, b2Vec2 point )\n{\n\treturn b2Dot( plane.normal, point ) - plane.offset;\n}\n\n/// One-dimensional mass-spring-damper simulation. Returns the new velocity given the position and time step.\n/// You can then compute the new position using:\n/// position += timeStep * newVelocity\n/// This drives towards a zero position. By using implicit integration we get a stable solution\n/// that doesn't require transcendental functions.\nB2_INLINE float b2SpringDamper( float hertz, float dampingRatio, float position, float velocity, float timeStep )\n{\n\tfloat omega = 2.0f * B2_PI * hertz;\n\tfloat omegaH = omega * timeStep;\n\treturn ( velocity - omega * omegaH * position ) / ( 1.0f + 2.0f * dampingRatio * omegaH + omegaH * omegaH );\n}\n\n/// Box2D bases all length units on meters, but you may need different units for your game.\n/// You can set this value to use different units. This should be done at application startup\n/// and only modified once. Default value is 1.\n/// For example, if your game uses pixels for units you can use pixels for all length values\n/// sent to Box2D. There should be no extra cost. However, Box2D has some internal tolerances\n/// and thresholds that have been tuned for meters. By calling this function, Box2D is able\n/// to adjust those tolerances and thresholds to improve accuracy.\n/// A good rule of thumb is to pass the height of your player character to this function. So\n/// if your player character is 32 pixels high, then pass 32 to this function. Then you may\n/// confidently use pixels for all the length values sent to Box2D. All length values returned\n/// from Box2D will also be pixels because Box2D does not do any scaling internally.\n/// However, you are now on the hook for coming up with good values for gravity, density, and\n/// forces.\n/// @warning This must be modified before any calls to Box2D\nB2_API void b2SetLengthUnitsPerMeter( float lengthUnits );\n\n/// Get the current length units per meter.\nB2_API float b2GetLengthUnitsPerMeter( void );\n\n/**@}*/\n\n/**\n * @defgroup math_cpp C++ Math\n * @brief Math operator overloads for C++\n *\n * See math_functions.h for details.\n * @{\n */\n\n#ifdef __cplusplus\n\n/// Unary add one vector to another\ninline void operator+=( b2Vec2& a, b2Vec2 b )\n{\n\ta.x += b.x;\n\ta.y += b.y;\n}\n\n/// Unary subtract one vector from another\ninline void operator-=( b2Vec2& a, b2Vec2 b )\n{\n\ta.x -= b.x;\n\ta.y -= b.y;\n}\n\n/// Unary multiply a vector by a scalar\ninline void operator*=( b2Vec2& a, float b )\n{\n\ta.x *= b;\n\ta.y *= b;\n}\n\n/// Unary negate a vector\ninline b2Vec2 operator-( b2Vec2 a )\n{\n\treturn { -a.x, -a.y };\n}\n\n/// Binary vector addition\ninline b2Vec2 operator+( b2Vec2 a, b2Vec2 b )\n{\n\treturn { a.x + b.x, a.y + b.y };\n}\n\n/// Binary vector subtraction\ninline b2Vec2 operator-( b2Vec2 a, b2Vec2 b )\n{\n\treturn { a.x - b.x, a.y - b.y };\n}\n\n/// Binary scalar and vector multiplication\ninline b2Vec2 operator*( float a, b2Vec2 b )\n{\n\treturn { a * b.x, a * b.y };\n}\n\n/// Binary scalar and vector multiplication\ninline b2Vec2 operator*( b2Vec2 a, float b )\n{\n\treturn { a.x * b, a.y * b };\n}\n\n/// Binary vector equality\ninline bool operator==( b2Vec2 a, b2Vec2 b )\n{\n\treturn a.x == b.x && a.y == b.y;\n}\n\n/// Binary vector inequality\ninline bool operator!=( b2Vec2 a, b2Vec2 b )\n{\n\treturn a.x != b.x || a.y != b.y;\n}\n\n#endif\n\n/**@}*/\n"
  },
  {
    "path": "include/box2d/types.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"base.h\"\n#include \"collision.h\"\n#include \"id.h\"\n#include \"math_functions.h\"\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#define B2_DEFAULT_CATEGORY_BITS 1\n#define B2_DEFAULT_MASK_BITS UINT64_MAX\n\n/// Task interface\n/// This is prototype for a Box2D task. Your task system is expected to invoke the Box2D task with these arguments.\n/// The task spans a range of the parallel-for: [startIndex, endIndex)\n/// The worker index must correctly identify each worker in the user thread pool, expected in [0, workerCount).\n/// A worker must only exist on only one thread at a time and is analogous to the thread index.\n/// The task context is the context pointer sent from Box2D when it is enqueued.\n/// The startIndex and endIndex are expected in the range [0, itemCount) where itemCount is the argument to b2EnqueueTaskCallback\n/// below. Box2D expects startIndex < endIndex and will execute a loop like this:\n///\n/// @code{.c}\n/// for (int i = startIndex; i < endIndex; ++i)\n/// {\n/// \tDoWork();\n/// }\n/// @endcode\n/// @ingroup world\ntypedef void b2TaskCallback( int startIndex, int endIndex, uint32_t workerIndex, void* taskContext );\n\n/// These functions can be provided to Box2D to invoke a task system. These are designed to work well with enkiTS.\n/// Returns a pointer to the user's task object. May be nullptr. A nullptr indicates to Box2D that the work was executed\n/// serially within the callback and there is no need to call b2FinishTaskCallback.\n/// The itemCount is the number of Box2D work items that are to be partitioned among workers by the user's task system.\n/// This is essentially a parallel-for. The minRange parameter is a suggestion of the minimum number of items to assign\n/// per worker to reduce overhead. For example, suppose the task is small and that itemCount is 16. A minRange of 8 suggests\n/// that your task system should split the work items among just two workers, even if you have more available.\n/// In general the range [startIndex, endIndex) send to b2TaskCallback should obey:\n/// endIndex - startIndex >= minRange\n/// The exception of course is when itemCount < minRange.\n/// @ingroup world\ntypedef void* b2EnqueueTaskCallback( b2TaskCallback* task, int itemCount, int minRange, void* taskContext, void* userContext );\n\n/// Finishes a user task object that wraps a Box2D task.\n/// @ingroup world\ntypedef void b2FinishTaskCallback( void* userTask, void* userContext );\n\n/// Optional friction mixing callback. This intentionally provides no context objects because this is called\n/// from a worker thread.\n/// @warning This function should not attempt to modify Box2D state or user application state.\n/// @ingroup world\ntypedef float b2FrictionCallback( float frictionA, uint64_t userMaterialIdA, float frictionB, uint64_t userMaterialIdB );\n\n/// Optional restitution mixing callback. This intentionally provides no context objects because this is called\n/// from a worker thread.\n/// @warning This function should not attempt to modify Box2D state or user application state.\n/// @ingroup world\ntypedef float b2RestitutionCallback( float restitutionA, uint64_t userMaterialIdA, float restitutionB, uint64_t userMaterialIdB );\n\n/// Result from b2World_RayCastClosest\n/// If there is initial overlap the fraction and normal will be zero while the point is an arbitrary point in the overlap region.\n/// @ingroup world\ntypedef struct b2RayResult\n{\n\tb2ShapeId shapeId;\n\tb2Vec2 point;\n\tb2Vec2 normal;\n\tfloat fraction;\n\tint nodeVisits;\n\tint leafVisits;\n\tbool hit;\n} b2RayResult;\n\n/// World definition used to create a simulation world.\n/// Must be initialized using b2DefaultWorldDef().\n/// @ingroup world\ntypedef struct b2WorldDef\n{\n\t/// Gravity vector. Box2D has no up-vector defined.\n\tb2Vec2 gravity;\n\n\t/// Restitution speed threshold, usually in m/s. Collisions above this\n\t/// speed have restitution applied (will bounce).\n\tfloat restitutionThreshold;\n\n\t/// Threshold speed for hit events. Usually meters per second.\n\tfloat hitEventThreshold;\n\n\t/// Contact stiffness. Cycles per second. Increasing this increases the speed of overlap recovery, but can introduce jitter.\n\tfloat contactHertz;\n\n\t/// Contact bounciness. Non-dimensional. You can speed up overlap recovery by decreasing this with\n\t/// the trade-off that overlap resolution becomes more energetic.\n\tfloat contactDampingRatio;\n\n\t/// This parameter controls how fast overlap is resolved and usually has units of meters per second. This only\n\t/// puts a cap on the resolution speed. The resolution speed is increased by increasing the hertz and/or\n\t/// decreasing the damping ratio.\n\tfloat contactSpeed;\n\n\t/// Maximum linear speed. Usually meters per second.\n\tfloat maximumLinearSpeed;\n\n\t/// Optional mixing callback for friction. The default uses sqrt(frictionA * frictionB).\n\tb2FrictionCallback* frictionCallback;\n\n\t/// Optional mixing callback for restitution. The default uses max(restitutionA, restitutionB).\n\tb2RestitutionCallback* restitutionCallback;\n\n\t/// Can bodies go to sleep to improve performance\n\tbool enableSleep;\n\n\t/// Enable continuous collision\n\tbool enableContinuous;\n\n\t/// Contact softening when mass ratios are large. Experimental.\n\tbool enableContactSoftening;\n\n\t/// Number of workers to use with the provided task system. Box2D performs best when using only\n\t/// performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide\n\t/// little benefit and may even harm performance.\n\t/// @note Box2D does not create threads. This is the number of threads your applications has created\n\t/// that you are allocating to b2World_Step.\n\t/// @warning Do not modify the default value unless you are also providing a task system and providing\n\t/// task callbacks (enqueueTask and finishTask).\n\tint workerCount;\n\n\t/// Function to spawn tasks\n\tb2EnqueueTaskCallback* enqueueTask;\n\n\t/// Function to finish a task\n\tb2FinishTaskCallback* finishTask;\n\n\t/// User context that is provided to enqueueTask and finishTask\n\tvoid* userTaskContext;\n\n\t/// User data\n\tvoid* userData;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2WorldDef;\n\n/// Use this to initialize your world definition\n/// @ingroup world\nB2_API b2WorldDef b2DefaultWorldDef( void );\n\n/// The body simulation type.\n/// Each body is one of these three types. The type determines how the body behaves in the simulation.\n/// @ingroup body\ntypedef enum b2BodyType\n{\n\t/// zero mass, zero velocity, may be manually moved\n\tb2_staticBody = 0,\n\n\t/// zero mass, velocity set by user, moved by solver\n\tb2_kinematicBody = 1,\n\n\t/// positive mass, velocity determined by forces, moved by solver\n\tb2_dynamicBody = 2,\n\n\t/// number of body types\n\tb2_bodyTypeCount,\n} b2BodyType;\n\n/// Motion locks to restrict the body movement\ntypedef struct b2MotionLocks\n{\n\t/// Prevent translation along the x-axis\n\tbool linearX;\n\n\t/// Prevent translation along the y-axis\n\tbool linearY;\n\n\t/// Prevent rotation around the z-axis\n\tbool angularZ;\n} b2MotionLocks;\n\n/// A body definition holds all the data needed to construct a rigid body.\n/// You can safely re-use body definitions. Shapes are added to a body after construction.\n/// Body definitions are temporary objects used to bundle creation parameters.\n/// Must be initialized using b2DefaultBodyDef().\n/// @ingroup body\ntypedef struct b2BodyDef\n{\n\t/// The body type: static, kinematic, or dynamic.\n\tb2BodyType type;\n\n\t/// The initial world position of the body. Bodies should be created with the desired position.\n\t/// @note Creating bodies at the origin and then moving them nearly doubles the cost of body creation, especially\n\t/// if the body is moved after shapes have been added.\n\tb2Vec2 position;\n\n\t/// The initial world rotation of the body. Use b2MakeRot() if you have an angle.\n\tb2Rot rotation;\n\n\t/// The initial linear velocity of the body's origin. Usually in meters per second.\n\tb2Vec2 linearVelocity;\n\n\t/// The initial angular velocity of the body. Radians per second.\n\tfloat angularVelocity;\n\n\t/// Linear damping is used to reduce the linear velocity. The damping parameter\n\t/// can be larger than 1 but the damping effect becomes sensitive to the\n\t/// time step when the damping parameter is large.\n\t/// Generally linear damping is undesirable because it makes objects move slowly\n\t/// as if they are floating.\n\tfloat linearDamping;\n\n\t/// Angular damping is used to reduce the angular velocity. The damping parameter\n\t/// can be larger than 1.0f but the damping effect becomes sensitive to the\n\t/// time step when the damping parameter is large.\n\t/// Angular damping can be use slow down rotating bodies.\n\tfloat angularDamping;\n\n\t/// Scale the gravity applied to this body. Non-dimensional.\n\tfloat gravityScale;\n\n\t/// Sleep speed threshold, default is 0.05 meters per second\n\tfloat sleepThreshold;\n\n\t/// Optional body name for debugging. Up to 31 characters (excluding null termination)\n\tconst char* name;\n\n\t/// Use this to store application specific body data.\n\tvoid* userData;\n\n\t/// Motions locks to restrict linear and angular movement.\n\t/// Caution: may lead to softer constraints along the locked direction\n\tb2MotionLocks motionLocks;\n\n\t/// Set this flag to false if this body should never fall asleep.\n\tbool enableSleep;\n\n\t/// Is this body initially awake or sleeping?\n\tbool isAwake;\n\n\t/// Treat this body as a high speed object that performs continuous collision detection\n\t/// against dynamic and kinematic bodies, but not other bullet bodies.\n\t/// @warning Bullets should be used sparingly. They are not a solution for general dynamic-versus-dynamic\n\t/// continuous collision. They do not guarantee accurate collision if both bodies are fast moving because\n\t/// the bullet does a continuous check after all non-bullet bodies have moved. You could get unlucky and have\n\t/// the bullet body end a time step very close to a non-bullet body and the non-bullet body then moves over\n\t/// the bullet body. In continuous collision, initial overlap is ignored to avoid freezing bodies in place.\n\t/// I do not recommend using them for game projectiles if precise collision timing is needed. Instead consider\n\t/// using a ray or shape cast. You can use a marching ray or shape cast for projectile that moves over time.\n\t/// If you want a fast moving projectile to collide with a fast moving target, you need to consider the relative\n\t/// movement in your ray or shape cast. This is out of the scope of Box2D.\n\t/// So what are good use cases for bullets? Pinball games or games with dynamic containers that hold other objects.\n\t/// It should be a use case where it doesn't break the game if there is a collision missed, but the having them\n\t/// captured improves the quality of the game.\n\tbool isBullet;\n\n\t/// Used to disable a body. A disabled body does not move or collide.\n\tbool isEnabled;\n\n\t/// This allows this body to bypass rotational speed limits. Should only be used\n\t/// for circular objects, like wheels.\n\tbool allowFastRotation;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2BodyDef;\n\n/// Use this to initialize your body definition\n/// @ingroup body\nB2_API b2BodyDef b2DefaultBodyDef( void );\n\n/// This is used to filter collision on shapes. It affects shape-vs-shape collision\n/// and shape-versus-query collision (such as b2World_CastRay).\n/// @ingroup shape\ntypedef struct b2Filter\n{\n\t/// The collision category bits. Normally you would just set one bit. The category bits should\n\t/// represent your application object types. For example:\n\t/// @code{.cpp}\n\t/// enum MyCategories\n\t/// {\n\t///    Static  = 0x00000001,\n\t///    Dynamic = 0x00000002,\n\t///    Debris  = 0x00000004,\n\t///    Player  = 0x00000008,\n\t///    // etc\n\t/// };\n\t/// @endcode\n\tuint64_t categoryBits;\n\n\t/// The collision mask bits. This states the categories that this\n\t/// shape would accept for collision.\n\t/// For example, you may want your player to only collide with static objects\n\t/// and other players.\n\t/// @code{.c}\n\t/// maskBits = Static | Player;\n\t/// @endcode\n\tuint64_t maskBits;\n\n\t/// Collision groups allow a certain group of objects to never collide (negative)\n\t/// or always collide (positive). A group index of zero has no effect. Non-zero group filtering\n\t/// always wins against the mask bits.\n\t/// For example, you may want ragdolls to collide with other ragdolls but you don't want\n\t/// ragdoll self-collision. In this case you would give each ragdoll a unique negative group index\n\t/// and apply that group index to all shapes on the ragdoll.\n\tint groupIndex;\n} b2Filter;\n\n/// Use this to initialize your filter\n/// @ingroup shape\nB2_API b2Filter b2DefaultFilter( void );\n\n/// The query filter is used to filter collisions between queries and shapes. For example,\n/// you may want a ray-cast representing a projectile to hit players and the static environment\n/// but not debris.\n/// @ingroup shape\ntypedef struct b2QueryFilter\n{\n\t/// The collision category bits of this query. Normally you would just set one bit.\n\tuint64_t categoryBits;\n\n\t/// The collision mask bits. This states the shape categories that this\n\t/// query would accept for collision.\n\tuint64_t maskBits;\n} b2QueryFilter;\n\n/// Use this to initialize your query filter\n/// @ingroup shape\nB2_API b2QueryFilter b2DefaultQueryFilter( void );\n\n/// Shape type\n/// @ingroup shape\ntypedef enum b2ShapeType\n{\n\t/// A circle with an offset\n\tb2_circleShape,\n\n\t/// A capsule is an extruded circle\n\tb2_capsuleShape,\n\n\t/// A line segment\n\tb2_segmentShape,\n\n\t/// A convex polygon\n\tb2_polygonShape,\n\n\t/// A line segment owned by a chain shape\n\tb2_chainSegmentShape,\n\n\t/// The number of shape types\n\tb2_shapeTypeCount\n} b2ShapeType;\n\n/// Surface materials allow chain shapes to have per segment surface properties.\n/// @ingroup shape\ntypedef struct b2SurfaceMaterial\n{\n\t/// The Coulomb (dry) friction coefficient, usually in the range [0,1].\n\tfloat friction;\n\n\t/// The coefficient of restitution (bounce) usually in the range [0,1].\n\t/// https://en.wikipedia.org/wiki/Coefficient_of_restitution\n\tfloat restitution;\n\n\t/// The rolling resistance usually in the range [0,1].\n\tfloat rollingResistance;\n\n\t/// The tangent speed for conveyor belts\n\tfloat tangentSpeed;\n\n\t/// User material identifier. This is passed with query results and to friction and restitution\n\t/// combining functions. It is not used internally.\n\tuint64_t userMaterialId;\n\n\t/// Custom debug draw color.\n\tuint32_t customColor;\n} b2SurfaceMaterial;\n\n/// Use this to initialize your surface material\n/// @ingroup shape\nB2_API b2SurfaceMaterial b2DefaultSurfaceMaterial( void );\n\n/// Used to create a shape.\n/// This is a temporary object used to bundle shape creation parameters. You may use\n/// the same shape definition to create multiple shapes.\n/// Must be initialized using b2DefaultShapeDef().\n/// @ingroup shape\ntypedef struct b2ShapeDef\n{\n\t/// Use this to store application specific shape data.\n\tvoid* userData;\n\n\t/// The surface material for this shape.\n\tb2SurfaceMaterial material;\n\n\t/// The density, usually in kg/m^2.\n\t/// This is not part of the surface material because this is for the interior, which may have\n\t/// other considerations, such as being hollow. For example a wood barrel may be hollow or full of water.\n\tfloat density;\n\n\t/// Collision filtering data.\n\tb2Filter filter;\n\n\t/// Enable custom filtering. Only one of the two shapes needs to enable custom filtering. See b2WorldDef.\n\tbool enableCustomFiltering;\n\n\t/// A sensor shape generates overlap events but never generates a collision response.\n\t/// Sensors do not have continuous collision. Instead, use a ray or shape cast for those scenarios.\n\t/// Sensors still contribute to the body mass if they have non-zero density.\n\t/// @note Sensor events are disabled by default.\n\t/// @see enableSensorEvents\n\tbool isSensor;\n\n\t/// Enable sensor events for this shape. This applies to sensors and non-sensors. False by default, even for sensors.\n\tbool enableSensorEvents;\n\n\t/// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default.\n\tbool enableContactEvents;\n\n\t/// Enable hit events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default.\n\tbool enableHitEvents;\n\n\t/// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive\n\t/// and must be carefully handled due to multithreading. Ignored for sensors.\n\tbool enablePreSolveEvents;\n\n\t/// When shapes are created they will scan the environment for collision the next time step. This can significantly slow down\n\t/// static body creation when there are many static shapes.\n\t/// This is flag is ignored for dynamic and kinematic shapes which always invoke contact creation.\n\tbool invokeContactCreation;\n\n\t/// Should the body update the mass properties when this shape is created. Default is true.\n\t/// Warning: if this is true, you MUST call b2Body_ApplyMassFromShapes before simulating the world.\n\tbool updateBodyMass;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2ShapeDef;\n\n/// Use this to initialize your shape definition\n/// @ingroup shape\nB2_API b2ShapeDef b2DefaultShapeDef( void );\n\n/// Used to create a chain of line segments. This is designed to eliminate ghost collisions with some limitations.\n/// - chains are one-sided\n/// - chains have no mass and should be used on static bodies\n/// - chains have a counter-clockwise winding order (normal points right of segment direction)\n/// - chains are either a loop or open\n/// - a chain must have at least 4 points\n/// - the distance between any two points must be greater than B2_LINEAR_SLOP\n/// - a chain shape should not self intersect (this is not validated)\n/// - an open chain shape has NO COLLISION on the first and final edge\n/// - you may overlap two open chains on their first three and/or last three points to get smooth collision\n/// - a chain shape creates multiple line segment shapes on the body\n/// https://en.wikipedia.org/wiki/Polygonal_chain\n/// Must be initialized using b2DefaultChainDef().\n/// @warning Do not use chain shapes unless you understand the limitations. This is an advanced feature.\n/// @ingroup shape\ntypedef struct b2ChainDef\n{\n\t/// Use this to store application specific shape data.\n\tvoid* userData;\n\n\t/// An array of at least 4 points. These are cloned and may be temporary.\n\tconst b2Vec2* points;\n\n\t/// The point count, must be 4 or more.\n\tint count;\n\n\t/// Surface materials for each segment. These are cloned.\n\tconst b2SurfaceMaterial* materials;\n\n\t/// The material count. Must be 1 or count. This allows you to provide one\n\t/// material for all segments or a unique material per segment. For open\n\t/// chains, the material on the ghost segments are place holders.\n\tint materialCount;\n\n\t/// Contact filtering data.\n\tb2Filter filter;\n\n\t/// Indicates a closed chain formed by connecting the first and last points\n\tbool isLoop;\n\n\t/// Enable sensors to detect this chain. False by default.\n\tbool enableSensorEvents;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2ChainDef;\n\n/// Use this to initialize your chain definition\n/// @ingroup shape\nB2_API b2ChainDef b2DefaultChainDef( void );\n\n//! @cond\n/// Profiling data. Times are in milliseconds.\ntypedef struct b2Profile\n{\n\tfloat step;\n\tfloat pairs;\n\tfloat collide;\n\tfloat solve;\n\tfloat prepareStages;\n\tfloat solveConstraints;\n\tfloat prepareConstraints;\n\tfloat integrateVelocities;\n\tfloat warmStart;\n\tfloat solveImpulses;\n\tfloat integratePositions;\n\tfloat relaxImpulses;\n\tfloat applyRestitution;\n\tfloat storeImpulses;\n\tfloat splitIslands;\n\tfloat transforms;\n\tfloat sensorHits;\n\tfloat jointEvents;\n\tfloat hitEvents;\n\tfloat refit;\n\tfloat bullets;\n\tfloat sleepIslands;\n\tfloat sensors;\n} b2Profile;\n\n/// Counters that give details of the simulation size.\ntypedef struct b2Counters\n{\n\tint bodyCount;\n\tint shapeCount;\n\tint contactCount;\n\tint jointCount;\n\tint islandCount;\n\tint stackUsed;\n\tint staticTreeHeight;\n\tint treeHeight;\n\tint byteCount;\n\tint taskCount;\n\tint colorCounts[24];\n} b2Counters;\n//! @endcond\n\n/// Joint type enumeration\n///\n/// This is useful because all joint types use b2JointId and sometimes you\n/// want to get the type of a joint.\n/// @ingroup joint\ntypedef enum b2JointType\n{\n\tb2_distanceJoint,\n\tb2_filterJoint,\n\tb2_motorJoint,\n\tb2_prismaticJoint,\n\tb2_revoluteJoint,\n\tb2_weldJoint,\n\tb2_wheelJoint,\n} b2JointType;\n\n/// Base joint definition used by all joint types.\n/// The local frames are measured from the body's origin rather than the center of mass because:\n/// 1. you might not know where the center of mass will be\n/// 2. if you add/remove shapes from a body and recompute the mass, the joints will be broken\ntypedef struct b2JointDef\n{\n\t/// User data pointer\n\tvoid* userData;\n\n\t/// The first attached body\n\tb2BodyId bodyIdA;\n\n\t/// The second attached body\n\tb2BodyId bodyIdB;\n\n\t/// The first local joint frame\n\tb2Transform localFrameA;\n\n\t/// The second local joint frame\n\tb2Transform localFrameB;\n\n\t/// Force threshold for joint events\n\tfloat forceThreshold;\n\n\t/// Torque threshold for joint events\n\tfloat torqueThreshold;\n\n\t/// Constraint hertz (advanced feature)\n\tfloat constraintHertz;\n\n\t/// Constraint damping ratio (advanced feature)\n\tfloat constraintDampingRatio;\n\n\t/// Debug draw scale\n\tfloat drawScale;\n\n\t/// Set this flag to true if the attached bodies should collide\n\tbool collideConnected;\n\n} b2JointDef;\n\n/// Distance joint definition\n/// Connects a point on body A with a point on body B by a segment.\n/// Useful for ropes and springs.\n/// @ingroup distance_joint\ntypedef struct b2DistanceJointDef\n{\n\t/// Base joint definition\n\tb2JointDef base;\n\n\t/// The rest length of this joint. Clamped to a stable minimum value.\n\tfloat length;\n\n\t/// Enable the distance constraint to behave like a spring. If false\n\t/// then the distance joint will be rigid, overriding the limit and motor.\n\tbool enableSpring;\n\n\t/// The lower spring force controls how much tension it can sustain\n\tfloat lowerSpringForce;\n\n\t/// The upper spring force controls how much compression it an sustain\n\tfloat upperSpringForce;\n\n\t/// The spring linear stiffness Hertz, cycles per second\n\tfloat hertz;\n\n\t/// The spring linear damping ratio, non-dimensional\n\tfloat dampingRatio;\n\n\t/// Enable/disable the joint limit\n\tbool enableLimit;\n\n\t/// Minimum length for limit. Clamped to a stable minimum value.\n\tfloat minLength;\n\n\t/// Maximum length for limit. Must be greater than or equal to the minimum length.\n\tfloat maxLength;\n\n\t/// Enable/disable the joint motor\n\tbool enableMotor;\n\n\t/// The maximum motor force, usually in newtons\n\tfloat maxMotorForce;\n\n\t/// The desired motor speed, usually in meters per second\n\tfloat motorSpeed;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2DistanceJointDef;\n\n/// Use this to initialize your joint definition\n/// @ingroup distance_joint\nB2_API b2DistanceJointDef b2DefaultDistanceJointDef( void );\n\n/// A motor joint is used to control the relative velocity and or transform between two bodies.\n/// With a velocity of zero this acts like top-down friction.\n/// @ingroup motor_joint\ntypedef struct b2MotorJointDef\n{\n\t/// Base joint definition\n\tb2JointDef base;\n\n\t/// The desired linear velocity\n\tb2Vec2 linearVelocity;\n\n\t/// The maximum motor force in newtons\n\tfloat maxVelocityForce;\n\n\t/// The desired angular velocity\n\tfloat angularVelocity;\n\n\t/// The maximum motor torque in newton-meters\n\tfloat maxVelocityTorque;\n\n\t/// Linear spring hertz for position control\n\tfloat linearHertz;\n\n\t/// Linear spring damping ratio\n\tfloat linearDampingRatio;\n\n\t/// Maximum spring force in newtons\n\tfloat maxSpringForce;\n\n\t/// Angular spring hertz for position control\n\tfloat angularHertz;\n\n\t/// Angular spring damping ratio\n\tfloat angularDampingRatio;\n\n\t/// Maximum spring torque in newton-meters\n\tfloat maxSpringTorque;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2MotorJointDef;\n\n/// Use this to initialize your joint definition\n/// @ingroup motor_joint\nB2_API b2MotorJointDef b2DefaultMotorJointDef( void );\n\n/// A filter joint is used to disable collision between two specific bodies.\n///\n/// @ingroup filter_joint\ntypedef struct b2FilterJointDef\n{\n\t/// Base joint definition\n\tb2JointDef base;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2FilterJointDef;\n\n/// Use this to initialize your joint definition\n/// @ingroup filter_joint\nB2_API b2FilterJointDef b2DefaultFilterJointDef( void );\n\n/// Prismatic joint definition\n/// Body B may slide along the x-axis in local frame A. Body B cannot rotate relative to body A.\n/// The joint translation is zero when the local frame origins coincide in world space.\n/// @ingroup prismatic_joint\ntypedef struct b2PrismaticJointDef\n{\n\t/// Base joint definition\n\tb2JointDef base;\n\n\t/// Enable a linear spring along the prismatic joint axis\n\tbool enableSpring;\n\n\t/// The spring stiffness Hertz, cycles per second\n\tfloat hertz;\n\n\t/// The spring damping ratio, non-dimensional\n\tfloat dampingRatio;\n\n\t/// The target translation for the joint in meters. The spring-damper will drive\n\t/// to this translation.\n\tfloat targetTranslation;\n\n\t/// Enable/disable the joint limit\n\tbool enableLimit;\n\n\t/// The lower translation limit\n\tfloat lowerTranslation;\n\n\t/// The upper translation limit\n\tfloat upperTranslation;\n\n\t/// Enable/disable the joint motor\n\tbool enableMotor;\n\n\t/// The maximum motor force, typically in newtons\n\tfloat maxMotorForce;\n\n\t/// The desired motor speed, typically in meters per second\n\tfloat motorSpeed;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2PrismaticJointDef;\n\n/// Use this to initialize your joint definition\n/// @ingroup prismatic_joint\nB2_API b2PrismaticJointDef b2DefaultPrismaticJointDef( void );\n\n/// Revolute joint definition\n/// A point on body B is fixed to a point on body A. Allows relative rotation.\n/// @ingroup revolute_joint\ntypedef struct b2RevoluteJointDef\n{\n\t/// Base joint definition\n\tb2JointDef base;\n\n\t/// The target angle for the joint in radians. The spring-damper will drive\n\t/// to this angle.\n\tfloat targetAngle;\n\n\t/// Enable a rotational spring on the revolute hinge axis\n\tbool enableSpring;\n\n\t/// The spring stiffness Hertz, cycles per second\n\tfloat hertz;\n\n\t/// The spring damping ratio, non-dimensional\n\tfloat dampingRatio;\n\n\t/// A flag to enable joint limits\n\tbool enableLimit;\n\n\t/// The lower angle for the joint limit in radians. Minimum of -0.99*pi radians.\n\tfloat lowerAngle;\n\n\t/// The upper angle for the joint limit in radians. Maximum of 0.99*pi radians.\n\tfloat upperAngle;\n\n\t/// A flag to enable the joint motor\n\tbool enableMotor;\n\n\t/// The maximum motor torque, typically in newton-meters\n\tfloat maxMotorTorque;\n\n\t/// The desired motor speed in radians per second\n\tfloat motorSpeed;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2RevoluteJointDef;\n\n/// Use this to initialize your joint definition.\n/// @ingroup revolute_joint\nB2_API b2RevoluteJointDef b2DefaultRevoluteJointDef( void );\n\n/// Weld joint definition\n/// Connects two bodies together rigidly. This constraint provides springs to mimic\n/// soft-body simulation.\n/// @note The approximate solver in Box2D cannot hold many bodies together rigidly\n/// @ingroup weld_joint\ntypedef struct b2WeldJointDef\n{\n\t/// Base joint definition\n\tb2JointDef base;\n\n\t/// Linear stiffness expressed as Hertz (cycles per second). Use zero for maximum stiffness.\n\tfloat linearHertz;\n\n\t/// Angular stiffness as Hertz (cycles per second). Use zero for maximum stiffness.\n\tfloat angularHertz;\n\n\t/// Linear damping ratio, non-dimensional. Use 1 for critical damping.\n\tfloat linearDampingRatio;\n\n\t/// Linear damping ratio, non-dimensional. Use 1 for critical damping.\n\tfloat angularDampingRatio;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2WeldJointDef;\n\n/// Use this to initialize your joint definition\n/// @ingroup weld_joint\nB2_API b2WeldJointDef b2DefaultWeldJointDef( void );\n\n/// Wheel joint definition\n/// Body B is a wheel that may rotate freely and slide along the local x-axis in frame A.\n/// The joint translation is zero when the local frame origins coincide in world space.\n/// @ingroup wheel_joint\ntypedef struct b2WheelJointDef\n{\n\t/// Base joint definition\n\tb2JointDef base;\n\n\t/// Enable a linear spring along the local axis\n\tbool enableSpring;\n\n\t/// Spring stiffness in Hertz\n\tfloat hertz;\n\n\t/// Spring damping ratio, non-dimensional\n\tfloat dampingRatio;\n\n\t/// Enable/disable the joint linear limit\n\tbool enableLimit;\n\n\t/// The lower translation limit\n\tfloat lowerTranslation;\n\n\t/// The upper translation limit\n\tfloat upperTranslation;\n\n\t/// Enable/disable the joint rotational motor\n\tbool enableMotor;\n\n\t/// The maximum motor torque, typically in newton-meters\n\tfloat maxMotorTorque;\n\n\t/// The desired motor speed in radians per second\n\tfloat motorSpeed;\n\n\t/// Used internally to detect a valid definition. DO NOT SET.\n\tint internalValue;\n} b2WheelJointDef;\n\n/// Use this to initialize your joint definition\n/// @ingroup wheel_joint\nB2_API b2WheelJointDef b2DefaultWheelJointDef( void );\n\n/// The explosion definition is used to configure options for explosions. Explosions\n/// consider shape geometry when computing the impulse.\n/// @ingroup world\ntypedef struct b2ExplosionDef\n{\n\t/// Mask bits to filter shapes\n\tuint64_t maskBits;\n\n\t/// The center of the explosion in world space\n\tb2Vec2 position;\n\n\t/// The radius of the explosion\n\tfloat radius;\n\n\t/// The falloff distance beyond the radius. Impulse is reduced to zero at this distance.\n\tfloat falloff;\n\n\t/// Impulse per unit length. This applies an impulse according to the shape perimeter that\n\t/// is facing the explosion. Explosions only apply to circles, capsules, and polygons. This\n\t/// may be negative for implosions.\n\tfloat impulsePerLength;\n} b2ExplosionDef;\n\n/// Use this to initialize your explosion definition\n/// @ingroup world\nB2_API b2ExplosionDef b2DefaultExplosionDef( void );\n\n/**\n * @defgroup events Events\n * World event types.\n *\n * Events are used to collect events that occur during the world time step. These events\n * are then available to query after the time step is complete. This is preferable to callbacks\n * because Box2D uses multithreaded simulation.\n *\n * Also when events occur in the simulation step it may be problematic to modify the world, which is\n * often what applications want to do when events occur.\n *\n * With event arrays, you can scan the events in a loop and modify the world. However, you need to be careful\n * that some event data may become invalid. There are several samples that show how to do this safely.\n *\n * @{\n */\n\n/// A begin touch event is generated when a shape starts to overlap a sensor shape.\ntypedef struct b2SensorBeginTouchEvent\n{\n\t/// The id of the sensor shape\n\tb2ShapeId sensorShapeId;\n\n\t/// The id of the shape that began touching the sensor shape\n\tb2ShapeId visitorShapeId;\n} b2SensorBeginTouchEvent;\n\n/// An end touch event is generated when a shape stops overlapping a sensor shape.\n///\tThese include things like setting the transform, destroying a body or shape, or changing\n///\ta filter. You will also get an end event if the sensor or visitor are destroyed.\n///\tTherefore you should always confirm the shape id is valid using b2Shape_IsValid.\ntypedef struct b2SensorEndTouchEvent\n{\n\t/// The id of the sensor shape\n\t///\t@warning this shape may have been destroyed\n\t///\t@see b2Shape_IsValid\n\tb2ShapeId sensorShapeId;\n\n\t/// The id of the shape that stopped touching the sensor shape\n\t///\t@warning this shape may have been destroyed\n\t///\t@see b2Shape_IsValid\n\tb2ShapeId visitorShapeId;\n\n} b2SensorEndTouchEvent;\n\n/// Sensor events are buffered in the world and are available\n/// as begin/end overlap event arrays after the time step is complete.\n/// Note: these may become invalid if bodies and/or shapes are destroyed\ntypedef struct b2SensorEvents\n{\n\t/// Array of sensor begin touch events\n\tb2SensorBeginTouchEvent* beginEvents;\n\n\t/// Array of sensor end touch events\n\tb2SensorEndTouchEvent* endEvents;\n\n\t/// The number of begin touch events\n\tint beginCount;\n\n\t/// The number of end touch events\n\tint endCount;\n} b2SensorEvents;\n\n/// A begin touch event is generated when two shapes begin touching.\ntypedef struct b2ContactBeginTouchEvent\n{\n\t/// Id of the first shape\n\tb2ShapeId shapeIdA;\n\n\t/// Id of the second shape\n\tb2ShapeId shapeIdB;\n\n\t/// The transient contact id. This contact maybe destroyed automatically when the world is modified or simulated.\n\t/// Used b2Contact_IsValid before using this id.\n\tb2ContactId contactId;\n} b2ContactBeginTouchEvent;\n\n/// An end touch event is generated when two shapes stop touching.\n///\tYou will get an end event if you do anything that destroys contacts previous to the last\n///\tworld step. These include things like setting the transform, destroying a body\n///\tor shape, or changing a filter or body type.\ntypedef struct b2ContactEndTouchEvent\n{\n\t/// Id of the first shape\n\t///\t@warning this shape may have been destroyed\n\t///\t@see b2Shape_IsValid\n\tb2ShapeId shapeIdA;\n\n\t/// Id of the second shape\n\t///\t@warning this shape may have been destroyed\n\t///\t@see b2Shape_IsValid\n\tb2ShapeId shapeIdB;\n\n\t/// Id of the contact.\n\t///\t@warning this contact may have been destroyed\n\t///\t@see b2Contact_IsValid\n\tb2ContactId contactId;\n} b2ContactEndTouchEvent;\n\n/// A hit touch event is generated when two shapes collide with a speed faster than the hit speed threshold.\n/// This may be reported for speculative contacts that have a confirmed impulse.\ntypedef struct b2ContactHitEvent\n{\n\t/// Id of the first shape\n\tb2ShapeId shapeIdA;\n\n\t/// Id of the second shape\n\tb2ShapeId shapeIdB;\n\n\t/// Id of the contact.\n\t///\t@warning this contact may have been destroyed\n\t///\t@see b2Contact_IsValid\n\tb2ContactId contactId;\n\n\t/// Point where the shapes hit at the beginning of the time step.\n\t/// This is a mid-point between the two surfaces. It could be at speculative\n\t/// point where the two shapes were not touching at the beginning of the time step.\n\tb2Vec2 point;\n\n\t/// Normal vector pointing from shape A to shape B\n\tb2Vec2 normal;\n\n\t/// The speed the shapes are approaching. Always positive. Typically in meters per second.\n\tfloat approachSpeed;\n} b2ContactHitEvent;\n\n/// Contact events are buffered in the Box2D world and are available\n/// as event arrays after the time step is complete.\n/// Note: these may become invalid if bodies and/or shapes are destroyed\ntypedef struct b2ContactEvents\n{\n\t/// Array of begin touch events\n\tb2ContactBeginTouchEvent* beginEvents;\n\n\t/// Array of end touch events\n\tb2ContactEndTouchEvent* endEvents;\n\n\t/// Array of hit events\n\tb2ContactHitEvent* hitEvents;\n\n\t/// Number of begin touch events\n\tint beginCount;\n\n\t/// Number of end touch events\n\tint endCount;\n\n\t/// Number of hit events\n\tint hitCount;\n} b2ContactEvents;\n\n/// Body move events triggered when a body moves.\n/// Triggered when a body moves due to simulation. Not reported for bodies moved by the user.\n/// This also has a flag to indicate that the body went to sleep so the application can also\n/// sleep that actor/entity/object associated with the body.\n/// On the other hand if the flag does not indicate the body went to sleep then the application\n/// can treat the actor/entity/object associated with the body as awake.\n/// This is an efficient way for an application to update game object transforms rather than\n/// calling functions such as b2Body_GetTransform() because this data is delivered as a contiguous array\n/// and it is only populated with bodies that have moved.\n/// @note If sleeping is disabled all dynamic and kinematic bodies will trigger move events.\ntypedef struct b2BodyMoveEvent\n{\n\tvoid* userData;\n\tb2Transform transform;\n\tb2BodyId bodyId;\n\tbool fellAsleep;\n} b2BodyMoveEvent;\n\n/// Body events are buffered in the Box2D world and are available\n/// as event arrays after the time step is complete.\n/// Note: this data becomes invalid if bodies are destroyed\ntypedef struct b2BodyEvents\n{\n\t/// Array of move events\n\tb2BodyMoveEvent* moveEvents;\n\n\t/// Number of move events\n\tint moveCount;\n} b2BodyEvents;\n\n/// Joint events report joints that are awake and have a force and/or torque exceeding the threshold\n/// The observed forces and torques are not returned for efficiency reasons.\ntypedef struct b2JointEvent\n{\n\t/// The joint id\n\tb2JointId jointId;\n\n\t/// The user data from the joint for convenience\n\tvoid* userData;\n} b2JointEvent;\n\n/// Joint events are buffered in the world and are available\n/// as event arrays after the time step is complete.\n/// Note: this data becomes invalid if joints are destroyed\ntypedef struct b2JointEvents\n{\n\t/// Array of events\n\tb2JointEvent* jointEvents;\n\n\t/// Number of events\n\tint count;\n} b2JointEvents;\n\n/// The contact data for two shapes. By convention the manifold normal points\n/// from shape A to shape B.\n/// @see b2Shape_GetContactData() and b2Body_GetContactData()\ntypedef struct b2ContactData\n{\n\tb2ContactId contactId;\n\tb2ShapeId shapeIdA;\n\tb2ShapeId shapeIdB;\n\tb2Manifold manifold;\n} b2ContactData;\n\n/**@}*/\n\n/// Prototype for a contact filter callback.\n/// This is called when a contact pair is considered for collision. This allows you to\n/// perform custom logic to prevent collision between shapes. This is only called if\n/// one of the two shapes has custom filtering enabled.\n/// Notes:\n/// - this function must be thread-safe\n/// - this is only called if one of the two shapes has enabled custom filtering\n/// - this may be called for awake dynamic bodies and sensors\n/// Return false if you want to disable the collision\n/// @see b2ShapeDef\n/// @warning Do not attempt to modify the world inside this callback\n/// @ingroup world\ntypedef bool b2CustomFilterFcn( b2ShapeId shapeIdA, b2ShapeId shapeIdB, void* context );\n\n/// Prototype for a pre-solve callback.\n/// This is called after a contact is updated. This allows you to inspect a\n/// contact before it goes to the solver. If you are careful, you can modify the\n/// contact manifold (e.g. modify the normal).\n/// Notes:\n/// - this function must be thread-safe\n/// - this is only called if the shape has enabled pre-solve events\n/// - this is called only for awake dynamic bodies\n/// - this is not called for sensors\n/// - the supplied manifold has impulse values from the previous step\n/// Return false if you want to disable the contact this step\n/// @warning Do not attempt to modify the world inside this callback\n/// @ingroup world\ntypedef bool b2PreSolveFcn( b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Vec2 point, b2Vec2 normal, void* context );\n\n/// Prototype callback for overlap queries.\n/// Called for each shape found in the query.\n/// @see b2World_OverlapABB\n/// @return false to terminate the query.\n/// @ingroup world\ntypedef bool b2OverlapResultFcn( b2ShapeId shapeId, void* context );\n\n/// Prototype callback for ray and shape casts.\n/// Called for each shape found in the query. You control how the ray cast\n/// proceeds by returning a float:\n/// return -1: ignore this shape and continue\n/// return 0: terminate the ray cast\n/// return fraction: clip the ray to this point\n/// return 1: don't clip the ray and continue\n/// A cast with initial overlap will return a zero fraction and a zero normal.\n/// @param shapeId the shape hit by the ray\n/// @param point the point of initial intersection\n/// @param normal the normal vector at the point of intersection, zero for a shape cast with initial overlap\n/// @param fraction the fraction along the ray at the point of intersection, zero for a shape cast with initial overlap\n/// @param context the user context\n/// @return -1 to filter, 0 to terminate, fraction to clip the ray for closest hit, 1 to continue\n/// @see b2World_CastRay\n/// @ingroup world\ntypedef float b2CastResultFcn( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context );\n\n// Used to collect collision planes for character movers.\n// Return true to continue gathering planes.\ntypedef bool b2PlaneResultFcn( b2ShapeId shapeId, const b2PlaneResult* plane, void* context );\n\n/// These colors are used for debug draw and mostly match the named SVG colors.\n/// See https://www.rapidtables.com/web/color/index.html\n/// https://johndecember.com/html/spec/colorsvg.html\n/// https://upload.wikimedia.org/wikipedia/commons/2/2b/SVG_Recognized_color_keyword_names.svg\ntypedef enum b2HexColor\n{\n\tb2_colorAliceBlue = 0xF0F8FF,\n\tb2_colorAntiqueWhite = 0xFAEBD7,\n\tb2_colorAqua = 0x00FFFF,\n\tb2_colorAquamarine = 0x7FFFD4,\n\tb2_colorAzure = 0xF0FFFF,\n\tb2_colorBeige = 0xF5F5DC,\n\tb2_colorBisque = 0xFFE4C4,\n\tb2_colorBlack = 0x000000,\n\tb2_colorBlanchedAlmond = 0xFFEBCD,\n\tb2_colorBlue = 0x0000FF,\n\tb2_colorBlueViolet = 0x8A2BE2,\n\tb2_colorBrown = 0xA52A2A,\n\tb2_colorBurlywood = 0xDEB887,\n\tb2_colorCadetBlue = 0x5F9EA0,\n\tb2_colorChartreuse = 0x7FFF00,\n\tb2_colorChocolate = 0xD2691E,\n\tb2_colorCoral = 0xFF7F50,\n\tb2_colorCornflowerBlue = 0x6495ED,\n\tb2_colorCornsilk = 0xFFF8DC,\n\tb2_colorCrimson = 0xDC143C,\n\tb2_colorCyan = 0x00FFFF,\n\tb2_colorDarkBlue = 0x00008B,\n\tb2_colorDarkCyan = 0x008B8B,\n\tb2_colorDarkGoldenRod = 0xB8860B,\n\tb2_colorDarkGray = 0xA9A9A9,\n\tb2_colorDarkGreen = 0x006400,\n\tb2_colorDarkKhaki = 0xBDB76B,\n\tb2_colorDarkMagenta = 0x8B008B,\n\tb2_colorDarkOliveGreen = 0x556B2F,\n\tb2_colorDarkOrange = 0xFF8C00,\n\tb2_colorDarkOrchid = 0x9932CC,\n\tb2_colorDarkRed = 0x8B0000,\n\tb2_colorDarkSalmon = 0xE9967A,\n\tb2_colorDarkSeaGreen = 0x8FBC8F,\n\tb2_colorDarkSlateBlue = 0x483D8B,\n\tb2_colorDarkSlateGray = 0x2F4F4F,\n\tb2_colorDarkTurquoise = 0x00CED1,\n\tb2_colorDarkViolet = 0x9400D3,\n\tb2_colorDeepPink = 0xFF1493,\n\tb2_colorDeepSkyBlue = 0x00BFFF,\n\tb2_colorDimGray = 0x696969,\n\tb2_colorDodgerBlue = 0x1E90FF,\n\tb2_colorFireBrick = 0xB22222,\n\tb2_colorFloralWhite = 0xFFFAF0,\n\tb2_colorForestGreen = 0x228B22,\n\tb2_colorFuchsia = 0xFF00FF,\n\tb2_colorGainsboro = 0xDCDCDC,\n\tb2_colorGhostWhite = 0xF8F8FF,\n\tb2_colorGold = 0xFFD700,\n\tb2_colorGoldenRod = 0xDAA520,\n\tb2_colorGray = 0x808080,\n\tb2_colorGreen = 0x008000,\n\tb2_colorGreenYellow = 0xADFF2F,\n\tb2_colorHoneyDew = 0xF0FFF0,\n\tb2_colorHotPink = 0xFF69B4,\n\tb2_colorIndianRed = 0xCD5C5C,\n\tb2_colorIndigo = 0x4B0082,\n\tb2_colorIvory = 0xFFFFF0,\n\tb2_colorKhaki = 0xF0E68C,\n\tb2_colorLavender = 0xE6E6FA,\n\tb2_colorLavenderBlush = 0xFFF0F5,\n\tb2_colorLawnGreen = 0x7CFC00,\n\tb2_colorLemonChiffon = 0xFFFACD,\n\tb2_colorLightBlue = 0xADD8E6,\n\tb2_colorLightCoral = 0xF08080,\n\tb2_colorLightCyan = 0xE0FFFF,\n\tb2_colorLightGoldenRodYellow = 0xFAFAD2,\n\tb2_colorLightGray = 0xD3D3D3,\n\tb2_colorLightGreen = 0x90EE90,\n\tb2_colorLightPink = 0xFFB6C1,\n\tb2_colorLightSalmon = 0xFFA07A,\n\tb2_colorLightSeaGreen = 0x20B2AA,\n\tb2_colorLightSkyBlue = 0x87CEFA,\n\tb2_colorLightSlateGray = 0x778899,\n\tb2_colorLightSteelBlue = 0xB0C4DE,\n\tb2_colorLightYellow = 0xFFFFE0,\n\tb2_colorLime = 0x00FF00,\n\tb2_colorLimeGreen = 0x32CD32,\n\tb2_colorLinen = 0xFAF0E6,\n\tb2_colorMagenta = 0xFF00FF,\n\tb2_colorMaroon = 0x800000,\n\tb2_colorMediumAquaMarine = 0x66CDAA,\n\tb2_colorMediumBlue = 0x0000CD,\n\tb2_colorMediumOrchid = 0xBA55D3,\n\tb2_colorMediumPurple = 0x9370DB,\n\tb2_colorMediumSeaGreen = 0x3CB371,\n\tb2_colorMediumSlateBlue = 0x7B68EE,\n\tb2_colorMediumSpringGreen = 0x00FA9A,\n\tb2_colorMediumTurquoise = 0x48D1CC,\n\tb2_colorMediumVioletRed = 0xC71585,\n\tb2_colorMidnightBlue = 0x191970,\n\tb2_colorMintCream = 0xF5FFFA,\n\tb2_colorMistyRose = 0xFFE4E1,\n\tb2_colorMoccasin = 0xFFE4B5,\n\tb2_colorNavajoWhite = 0xFFDEAD,\n\tb2_colorNavy = 0x000080,\n\tb2_colorOldLace = 0xFDF5E6,\n\tb2_colorOlive = 0x808000,\n\tb2_colorOliveDrab = 0x6B8E23,\n\tb2_colorOrange = 0xFFA500,\n\tb2_colorOrangeRed = 0xFF4500,\n\tb2_colorOrchid = 0xDA70D6,\n\tb2_colorPaleGoldenRod = 0xEEE8AA,\n\tb2_colorPaleGreen = 0x98FB98,\n\tb2_colorPaleTurquoise = 0xAFEEEE,\n\tb2_colorPaleVioletRed = 0xDB7093,\n\tb2_colorPapayaWhip = 0xFFEFD5,\n\tb2_colorPeachPuff = 0xFFDAB9,\n\tb2_colorPeru = 0xCD853F,\n\tb2_colorPink = 0xFFC0CB,\n\tb2_colorPlum = 0xDDA0DD,\n\tb2_colorPowderBlue = 0xB0E0E6,\n\tb2_colorPurple = 0x800080,\n\tb2_colorRebeccaPurple = 0x663399,\n\tb2_colorRed = 0xFF0000,\n\tb2_colorRosyBrown = 0xBC8F8F,\n\tb2_colorRoyalBlue = 0x4169E1,\n\tb2_colorSaddleBrown = 0x8B4513,\n\tb2_colorSalmon = 0xFA8072,\n\tb2_colorSandyBrown = 0xF4A460,\n\tb2_colorSeaGreen = 0x2E8B57,\n\tb2_colorSeaShell = 0xFFF5EE,\n\tb2_colorSienna = 0xA0522D,\n\tb2_colorSilver = 0xC0C0C0,\n\tb2_colorSkyBlue = 0x87CEEB,\n\tb2_colorSlateBlue = 0x6A5ACD,\n\tb2_colorSlateGray = 0x708090,\n\tb2_colorSnow = 0xFFFAFA,\n\tb2_colorSpringGreen = 0x00FF7F,\n\tb2_colorSteelBlue = 0x4682B4,\n\tb2_colorTan = 0xD2B48C,\n\tb2_colorTeal = 0x008080,\n\tb2_colorThistle = 0xD8BFD8,\n\tb2_colorTomato = 0xFF6347,\n\tb2_colorTurquoise = 0x40E0D0,\n\tb2_colorViolet = 0xEE82EE,\n\tb2_colorWheat = 0xF5DEB3,\n\tb2_colorWhite = 0xFFFFFF,\n\tb2_colorWhiteSmoke = 0xF5F5F5,\n\tb2_colorYellow = 0xFFFF00,\n\tb2_colorYellowGreen = 0x9ACD32,\n\n\tb2_colorBox2DRed = 0xDC3132,\n\tb2_colorBox2DBlue = 0x30AEBF,\n\tb2_colorBox2DGreen = 0x8CC924,\n\tb2_colorBox2DYellow = 0xFFEE8C\n} b2HexColor;\n\n/// This struct holds callbacks you can implement to draw a Box2D world.\n/// This structure should be zero initialized.\n/// @ingroup world\ntypedef struct b2DebugDraw\n{\n\t/// Draw a closed polygon provided in CCW order.\n\tvoid ( *DrawPolygonFcn )( const b2Vec2* vertices, int vertexCount, b2HexColor color, void* context );\n\n\t/// Draw a solid closed polygon provided in CCW order.\n\tvoid ( *DrawSolidPolygonFcn )( b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius, b2HexColor color,\n\t\t\t\t\t\t\t\tvoid* context );\n\n\t/// Draw a circle.\n\tvoid ( *DrawCircleFcn )( b2Vec2 center, float radius, b2HexColor color, void* context );\n\n\t/// Draw a solid circle.\n\tvoid ( *DrawSolidCircleFcn )( b2Transform transform, float radius, b2HexColor color, void* context );\n\n\t/// Draw a solid capsule.\n\tvoid ( *DrawSolidCapsuleFcn )( b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color, void* context );\n\n\t/// Draw a line segment.\n\tvoid ( *DrawLineFcn )( b2Vec2 p1, b2Vec2 p2, b2HexColor color, void* context );\n\n\t/// Draw a transform. Choose your own length scale.\n\tvoid ( *DrawTransformFcn )( b2Transform transform, void* context );\n\n\t/// Draw a point.\n\tvoid ( *DrawPointFcn )( b2Vec2 p, float size, b2HexColor color, void* context );\n\n\t/// Draw a string in world space\n\tvoid ( *DrawStringFcn )( b2Vec2 p, const char* s, b2HexColor color, void* context );\n\n\t/// World bounds to use for debug draw\n\tb2AABB drawingBounds;\n\n\t/// Scale to use when drawing forces\n\tfloat forceScale;\n\n\t/// Global scaling for joint drawing\n\tfloat jointScale;\n\n\t/// Option to draw shapes\n\tbool drawShapes;\n\n\t/// Option to draw joints\n\tbool drawJoints;\n\n\t/// Option to draw additional information for joints\n\tbool drawJointExtras;\n\n\t/// Option to draw the bounding boxes for shapes\n\tbool drawBounds;\n\n\t/// Option to draw the mass and center of mass of dynamic bodies\n\tbool drawMass;\n\n\t/// Option to draw body names\n\tbool drawBodyNames;\n\n\t/// Option to draw contact points\n\tbool drawContactPoints;\n\n\t/// Option to visualize the graph coloring used for contacts and joints\n\tbool drawGraphColors;\n\n\t/// Option to draw contact feature ids\n\tbool drawContactFeatures;\n\n\t/// Option to draw contact normals\n\tbool drawContactNormals;\n\n\t/// Option to draw contact normal forces\n\tbool drawContactForces;\n\n\t/// Option to draw contact friction forces\n\tbool drawFrictionForces;\n\n\t/// Option to draw islands as bounding boxes\n\tbool drawIslands;\n\n\t/// User context that is passed as an argument to drawing callback functions\n\tvoid* context;\n} b2DebugDraw;\n\n/// Use this to initialize your drawing interface. This allows you to implement a sub-set\n/// of the drawing functions.\nB2_API b2DebugDraw b2DefaultDebugDraw( void );\n"
  },
  {
    "path": "samples/CMakeLists.txt",
    "content": "# Box2D samples app\n\n# glad for OpenGL API\nset(GLAD_DIR ${CMAKE_SOURCE_DIR}/extern/glad)\n\nadd_library(\n\tglad STATIC\n\t${GLAD_DIR}/src/glad.c\n\t${GLAD_DIR}/include/glad/glad.h\n\t${GLAD_DIR}/include/KHR/khrplatform.h\n)\ntarget_include_directories(glad PUBLIC ${GLAD_DIR}/include)\n\n# glfw for windowing and input\nset(GLFW_BUILD_DOCS OFF CACHE BOOL \"GLFW Docs\")\nset(GLFW_BUILD_EXAMPLES OFF CACHE BOOL \"GLFW Examples\")\nset(GLFW_BUILD_TESTS OFF CACHE BOOL \"GLFW Tests\")\nset(GLFW_INSTALL OFF CACHE BOOL \"GLFW Install\")\n\nFetchContent_Declare(\n\tglfw\n\tGIT_REPOSITORY https://github.com/glfw/glfw.git\n\tGIT_TAG 3.4\n\tGIT_SHALLOW TRUE\n\tGIT_PROGRESS TRUE\n)\nFetchContent_MakeAvailable(glfw)\n\n# imgui and glfw backend for GUI\n# https://gist.github.com/jeffamstutz/992723dfabac4e3ffff265eb71a24cd9\n# Modified to pin to a specific imgui release\nFetchContent_Populate(imgui\n\tURL https://github.com/ocornut/imgui/archive/refs/tags/v1.91.3.zip\n\tSOURCE_DIR ${CMAKE_SOURCE_DIR}/build/imgui\n)\n\nset(IMGUI_DIR ${CMAKE_SOURCE_DIR}/build/imgui)\n\nadd_library(imgui STATIC\n\t${IMGUI_DIR}/imconfig.h\n\t${IMGUI_DIR}/imgui.h\n\n\t${IMGUI_DIR}/imgui.cpp\n\t${IMGUI_DIR}/imgui_draw.cpp\n\t${IMGUI_DIR}/imgui_demo.cpp\n\t${IMGUI_DIR}/imgui_tables.cpp\n\t${IMGUI_DIR}/imgui_widgets.cpp\n\n\t${IMGUI_DIR}/backends/imgui_impl_glfw.cpp\n\t${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp\n)\n\ntarget_link_libraries(imgui PUBLIC glfw glad)\ntarget_include_directories(imgui PUBLIC ${IMGUI_DIR} ${IMGUI_DIR}/backends)\ntarget_compile_definitions(imgui PUBLIC IMGUI_DISABLE_OBSOLETE_FUNCTIONS)\n# The sample app also uses stb_truetype and this keeps the symbols separate\ntarget_compile_definitions(imgui PRIVATE IMGUI_STB_NAMESPACE=imgui_stb)\nset_target_properties(imgui PROPERTIES\n\tCXX_STANDARD 20\n    CXX_STANDARD_REQUIRED YES\n    CXX_EXTENSIONS NO\n)\n\n# jsmn for json\nset(JSMN_DIR ${CMAKE_SOURCE_DIR}/extern/jsmn)\n\nadd_library(jsmn INTERFACE ${JSMN_DIR}/jsmn.h)\ntarget_include_directories(jsmn INTERFACE ${JSMN_DIR})\n\nset(BOX2D_SAMPLE_FILES\n\tcar.cpp\n\tcar.h\n\tcontainer.c\n\tcontainer.h\n\tdonut.cpp\n\tdonut.h\n\tdoohickey.cpp\n\tdoohickey.h\n\tdraw.c\n\tdraw.h\n\tmain.cpp\n\tsample.cpp\n\tsample.h\n\tsample_benchmark.cpp\n\tsample_bodies.cpp\n\tsample_character.cpp\n\tsample_collision.cpp\n\tsample_continuous.cpp\n\tsample_determinism.cpp\n\tsample_events.cpp\n\tsample_geometry.cpp\n\tsample_issues.cpp\n\tsample_joints.cpp\n\tsample_robustness.cpp\n\tsample_shapes.cpp\n\tsample_stacking.cpp\n\tsample_world.cpp\n\tshader.c\n\tshader.h\n\tstb_image_write.h\n\tstb_truetype.h\n)\n\nadd_executable(samples ${BOX2D_SAMPLE_FILES})\n\nset_target_properties(samples PROPERTIES\n\tCXX_STANDARD 20\n    CXX_STANDARD_REQUIRED YES\n    CXX_EXTENSIONS NO\n)\n\nif (BOX2D_COMPILE_WARNING_AS_ERROR)\n\tset_target_properties(samples PROPERTIES COMPILE_WARNING_AS_ERROR ON)\nendif()\n\ntarget_include_directories(samples PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${JSMN_DIR})\ntarget_link_libraries(samples PUBLIC box2d shared imgui glfw glad enkiTS)\n\n# target_compile_definitions(samples PRIVATE \"$<$<CONFIG:DEBUG>:SAMPLES_DEBUG>\")\n# message(STATUS \"runtime = ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}\")\n# message(STATUS \"binary = ${CMAKE_CURRENT_BINARY_DIR}\")\n\n# Copy font files, etc\nadd_custom_command(\n\tTARGET samples POST_BUILD\n\tCOMMAND ${CMAKE_COMMAND} -E copy_directory\n\t\t\t${CMAKE_CURRENT_SOURCE_DIR}/data/\n\t\t\t${CMAKE_CURRENT_BINARY_DIR}/data/)\n\nsource_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${BOX2D_SAMPLE_FILES})\n"
  },
  {
    "path": "samples/car.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"car.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <assert.h>\n\nCar::Car()\n{\n\tm_chassisId = {};\n\tm_rearWheelId = {};\n\tm_frontWheelId = {};\n\tm_rearAxleId = {};\n\tm_frontAxleId = {};\n\tm_isSpawned = false;\n}\n\nvoid Car::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float hertz, float dampingRatio, float torque, void* userData )\n{\n\tassert( m_isSpawned == false );\n\n\tassert( B2_IS_NULL( m_chassisId ) );\n\tassert( B2_IS_NULL( m_frontWheelId ) );\n\tassert( B2_IS_NULL( m_rearWheelId ) );\n\n\tb2Vec2 vertices[6] = {\n\t\t{ -1.5f, -0.5f }, { 1.5f, -0.5f }, { 1.5f, 0.0f }, { 0.0f, 0.9f }, { -1.15f, 0.9f }, { -1.5f, 0.2f },\n\t};\n\n\tfor ( int i = 0; i < 6; ++i )\n\t{\n\t\tvertices[i].x *= 0.85f * scale;\n\t\tvertices[i].y *= 0.85f * scale;\n\t}\n\n\tb2Hull hull = b2ComputeHull( vertices, 6 );\n\tb2Polygon chassis = b2MakePolygon( &hull, 0.15f * scale );\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.density = 1.0f / scale;\n\tshapeDef.material.friction = 0.2f;\n\n\tb2Circle circle = { { 0.0f, 0.0f }, 0.4f * scale };\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tbodyDef.position = b2Add( { 0.0f, 1.0f * scale }, position );\n\tm_chassisId = b2CreateBody( worldId, &bodyDef );\n\tb2CreatePolygonShape( m_chassisId, &shapeDef, &chassis );\n\n\tshapeDef.density = 2.0f / scale;\n\tshapeDef.material.friction = 1.5f;\n\tshapeDef.material.rollingResistance = 0.1f;\n\n\tbodyDef.position = b2Add( { -1.0f * scale, 0.35f * scale }, position );\n\tbodyDef.allowFastRotation = true;\n\tm_rearWheelId = b2CreateBody( worldId, &bodyDef );\n\tb2CreateCircleShape( m_rearWheelId, &shapeDef, &circle );\n\n\tbodyDef.position = b2Add( { 1.0f * scale, 0.4f * scale }, position );\n\tbodyDef.allowFastRotation = true;\n\tm_frontWheelId = b2CreateBody( worldId, &bodyDef );\n\tb2CreateCircleShape( m_frontWheelId, &shapeDef, &circle );\n\n\tb2Vec2 axis = { 0.0f, 1.0f };\n\tb2Vec2 pivot = b2Body_GetPosition( m_rearWheelId );\n\n\t// float throttle = 0.0f;\n\t// float speed = 35.0f;\n\t// float torque = 2.5f * scale;\n\t// float hertz = 5.0f;\n\t// float dampingRatio = 0.7f;\n\n\tb2WheelJointDef jointDef = b2DefaultWheelJointDef();\n\n\tjointDef.base.bodyIdA = m_chassisId;\n\tjointDef.base.bodyIdB = m_rearWheelId;\n\tjointDef.base.localFrameA.q = b2MakeRot( 0.5f * B2_PI );\n\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\tjointDef.motorSpeed = 0.0f;\n\tjointDef.maxMotorTorque = torque;\n\tjointDef.enableMotor = true;\n\tjointDef.hertz = hertz;\n\tjointDef.dampingRatio = dampingRatio;\n\tjointDef.lowerTranslation = -0.25f * scale;\n\tjointDef.upperTranslation = 0.25f * scale;\n\tjointDef.enableLimit = true;\n\tm_rearAxleId = b2CreateWheelJoint( worldId, &jointDef );\n\n\tpivot = b2Body_GetPosition( m_frontWheelId );\n\tjointDef.base.bodyIdA = m_chassisId;\n\tjointDef.base.bodyIdB = m_frontWheelId;\n\tjointDef.base.localFrameA.q = b2MakeRot( 0.5f * B2_PI );\n\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\tjointDef.motorSpeed = 0.0f;\n\tjointDef.maxMotorTorque = torque;\n\tjointDef.enableMotor = true;\n\tjointDef.hertz = hertz;\n\tjointDef.dampingRatio = dampingRatio;\n\tjointDef.lowerTranslation = -0.25f * scale;\n\tjointDef.upperTranslation = 0.25f * scale;\n\tjointDef.enableLimit = true;\n\tm_frontAxleId = b2CreateWheelJoint( worldId, &jointDef );\n}\n\nvoid Car::Despawn()\n{\n\tassert( m_isSpawned == true );\n\n\tb2DestroyJoint( m_rearAxleId, false );\n\tb2DestroyJoint( m_frontAxleId, false );\n\tb2DestroyBody( m_rearWheelId );\n\tb2DestroyBody( m_frontWheelId );\n\tb2DestroyBody( m_chassisId );\n\n\tm_isSpawned = false;\n}\n\nvoid Car::SetSpeed( float speed )\n{\n\tb2WheelJoint_SetMotorSpeed( m_rearAxleId, speed );\n\tb2WheelJoint_SetMotorSpeed( m_frontAxleId, speed );\n\tb2Joint_WakeBodies( m_rearAxleId );\n}\n\nvoid Car::SetTorque( float torque )\n{\n\tb2WheelJoint_SetMaxMotorTorque( m_rearAxleId, torque );\n\tb2WheelJoint_SetMaxMotorTorque( m_frontAxleId, torque );\n}\n\nvoid Car::SetHertz( float hertz )\n{\n\tb2WheelJoint_SetSpringHertz( m_rearAxleId, hertz );\n\tb2WheelJoint_SetSpringHertz( m_frontAxleId, hertz );\n}\n\nvoid Car::SetDampingRadio( float dampingRatio )\n{\n\tb2WheelJoint_SetSpringDampingRatio( m_rearAxleId, dampingRatio );\n\tb2WheelJoint_SetSpringDampingRatio( m_frontAxleId, dampingRatio );\n}\n\nTruck::Truck()\n{\n\tm_chassisId = {};\n\tm_rearWheelId = {};\n\tm_frontWheelId = {};\n\tm_rearAxleId = {};\n\tm_frontAxleId = {};\n\tm_isSpawned = false;\n}\n\nvoid Truck::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float hertz, float dampingRatio, float torque, float density,\n\t\t\t\t   void* userData )\n{\n\tassert( m_isSpawned == false );\n\n\tassert( B2_IS_NULL( m_chassisId ) );\n\tassert( B2_IS_NULL( m_frontWheelId ) );\n\tassert( B2_IS_NULL( m_rearWheelId ) );\n\n\t// b2Vec2 vertices[6] = {\n\t//\t{ -1.5f, -0.5f }, { 1.5f, -0.5f }, { 1.5f, 0.0f }, { 0.0f, 0.9f }, { -1.15f, 0.9f }, { -1.5f, 0.2f },\n\t// };\n\n\tb2Vec2 vertices[5] = {\n\t\t{ -0.65f, -0.4f }, { 1.5f, -0.4f }, { 1.5f, 0.0f }, { 0.0f, 0.9f }, { -0.65f, 0.9f },\n\t};\n\n\tfor ( int i = 0; i < 5; ++i )\n\t{\n\t\tvertices[i].x *= 0.85f * scale;\n\t\tvertices[i].y *= 0.85f * scale;\n\t}\n\n\tb2Hull hull = b2ComputeHull( vertices, 5 );\n\tb2Polygon chassis = b2MakePolygon( &hull, 0.15f * scale );\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.density = density;\n\tshapeDef.material.friction = 0.2f;\n\tshapeDef.material.customColor = b2_colorHotPink;\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tbodyDef.position = b2Add( { 0.0f, 1.0f * scale }, position );\n\tm_chassisId = b2CreateBody( worldId, &bodyDef );\n\tb2CreatePolygonShape( m_chassisId, &shapeDef, &chassis );\n\n\tb2Polygon box = b2MakeOffsetBox( 1.25f * scale, 0.1f * scale,  { -2.05f * scale, -0.275f * scale }, b2Rot_identity );\n\tbox.radius = 0.1f * scale;\n\tb2CreatePolygonShape( m_chassisId, &shapeDef, &box );\n\n\tbox = b2MakeOffsetBox( 0.05f * scale, 0.35f * scale,  { -3.25f * scale, 0.375f * scale }, b2Rot_identity  );\n\tbox.radius = 0.1f * scale;\n\tb2CreatePolygonShape( m_chassisId, &shapeDef, &box );\n\n\tshapeDef.density = 2.0f * density;\n\tshapeDef.material.friction = 2.5f;\n\tshapeDef.material.customColor = b2_colorSilver;\n\n\tb2Circle circle = { { 0.0f, 0.0f }, 0.4f * scale };\n\tbodyDef.position = b2Add( { -2.75f * scale, 0.3f * scale }, position );\n\tm_rearWheelId = b2CreateBody( worldId, &bodyDef );\n\tb2CreateCircleShape( m_rearWheelId, &shapeDef, &circle );\n\n\tbodyDef.position = b2Add( { 0.8f * scale, 0.3f * scale }, position );\n\tm_frontWheelId = b2CreateBody( worldId, &bodyDef );\n\tb2CreateCircleShape( m_frontWheelId, &shapeDef, &circle );\n\n\tb2Vec2 axis = { 0.0f, 1.0f };\n\tb2Vec2 pivot = b2Body_GetPosition( m_rearWheelId );\n\n\t// float throttle = 0.0f;\n\t// float speed = 35.0f;\n\t// float torque = 2.5f * scale;\n\t// float hertz = 5.0f;\n\t// float dampingRatio = 0.7f;\n\n\tb2WheelJointDef jointDef = b2DefaultWheelJointDef();\n\n\tjointDef.base.bodyIdA = m_chassisId;\n\tjointDef.base.bodyIdB = m_rearWheelId;\n\tjointDef.base.localFrameA.q = b2MakeRot( 0.5f * B2_PI );\n\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\tjointDef.motorSpeed = 0.0f;\n\tjointDef.maxMotorTorque = torque;\n\tjointDef.enableMotor = true;\n\tjointDef.hertz = hertz;\n\tjointDef.dampingRatio = dampingRatio;\n\tjointDef.lowerTranslation = -0.25f * scale;\n\tjointDef.upperTranslation = 0.25f * scale;\n\tjointDef.enableLimit = true;\n\tm_rearAxleId = b2CreateWheelJoint( worldId, &jointDef );\n\n\tpivot = b2Body_GetPosition( m_frontWheelId );\n\tjointDef.base.bodyIdA = m_chassisId;\n\tjointDef.base.bodyIdB = m_frontWheelId;\n\tjointDef.base.localFrameA.q = b2MakeRot( 0.5f * B2_PI );\n\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\tjointDef.motorSpeed = 0.0f;\n\tjointDef.maxMotorTorque = torque;\n\tjointDef.enableMotor = true;\n\tjointDef.hertz = hertz;\n\tjointDef.dampingRatio = dampingRatio;\n\tjointDef.lowerTranslation = -0.25f * scale;\n\tjointDef.upperTranslation = 0.25f * scale;\n\tjointDef.enableLimit = true;\n\tm_frontAxleId = b2CreateWheelJoint( worldId, &jointDef );\n}\n\nvoid Truck::Despawn()\n{\n\tassert( m_isSpawned == true );\n\n\tb2DestroyJoint( m_rearAxleId, false );\n\tb2DestroyJoint( m_frontAxleId, false );\n\tb2DestroyBody( m_rearWheelId );\n\tb2DestroyBody( m_frontWheelId );\n\tb2DestroyBody( m_chassisId );\n\n\tm_isSpawned = false;\n}\n\nvoid Truck::SetSpeed( float speed )\n{\n\tb2WheelJoint_SetMotorSpeed( m_rearAxleId, speed );\n\tb2WheelJoint_SetMotorSpeed( m_frontAxleId, speed );\n\tb2Joint_WakeBodies( m_rearAxleId );\n}\n\nvoid Truck::SetTorque( float torque )\n{\n\tb2WheelJoint_SetMaxMotorTorque( m_rearAxleId, torque );\n\tb2WheelJoint_SetMaxMotorTorque( m_frontAxleId, torque );\n}\n\nvoid Truck::SetHertz( float hertz )\n{\n\tb2WheelJoint_SetSpringHertz( m_rearAxleId, hertz );\n\tb2WheelJoint_SetSpringHertz( m_frontAxleId, hertz );\n}\n\nvoid Truck::SetDampingRadio( float dampingRatio )\n{\n\tb2WheelJoint_SetSpringDampingRatio( m_rearAxleId, dampingRatio );\n\tb2WheelJoint_SetSpringDampingRatio( m_frontAxleId, dampingRatio );\n}\n"
  },
  {
    "path": "samples/car.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"box2d/types.h\"\n\nclass Car\n{\npublic:\n\tCar();\n\n\tvoid Spawn( b2WorldId worldId, b2Vec2 position, float scale, float hertz, float dampingRatio, float torque, void* userData );\n\tvoid Despawn();\n\n\tvoid SetSpeed( float speed );\n\tvoid SetTorque( float torque );\n\tvoid SetHertz( float hertz );\n\tvoid SetDampingRadio( float dampingRatio );\n\n\tb2BodyId m_chassisId;\n\tb2BodyId m_rearWheelId;\n\tb2BodyId m_frontWheelId;\n\tb2JointId m_rearAxleId;\n\tb2JointId m_frontAxleId;\n\tbool m_isSpawned;\n};\n\nclass Truck\n{\npublic:\n\tTruck();\n\n\tvoid Spawn( b2WorldId worldId, b2Vec2 position, float scale, float hertz, float dampingRatio, float torque, float density,\n\t\t\t\tvoid* userData );\n\tvoid Despawn();\n\n\tvoid SetSpeed( float speed );\n\tvoid SetTorque( float torque );\n\tvoid SetHertz( float hertz );\n\tvoid SetDampingRadio( float dampingRatio );\n\n\tb2BodyId m_chassisId;\n\tb2BodyId m_rearWheelId;\n\tb2BodyId m_frontWheelId;\n\tb2JointId m_rearAxleId;\n\tb2JointId m_frontAxleId;\n\tbool m_isSpawned;\n};\n"
  },
  {
    "path": "samples/container.c",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"container.h\"\n\n#include <assert.h>\n#include <stdlib.h>\n#include <string.h>\n\nvoid* GrowAlloc( void* oldMem, int oldSize, int newSize )\n{\n\tassert( newSize > oldSize );\n\tvoid* newMem = malloc( newSize );\n\tif ( oldSize > 0 )\n\t{\n\t\tmemcpy( newMem, oldMem, oldSize );\n\t\tfree( oldMem );\n\t}\n\n\treturn newMem;\n}\n"
  },
  {
    "path": "samples/container.h",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#define NULL_INDEX -1\n\n// Array declaration. Works with forward declaration of TYPE.\n#define ARRAY_DECLARE( TYPE )                                                                                                    \\\n\ttypedef struct                                                                                                               \\\n\t{                                                                                                                            \\\n\t\tTYPE* data;                                                                                                              \\\n\t\tint count;                                                                                                               \\\n\t\tint capacity;                                                                                                            \\\n\t} TYPE##Array;                                                                                                              \\\n\tTYPE##Array TYPE##Array_Create( int capacity );                                                                            \\\n\tvoid TYPE##Array_Reserve( TYPE##Array* a, int newCapacity );                                                               \\\n\tvoid TYPE##Array_Destroy( TYPE##Array* a )\n\n// Inline array functions that need the TYPE to be defined.\n#define ARRAY_INLINE( TYPE )                                                                                                     \\\n\tstatic inline void TYPE##Array_Resize( TYPE##Array* a, int count )                                                         \\\n\t{                                                                                                                            \\\n\t\tTYPE##Array_Reserve( a, count );                                                                                        \\\n\t\ta->count = count;                                                                                                        \\\n\t}                                                                                                                            \\\n\tstatic inline TYPE* TYPE##Array_Get( TYPE##Array* a, int index )                                                           \\\n\t{                                                                                                                            \\\n\t\tassert( 0 <= index && index < a->count );                                                                                \\\n\t\treturn a->data + index;                                                                                                  \\\n\t}                                                                                                                            \\\n\tstatic inline TYPE* TYPE##Array_Add( TYPE##Array* a )                                                                      \\\n\t{                                                                                                                            \\\n\t\tif ( a->count == a->capacity )                                                                                           \\\n\t\t{                                                                                                                        \\\n\t\t\tint newCapacity = a->capacity < 2 ? 2 : a->capacity + ( a->capacity >> 1 );                                          \\\n\t\t\tTYPE##Array_Reserve( a, newCapacity );                                                                              \\\n\t\t}                                                                                                                        \\\n\t\ta->count += 1;                                                                                                           \\\n\t\treturn a->data + ( a->count - 1 );                                                                                       \\\n\t}                                                                                                                            \\\n\tstatic inline void TYPE##Array_Push( TYPE##Array* a, TYPE value )                                                          \\\n\t{                                                                                                                            \\\n\t\tif ( a->count == a->capacity )                                                                                           \\\n\t\t{                                                                                                                        \\\n\t\t\tint newCapacity = a->capacity < 2 ? 2 : a->capacity + ( a->capacity >> 1 );                                          \\\n\t\t\tTYPE##Array_Reserve( a, newCapacity );                                                                              \\\n\t\t}                                                                                                                        \\\n\t\ta->data[a->count] = value;                                                                                               \\\n\t\ta->count += 1;                                                                                                           \\\n\t}                                                                                                                            \\\n\tstatic inline void TYPE##Array_Set( TYPE##Array* a, int index, TYPE value )                                                \\\n\t{                                                                                                                            \\\n\t\tassert( 0 <= index && index < a->count );                                                                                \\\n\t\ta->data[index] = value;                                                                                                  \\\n\t}                                                                                                                            \\\n\tstatic inline int TYPE##Array_RemoveSwap( TYPE##Array* a, int index )                                                      \\\n\t{                                                                                                                            \\\n\t\tassert( 0 <= index && index < a->count );                                                                                \\\n\t\tint movedIndex = NULL_INDEX;                                                                                             \\\n\t\tif ( index != a->count - 1 )                                                                                             \\\n\t\t{                                                                                                                        \\\n\t\t\tmovedIndex = a->count - 1;                                                                                           \\\n\t\t\ta->data[index] = a->data[movedIndex];                                                                                \\\n\t\t}                                                                                                                        \\\n\t\ta->count -= 1;                                                                                                           \\\n\t\treturn movedIndex;                                                                                                       \\\n\t}                                                                                                                            \\\n\tstatic inline TYPE TYPE##Array_Pop( TYPE##Array* a )                                                                       \\\n\t{                                                                                                                            \\\n\t\tassert( a->count > 0 );                                                                                                  \\\n\t\tTYPE value = a->data[a->count - 1];                                                                                      \\\n\t\ta->count -= 1;                                                                                                           \\\n\t\treturn value;                                                                                                            \\\n\t}                                                                                                                            \\\n\n// These functions go in the source file\n#define ARRAY_SOURCE( TYPE )                                                                                                     \\\n\tTYPE##Array TYPE##Array_Create( int capacity )                                                                             \\\n\t{                                                                                                                            \\\n\t\tTYPE##Array a = { 0 };                                                                                                  \\\n\t\tif ( capacity > 0 )                                                                                                      \\\n\t\t{                                                                                                                        \\\n\t\t\ta.data = malloc( capacity * sizeof( TYPE ) );                                                                        \\\n\t\t\ta.capacity = capacity;                                                                                               \\\n\t\t}                                                                                                                        \\\n\t\treturn a;                                                                                                                \\\n\t}                                                                                                                            \\\n\tvoid TYPE##Array_Reserve( TYPE##Array* a, int newCapacity )                                                                \\\n\t{                                                                                                                            \\\n\t\tif ( newCapacity <= a->capacity )                                                                                        \\\n\t\t{                                                                                                                        \\\n\t\t\treturn;                                                                                                              \\\n\t\t}                                                                                                                        \\\n\t\ta->data = GrowAlloc( a->data, a->capacity * sizeof( TYPE ), newCapacity * sizeof( TYPE ) );                              \\\n\t\ta->capacity = newCapacity;                                                                                               \\\n\t}                                                                                                                            \\\n\tvoid TYPE##Array_Destroy( TYPE##Array* a )                                                                                 \\\n\t{                                                                                                                            \\\n\t\tfree( a->data );                                                                           \\\n\t\ta->data = NULL;                                                                                                          \\\n\t\ta->count = 0;                                                                                                            \\\n\t\ta->capacity = 0;                                                                                                         \\\n\t}\n\nvoid* GrowAlloc( void* oldMem, int oldSize, int newSize );\n"
  },
  {
    "path": "samples/data/background.fs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nout vec4 FragColor;\n\nuniform float time;\nuniform vec2 resolution;\nuniform vec3 baseColor;\n\n// A simple pseudo-random function\nfloat random(vec2 st)\n{\n    return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);\n}\n\nvoid main()\n{\n    vec2 uv = gl_FragCoord.xy / resolution.xy;\n    \n    // Create some noise\n    float noise = random(uv + time * 0.1);\n    \n    // Adjust these values to control the intensity and color of the grain\n    float grainIntensity = 0.01;\n    \n    // Mix the base color with the noise\n    vec3 color = baseColor + vec3(noise * grainIntensity);\n    \n    FragColor = vec4(color, 1.0);\n}\n\n"
  },
  {
    "path": "samples/data/background.vs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n#version 330\n\nlayout(location = 0) in vec2 v_position;\n\nvoid main(void)\n{\n    gl_Position = vec4(v_position, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "samples/data/circle.fs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nin vec2 f_position;\nin vec4 f_color;\nin float f_thickness;\n\nout vec4 fragColor;\n\nvoid main()\n{\n    // radius in unit quad\n    float radius = 1.0;\n\n    // distance to circle\n    vec2 w = f_position;\n    float dw = length(w);\n    float d = abs(dw - radius);\n\n    fragColor = vec4(f_color.rgb, smoothstep(f_thickness, 0.0, d));\n}\n"
  },
  {
    "path": "samples/data/circle.vs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nuniform mat4 projectionMatrix;\nuniform float pixelScale;\n\nlayout(location = 0) in vec2 v_localPosition;\nlayout(location = 1) in vec2 v_instancePosition;\nlayout(location = 2) in float v_instanceRadius;\nlayout(location = 3) in vec4 v_instanceColor;\n\nout vec2 f_position;\nout vec4 f_color;\nout float f_thickness;\n\nvoid main()\n{\n    f_position = v_localPosition;\n    f_color = v_instanceColor;\n    float radius = v_instanceRadius;\n\n    // resolution.y = pixelScale * radius\n    f_thickness = 3.0f / (pixelScale * radius);\n\n    vec2 p = vec2(radius * v_localPosition.x, radius * v_localPosition.y) + v_instancePosition;\n    gl_Position = projectionMatrix * vec4(p, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "samples/data/font.fs",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330 core\n\nin vec4 color;\nin vec2 uv;\n\nuniform sampler2D FontAtlas;\n\nout vec4 fragColor;\n\nvoid main()\n{\n\tfragColor = vec4(color.rgb, color.a * texture(FontAtlas, uv).r);\n}\n"
  },
  {
    "path": "samples/data/font.vs",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330 core\n\nlayout (location = 0) in vec2 aPosition;\nlayout (location = 1) in vec2 aUV;\nlayout (location = 2) in vec4 aColor;\n\nout vec4 color;\nout vec2 uv;\n\nuniform mat4 ProjectionMatrix;\n\nvoid main()\n{\n\tgl_Position = ProjectionMatrix * vec4(aPosition, 0.0, 1.0);\n\n\tcolor = aColor;\n\tuv = aUV;\n}\n"
  },
  {
    "path": "samples/data/line.fs",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nin vec4 f_color;\nout vec4 color;\n\nvoid main(void)\n{\n    color = f_color;\n}\n"
  },
  {
    "path": "samples/data/line.vs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nuniform mat4 projectionMatrix;\nlayout(location = 0) in vec2 v_position;\nlayout(location = 1) in vec4 v_color;\n\nout vec4 f_color;\n\nvoid main(void)\n{\n    f_color = v_color;\n    gl_Position = projectionMatrix * vec4(v_position, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "samples/data/point.fs",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nin vec4 f_color;\nout vec4 color;\n\nvoid main(void)\n{\n    color = f_color;\n}\n"
  },
  {
    "path": "samples/data/point.vs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nuniform mat4 projectionMatrix;\nlayout(location = 0) in vec2 v_position;\nlayout(location = 1) in float v_size;\nlayout(location = 2) in vec4 v_color;\n\nout vec4 f_color;\n\nvoid main(void)\n{\n\tf_color = v_color;\n\tgl_Position = projectionMatrix * vec4(v_position, 0.0f, 1.0f);\n\tgl_PointSize = v_size;\n}\n"
  },
  {
    "path": "samples/data/solid_capsule.fs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nin vec2 f_position;\nin vec4 f_color;\nin float f_length;\nin float f_thickness;\n\nout vec4 color;\n\n// Thanks to baz and kolyan3040 for help on this shader\n// todo this can be optimized a bit, keeping some terms for clarity\n\n// https://en.wikipedia.org/wiki/Alpha_compositing\nvec4 blend_colors(vec4 front,vec4 back)\n{\n    vec3 cSrc = front.rgb;\n    float alphaSrc = front.a;\n    vec3 cDst = back.rgb;\n    float alphaDst = back.a;\n    \n    vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc);\n    float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc);\n    \n    // remove alpha from rgb\n    cOut = cOut / alphaOut;\n    \n    return vec4(cOut, alphaOut);\n}\n\nvoid main()\n{\n    // radius in unit quad\n    float radius = 0.5 * (2.0 - f_length);\n    \n    vec4 borderColor = f_color;\n    vec4 fillColor = 0.6f * borderColor;\n    \n    vec2 v1 = vec2(-0.5 * f_length, 0);\n    vec2 v2 = vec2(0.5 * f_length, 0);\n    \n    // distance to line segment\n    vec2 e = v2 - v1;\n    vec2 w = f_position - v1;\n    float we = dot(w, e);\n    vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0);\n    float dw = length(b);\n    \n    // SDF union of capsule and line segment\n    float d = min(dw, abs(dw - radius));\n    \n    // roll the fill alpha down at the border\n    vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(radius + f_thickness, radius, dw));\n\n    // roll the border alpha down from 1 to 0 across the border thickness\n    vec4 front = vec4(borderColor.rgb, smoothstep(f_thickness, 0.0f, d));\n    \n    color = blend_colors(front, back);\n}\n"
  },
  {
    "path": "samples/data/solid_capsule.vs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nuniform mat4 projectionMatrix;\nuniform float pixelScale;\n\nlayout(location=0) in vec2 v_localPosition;\nlayout(location=1) in vec4 v_instanceTransform;\nlayout(location=2) in float v_instanceRadius;\nlayout(location=3) in float v_instanceLength;\nlayout(location=4) in vec4 v_instanceColor;\n\nout vec2 f_position;\nout vec4 f_color;\nout float f_length;\nout float f_thickness;\n\nvoid main()\n{\n    f_position = v_localPosition;\n    f_color = v_instanceColor;\n    \n    float radius = v_instanceRadius;\n    float length = v_instanceLength;\n    \n    // scale quad large enough to hold capsule\n    float scale = radius + 0.5 * length;\n    \n    // quad range of [-1, 1] implies normalize radius and length\n    f_length = length / scale;\n    \n    // resolution.y = pixelScale * scale\n    f_thickness = 3.0f / (pixelScale * scale);\n    \n    float x = v_instanceTransform.x;\n    float y = v_instanceTransform.y;\n    float c = v_instanceTransform.z;\n    float s = v_instanceTransform.w;\n    vec2 p = vec2(scale * v_localPosition.x, scale * v_localPosition.y);\n    p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y);\n    gl_Position = projectionMatrix * vec4(p, 0.0, 1.0);\n}\n"
  },
  {
    "path": "samples/data/solid_circle.fs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nin vec2 f_position;\nin vec4 f_color;\nin float f_thickness;\n\nout vec4 fragColor;\n\n// https://en.wikipedia.org/wiki/Alpha_compositing\nvec4 blend_colors(vec4 front, vec4 back)\n{\n    vec3 cSrc = front.rgb;\n    float alphaSrc = front.a;\n    vec3 cDst = back.rgb;\n    float alphaDst = back.a;\n\n    vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc);\n    float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc);\n    cOut = cOut / alphaOut;\n\n    return vec4(cOut, alphaOut);\n}\n\nvoid main()\n{\n    // radius in unit quad\n    float radius = 1.0;\n\n    // distance to axis line segment\n    vec2 e = vec2(radius, 0);\n    vec2 w = f_position;\n    float we = dot(w, e);\n    vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0);\n    float da = length(b);\n\n    // distance to circle\n    float dw = length(w);\n    float dc = abs(dw - radius);\n\n    // union of circle and axis\n    float d = min(da, dc);\n\n    vec4 borderColor = f_color;\n    vec4 fillColor = 0.6f * borderColor;\n\n    // roll the fill alpha down at the border\n    vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(radius + f_thickness, radius, dw));\n\n    // roll the border alpha down from 1 to 0 across the border thickness\n    vec4 front = vec4(borderColor.rgb, smoothstep(f_thickness, 0.0f, d));\n\n    fragColor = blend_colors(front, back);\n}\n"
  },
  {
    "path": "samples/data/solid_circle.vs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nuniform mat4 projectionMatrix;\nuniform float pixelScale;\n\nlayout(location = 0) in vec2 v_localPosition;\nlayout(location = 1) in vec4 v_instanceTransform;\nlayout(location = 2) in float v_instanceRadius;\nlayout(location = 3) in vec4 v_instanceColor;\n\nout vec2 f_position;\nout vec4 f_color;\nout float f_thickness;\n\nvoid main()\n{\n    f_position = v_localPosition;\n    f_color = v_instanceColor;\n    float radius = v_instanceRadius;\n\n    // resolution.y = pixelScale * radius\n    f_thickness = 3.0f / (pixelScale * radius);\n    \n    float x = v_instanceTransform.x;\n    float y = v_instanceTransform.y;\n    float c = v_instanceTransform.z;\n    float s = v_instanceTransform.w;\n    vec2 p = vec2(radius * v_localPosition.x, radius * v_localPosition.y);\n    p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y);\n    gl_Position = projectionMatrix * vec4(p, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "samples/data/solid_polygon.fs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nin vec2 f_position;\nin vec2 f_points[8];\nflat in int f_count;\nin float f_radius;\nin vec4 f_color;\nin float f_thickness;\n\nout vec4 fragColor;\n\n// https://en.wikipedia.org/wiki/Alpha_compositing\nvec4 blend_colors(vec4 front, vec4 back)\n{\n    vec3 cSrc = front.rgb;\n    float alphaSrc = front.a;\n    vec3 cDst = back.rgb;\n    float alphaDst = back.a;\n\n    vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc);\n    float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc);\n\n    // remove alpha from rgb\n    cOut = cOut / alphaOut;\n\n    return vec4(cOut, alphaOut);\n}\n\nfloat cross2d(in vec2 v1, in vec2 v2)\n{\n    return v1.x * v2.y - v1.y * v2.x;\n}\n\n// Signed distance function for convex polygon\nfloat sdConvexPolygon(in vec2 p, in vec2[8] v, in int count)\n{\n    // Initial squared distance\n    float d = dot(p - v[0], p - v[0]);\n\n    // Consider query point inside to start\n    float side = -1.0;\n    int j = count - 1;\n    for (int i = 0; i < count; ++i)\n    {\n        // Distance to a polygon edge\n        vec2 e = v[i] - v[j];\n        vec2 w = p - v[j];\n        float we = dot(w, e);\n        vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0);\n        float bb = dot(b, b);\n\n        // Track smallest distance\n        if (bb < d)\n        {\n            d = bb;\n        }\n\n        // If the query point is outside any edge then it is outside the entire polygon.\n        // This depends on the CCW winding order of points.\n        float s = cross2d(w, e);\n        if (s >= 0.0)\n        {\n            side = 1.0;\n        }\n\n        j = i;\n    }\n\n    return side * sqrt(d);\n}\n\nvoid main()\n{\n    vec4 borderColor = f_color;\n    vec4 fillColor = 0.6f * borderColor;\n\n    float dw = sdConvexPolygon(f_position, f_points, f_count);\n    float d = abs(dw - f_radius);\n\n    // roll the fill alpha down at the border\n    vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(f_radius + f_thickness, f_radius, dw));\n\n    // roll the border alpha down from 1 to 0 across the border thickness\n    vec4 front = vec4(borderColor.rgb, smoothstep(f_thickness, 0.0f, d));\n\n    fragColor = blend_colors(front, back);\n\n    // todo debugging\n    // float resy = 3.0f / f_thickness;\n\n    // if (resy < 539.9f)\n    // {\n    //     fragColor = vec4(1, 0, 0, 1);\n    // }\n    // else if (resy > 540.1f)\n    // {\n    //     fragColor = vec4(0, 1, 0, 1);\n    // }\n    // else\n    // {\n    //     fragColor = vec4(0, 0, 1, 1);\n    // }\n}\n"
  },
  {
    "path": "samples/data/solid_polygon.vs",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#version 330\n\nuniform mat4 projectionMatrix;\nuniform float pixelScale;\n\nlayout(location = 0) in vec2 v_localPosition;\nlayout(location = 1) in vec4 v_instanceTransform;\nlayout(location = 2) in vec4 v_instancePoints12;\nlayout(location = 3) in vec4 v_instancePoints34;\nlayout(location = 4) in vec4 v_instancePoints56;\nlayout(location = 5) in vec4 v_instancePoints78;\nlayout(location = 6) in int v_instanceCount;\nlayout(location = 7) in float v_instanceRadius;\nlayout(location = 8) in vec4 v_instanceColor;\n\nout vec2 f_position;\nout vec4 f_color;\nout vec2 f_points[8];\nflat out int f_count;\nout float f_radius;\nout float f_thickness;\n\nvoid main()\n{\n    f_position = v_localPosition;\n    f_color = v_instanceColor;\n\n    f_radius = v_instanceRadius;\n    f_count = v_instanceCount;\n\n    f_points[0] = v_instancePoints12.xy;\n    f_points[1] = v_instancePoints12.zw;\n    f_points[2] = v_instancePoints34.xy;\n    f_points[3] = v_instancePoints34.zw;\n    f_points[4] = v_instancePoints56.xy;\n    f_points[5] = v_instancePoints56.zw;\n    f_points[6] = v_instancePoints78.xy;\n    f_points[7] = v_instancePoints78.zw;\n\n    // Compute polygon AABB\n    vec2 lower = f_points[0];\n    vec2 upper = f_points[0];\n    for (int i = 1; i < v_instanceCount; ++i)\n    {\n        lower = min(lower, f_points[i]);\n        upper = max(upper, f_points[i]);\n    }\n\n    vec2 center = 0.5 * (lower + upper);\n    vec2 width = upper - lower;\n    float maxWidth = max(width.x, width.y);\n\n    float scale = f_radius + 0.5 * maxWidth;\n    float invScale = 1.0 / scale;\n\n    // Shift and scale polygon points so they fit in 2x2 quad\n    for (int i = 0; i < f_count; ++i)\n    {\n        f_points[i] = invScale * (f_points[i] - center);\n    }\n\n    // Scale radius as well\n    f_radius = invScale * f_radius;\n\n    // resolution.y = pixelScale * scale\n    f_thickness = 3.0f / (pixelScale * scale);\n\n    // scale up and transform quad to fit polygon\n    float x = v_instanceTransform.x;\n    float y = v_instanceTransform.y;\n    float c = v_instanceTransform.z;\n    float s = v_instanceTransform.w;\n    vec2 p = vec2(scale * v_localPosition.x, scale * v_localPosition.y) + center;\n    p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y);\n    gl_Position = projectionMatrix * vec4(p, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "samples/donut.cpp",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"donut.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <assert.h>\n\nDonut::Donut()\n{\n\tfor ( int i = 0; i < m_sides; ++i )\n\t{\n\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\tm_jointIds[i] = b2_nullJointId;\n\t}\n\n\tm_isSpawned = false;\n}\n\nvoid Donut::Create( b2WorldId worldId, b2Vec2 position, float scale, int groupIndex, bool enableSensorEvents, void* userData )\n{\n\tassert( m_isSpawned == false );\n\n\tfor ( int i = 0; i < m_sides; ++i )\n\t{\n\t\tassert( B2_IS_NULL( m_bodyIds[i] ) );\n\t\tassert( B2_IS_NULL( m_jointIds[i] ) );\n\t}\n\n\tfloat radius = 1.0f * scale;\n\tfloat deltaAngle = 2.0f * B2_PI / m_sides;\n\tfloat length = 2.0f * B2_PI * radius / m_sides;\n\n\tb2Capsule capsule = { { 0.0f, -0.5f * length }, { 0.0f, 0.5f * length }, 0.25f * scale };\n\n\tb2Vec2 center = position;\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tbodyDef.userData = userData;\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.enableSensorEvents = enableSensorEvents;\n\tshapeDef.filter.groupIndex = -groupIndex;\n\tshapeDef.material.friction = 0.3f;\n\n\t// Create bodies\n\tfloat angle = 0.0f;\n\tfor ( int i = 0; i < m_sides; ++i )\n\t{\n\t\tbodyDef.position = { radius * cosf( angle ) + center.x, radius * sinf( angle ) + center.y };\n\t\tbodyDef.rotation = b2MakeRot( angle );\n\n\t\tm_bodyIds[i] = b2CreateBody( worldId, &bodyDef );\n\t\tb2CreateCapsuleShape( m_bodyIds[i], &shapeDef, &capsule );\n\n\t\tangle += deltaAngle;\n\t}\n\n\t// Create joints\n\tb2WeldJointDef weldDef = b2DefaultWeldJointDef();\n\tweldDef.angularHertz = 5.0f;\n\tweldDef.angularDampingRatio = 0.0f;\n\tweldDef.base.localFrameA.p = { 0.0f, 0.5f * length };\n\tweldDef.base.localFrameB.p = { 0.0f, -0.5f * length };\n\tweldDef.base.drawScale = 0.5f * scale;\n\n\tb2BodyId prevBodyId = m_bodyIds[m_sides - 1];\n\tfor ( int i = 0; i < m_sides; ++i )\n\t{\n\t\tweldDef.base.bodyIdA = prevBodyId;\n\t\tweldDef.base.bodyIdB = m_bodyIds[i];\n\t\tb2Rot qA = b2Body_GetRotation( prevBodyId );\n\t\tb2Rot qB = b2Body_GetRotation( m_bodyIds[i] );\n\t\tweldDef.base.localFrameA.q = b2InvMulRot( qA, qB );\n\t\tm_jointIds[i] = b2CreateWeldJoint( worldId, &weldDef );\n\t\tprevBodyId = weldDef.base.bodyIdB;\n\t}\n\n\tm_isSpawned = true;\n}\n\nvoid Donut::Destroy()\n{\n\tassert( m_isSpawned == true );\n\n\tfor ( int i = 0; i < m_sides; ++i )\n\t{\n\t\tb2DestroyBody( m_bodyIds[i] );\n\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\tm_jointIds[i] = b2_nullJointId;\n\t}\n\n\tm_isSpawned = false;\n}\n"
  },
  {
    "path": "samples/donut.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"box2d/types.h\"\n\nclass Donut\n{\npublic:\n\tDonut();\n\n\tvoid Create( b2WorldId worldId, b2Vec2 position, float scale, int groupIndex, bool enableSensorEvents, void* userData );\n\tvoid Destroy();\n\n\tstatic constexpr int m_sides = 7;\n\tb2BodyId m_bodyIds[m_sides];\n\tb2JointId m_jointIds[m_sides];\n\tbool m_isSpawned;\n};\n"
  },
  {
    "path": "samples/doohickey.cpp",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"doohickey.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <assert.h>\n\nDoohickey::Doohickey()\n{\n\tm_wheelId1 = {};\n\tm_wheelId2 = {};\n\tm_barId1 = {};\n\tm_barId2 = {};\n\n\tm_axleId1 = {};\n\tm_axleId2 = {};\n\tm_sliderId = {};\n\n\tm_isSpawned = false;\n}\n\nvoid Doohickey::Spawn( b2WorldId worldId, b2Vec2 position, float scale )\n{\n\tassert( m_isSpawned == false );\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.material.rollingResistance = 0.1f;\n\n\tb2Circle circle = { { 0.0f, 0.0f }, 1.0f * scale };\n\tb2Capsule capsule = { { -3.5f * scale, 0.0f }, { 3.5f * scale, 0.0f }, 0.15f * scale };\n\n\tbodyDef.position = b2MulAdd( position, scale, { -5.0f, 3.0f } );\n\tm_wheelId1 = b2CreateBody( worldId, &bodyDef );\n\tb2CreateCircleShape( m_wheelId1, &shapeDef, &circle );\n\n\tbodyDef.position = b2MulAdd( position, scale, { 5.0f, 3.0f } );\n\tm_wheelId2 = b2CreateBody( worldId, &bodyDef );\n\tb2CreateCircleShape( m_wheelId2, &shapeDef, &circle );\n\n\tbodyDef.position = b2MulAdd( position, scale, { -1.5f, 3.0f } );\n\tm_barId1 = b2CreateBody( worldId, &bodyDef );\n\tb2CreateCapsuleShape( m_barId1, &shapeDef, &capsule );\n\n\tbodyDef.position = b2MulAdd( position, scale, { 1.5f, 3.0f } );\n\tm_barId2 = b2CreateBody( worldId, &bodyDef );\n\tb2CreateCapsuleShape( m_barId2, &shapeDef, &capsule );\n\n\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\n\trevoluteDef.base.bodyIdA = m_wheelId1;\n\trevoluteDef.base.bodyIdB = m_barId1;\n\trevoluteDef.base.localFrameA.p = { 0.0f, 0.0f };\n\trevoluteDef.base.localFrameB.p = { -3.5f * scale, 0.0f };\n\trevoluteDef.enableMotor = true;\n\trevoluteDef.maxMotorTorque = 2.0f * scale;\n\tb2CreateRevoluteJoint( worldId, &revoluteDef );\n\n\trevoluteDef.base.bodyIdA = m_wheelId2;\n\trevoluteDef.base.bodyIdB = m_barId2;\n\trevoluteDef.base.localFrameA.p = { 0.0f, 0.0f };\n\trevoluteDef.base.localFrameB.p = { 3.5f * scale, 0.0f };\n\trevoluteDef.enableMotor = true;\n\trevoluteDef.maxMotorTorque = 2.0f * scale;\n\tb2CreateRevoluteJoint( worldId, &revoluteDef );\n\n\tb2PrismaticJointDef prismaticDef = b2DefaultPrismaticJointDef();\n\tprismaticDef.base.bodyIdA = m_barId1;\n\tprismaticDef.base.bodyIdB = m_barId2;\n\tprismaticDef.base.localFrameA.p = { 2.0f * scale, 0.0f };\n\tprismaticDef.base.localFrameB.p = { -2.0f * scale, 0.0f };\n\tprismaticDef.lowerTranslation = -2.0f * scale;\n\tprismaticDef.upperTranslation = 2.0f * scale;\n\tprismaticDef.enableLimit = true;\n\tprismaticDef.enableMotor = true;\n\tprismaticDef.maxMotorForce = 2.0f * scale;\n\tprismaticDef.enableSpring = true;\n\tprismaticDef.hertz = 1.0f;\n\tprismaticDef.dampingRatio = 0.5;\n\tb2CreatePrismaticJoint( worldId, &prismaticDef );\n}\n\nvoid Doohickey::Despawn()\n{\n\tassert( m_isSpawned == true );\n\n\tb2DestroyJoint( m_axleId1, false );\n\tb2DestroyJoint( m_axleId2, false );\n\tb2DestroyJoint( m_sliderId, false );\n\n\tb2DestroyBody( m_wheelId1 );\n\tb2DestroyBody( m_wheelId2 );\n\tb2DestroyBody( m_barId1 );\n\tb2DestroyBody( m_barId2 );\n\n\tm_isSpawned = false;\n}\n"
  },
  {
    "path": "samples/doohickey.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"box2d/types.h\"\n\nclass Doohickey\n{\npublic:\n\tDoohickey();\n\n\tvoid Spawn( b2WorldId worldId, b2Vec2 position, float scale );\n\tvoid Despawn();\n\n\tb2BodyId m_wheelId1;\n\tb2BodyId m_wheelId2;\n\tb2BodyId m_barId1;\n\tb2BodyId m_barId2;\n\n\tb2JointId m_axleId1;\n\tb2JointId m_axleId2;\n\tb2JointId m_sliderId;\n\n\tbool m_isSpawned;\n};\n"
  },
  {
    "path": "samples/draw.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"draw.h\"\n\n#include \"container.h\"\n#include \"shader.h\"\n\n#include \"box2d/math_functions.h\"\n\n#include <stdarg.h>\n#include <stddef.h>\n#include <stdio.h>\n\n#if defined( _MSC_VER )\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#include <stdlib.h>\n#else\n#include <stdlib.h>\n#endif\n\n// clang-format off\n#include <glad/glad.h>\n#include <GLFW/glfw3.h>\n// clang-format on\n\n#define STBTT_STATIC\n#define STB_TRUETYPE_IMPLEMENTATION\n#include \"stb_truetype.h\"\n\n//#define STB_IMAGE_WRITE_IMPLEMENTATION\n//#include \"stb_image_write.h\"\n\n#define BUFFER_OFFSET( x ) ( (const void*)( x ) )\n\n#define SHADER_TEXT( x ) \"#version 330\\n\" #x\n\ntypedef struct\n{\n\tuint8_t r, g, b, a;\n} RGBA8;\n\nstatic inline RGBA8 MakeRGBA8( b2HexColor c, float alpha )\n{\n\treturn (RGBA8){\n\t\t(uint8_t)( ( c >> 16 ) & 0xFF ),\n\t\t(uint8_t)( ( c >> 8 ) & 0xFF ),\n\t\t(uint8_t)( c & 0xFF ),\n\t\t(uint8_t)( 0xFF * alpha ),\n\t};\n}\n\nCamera GetDefaultCamera( void )\n{\n\treturn (Camera){\n\t\t.center = { 0.0f, 20.0f },\n\t\t.zoom = 1.0f,\n\t\t.width = 1920.0f,\n\t\t.height = 1080.0f,\n\t};\n}\n\nvoid ResetView( Camera* camera )\n{\n\tcamera->center = (b2Vec2){ 0.0f, 20.0f };\n\tcamera->zoom = 1.0f;\n}\n\nb2Vec2 ConvertScreenToWorld( Camera* camera, b2Vec2 screenPoint )\n{\n\tfloat w = camera->width;\n\tfloat h = camera->height;\n\tfloat u = screenPoint.x / w;\n\tfloat v = ( h - screenPoint.y ) / h;\n\n\tfloat ratio = w / h;\n\tb2Vec2 extents = { camera->zoom * ratio, camera->zoom };\n\n\tb2Vec2 lower = b2Sub( camera->center, extents );\n\tb2Vec2 upper = b2Add( camera->center, extents );\n\n\tb2Vec2 pw = { ( 1.0f - u ) * lower.x + u * upper.x, ( 1.0f - v ) * lower.y + v * upper.y };\n\treturn pw;\n}\n\nb2Vec2 ConvertWorldToScreen( Camera* camera, b2Vec2 worldPoint )\n{\n\tfloat w = camera->width;\n\tfloat h = camera->height;\n\tfloat ratio = w / h;\n\n\tb2Vec2 extents = { camera->zoom * ratio, camera->zoom };\n\n\tb2Vec2 lower = b2Sub( camera->center, extents );\n\tb2Vec2 upper = b2Add( camera->center, extents );\n\n\tfloat u = ( worldPoint.x - lower.x ) / ( upper.x - lower.x );\n\tfloat v = ( worldPoint.y - lower.y ) / ( upper.y - lower.y );\n\n\tb2Vec2 ps = { u * w, ( 1.0f - v ) * h };\n\treturn ps;\n}\n\n// Convert from world coordinates to normalized device coordinates.\n// http://www.songho.ca/opengl/gl_projectionmatrix.html\n// This also includes the view transform\nstatic void BuildProjectionMatrix( Camera* camera, float* m, float zBias )\n{\n\tfloat ratio = camera->width / camera->height;\n\tb2Vec2 extents = { camera->zoom * ratio, camera->zoom };\n\n\tb2Vec2 lower = b2Sub( camera->center, extents );\n\tb2Vec2 upper = b2Add( camera->center, extents );\n\tfloat w = upper.x - lower.x;\n\tfloat h = upper.y - lower.y;\n\n\tm[0] = 2.0f / w;\n\tm[1] = 0.0f;\n\tm[2] = 0.0f;\n\tm[3] = 0.0f;\n\n\tm[4] = 0.0f;\n\tm[5] = 2.0f / h;\n\tm[6] = 0.0f;\n\tm[7] = 0.0f;\n\n\tm[8] = 0.0f;\n\tm[9] = 0.0f;\n\tm[10] = -1.0f;\n\tm[11] = 0.0f;\n\n\tm[12] = -2.0f * camera->center.x / w;\n\tm[13] = -2.0f * camera->center.y / h;\n\tm[14] = zBias;\n\tm[15] = 1.0f;\n}\n\nstatic void MakeOrthographicMatrix( float* m, float left, float right, float bottom, float top, float near, float far )\n{\n\tm[0] = 2.0f / ( right - left );\n\tm[1] = 0.0f;\n\tm[2] = 0.0f;\n\tm[3] = 0.0f;\n\n\tm[4] = 0.0f;\n\tm[5] = 2.0f / ( top - bottom );\n\tm[6] = 0.0f;\n\tm[7] = 0.0f;\n\n\tm[8] = 0.0f;\n\tm[9] = 0.0f;\n\tm[10] = -2.0f / ( far - near );\n\tm[11] = 0.0f;\n\n\tm[12] = -( right + left ) / ( right - left );\n\tm[13] = -( top + bottom ) / ( top - bottom );\n\tm[14] = -( far + near ) / ( far - near );\n\tm[15] = 1.0f;\n}\n\nb2AABB GetViewBounds( Camera* camera )\n{\n\tif ( camera->height == 0.0f || camera->width == 0.0f )\n\t{\n\t\tb2AABB bounds = { .lowerBound = b2Vec2_zero, .upperBound = b2Vec2_zero };\n\t\treturn bounds;\n\t}\n\n\tb2AABB bounds;\n\tbounds.lowerBound = ConvertScreenToWorld( camera, (b2Vec2){ 0.0f, camera->height } );\n\tbounds.upperBound = ConvertScreenToWorld( camera, (b2Vec2){ camera->width, 0.0f } );\n\treturn bounds;\n}\n\ntypedef struct\n{\n\tb2Vec2 position;\n\tb2Vec2 uv;\n\tRGBA8 color;\n} FontVertex;\n\nARRAY_DECLARE( FontVertex );\nARRAY_INLINE( FontVertex );\nARRAY_SOURCE( FontVertex );\n\n#define FONT_FIRST_CHARACTER 32\n#define FONT_CHARACTER_COUNT 96\n#define FONT_ATLAS_WIDTH 512\n#define FONT_ATLAS_HEIGHT 512\n\n// The number of vertices the vbo can hold. Must be a multiple of 6.\n#define FONT_BATCH_SIZE ( 6 * 10000 )\n\ntypedef struct\n{\n\tfloat fontSize;\n\tFontVertexArray vertices;\n\tstbtt_bakedchar* characters;\n\tunsigned int textureId;\n\tuint32_t vaoId;\n\tuint32_t vboId;\n\tuint32_t programId;\n} Font;\n\nFont CreateFont( const char* trueTypeFile, float fontSize )\n{\n\tFont font = { 0 };\n\n\tFILE* file = fopen( trueTypeFile, \"rb\" );\n\tif ( file == NULL )\n\t{\n\t\tassert( false );\n\t\treturn font;\n\t}\n\n\tfont.vertices = FontVertexArray_Create( FONT_BATCH_SIZE );\n\tfont.fontSize = fontSize;\n\tfont.characters = malloc( FONT_CHARACTER_COUNT * sizeof( stbtt_bakedchar ) );\n\n\tint fileBufferCapacity = 1 << 20;\n\tunsigned char* fileBuffer = (unsigned char*)malloc( fileBufferCapacity * sizeof( unsigned char ) );\n\tfread( fileBuffer, 1, fileBufferCapacity, file );\n\n\tint pw = FONT_ATLAS_WIDTH;\n\tint ph = FONT_ATLAS_HEIGHT;\n\tunsigned char* tempBitmap = (unsigned char*)malloc( pw * ph * sizeof( unsigned char ) );\n\tstbtt_BakeFontBitmap( fileBuffer, 0, font.fontSize, tempBitmap, pw, ph, FONT_FIRST_CHARACTER, FONT_CHARACTER_COUNT,\n\t\t\t\t\t\t  font.characters );\n\n\tglGenTextures( 1, &font.textureId );\n\tglBindTexture( GL_TEXTURE_2D, font.textureId );\n\tglTexImage2D( GL_TEXTURE_2D, 0, GL_R8, pw, ph, 0, GL_RED, GL_UNSIGNED_BYTE, tempBitmap );\n\tglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );\n\n\t// for debugging\n\t// stbi_write_png( \"build/fontAtlas.png\", pw, ph, 1, tempBitmap, pw );\n\n\tfclose( file );\n\tfree( fileBuffer );\n\tfree( tempBitmap );\n\tfileBuffer = NULL;\n\ttempBitmap = NULL;\n\n\tfont.programId = CreateProgramFromFiles( \"samples/data/font.vs\", \"samples/data/font.fs\" );\n\tif ( font.programId == 0 )\n\t{\n\t\treturn font;\n\t}\n\n\t// Setting up the VAO and VBO\n\tglGenBuffers( 1, &font.vboId );\n\tglBindBuffer( GL_ARRAY_BUFFER, font.vboId );\n\tglBufferData( GL_ARRAY_BUFFER, FONT_BATCH_SIZE * sizeof( FontVertex ), NULL, GL_DYNAMIC_DRAW );\n\n\tglGenVertexArrays( 1, &font.vaoId );\n\tglBindVertexArray( font.vaoId );\n\n\t// position attribute\n\tglVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, sizeof( FontVertex ), (void*)offsetof( FontVertex, position ) );\n\tglEnableVertexAttribArray( 0 );\n\n\t// uv attribute\n\tglVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, sizeof( FontVertex ), (void*)offsetof( FontVertex, uv ) );\n\tglEnableVertexAttribArray( 1 );\n\n\t// color attribute will be expanded to floats using normalization\n\tglVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof( FontVertex ), (void*)offsetof( FontVertex, color ) );\n\tglEnableVertexAttribArray( 2 );\n\n\tglBindVertexArray( 0 );\n\n\tCheckOpenGL();\n\n\treturn font;\n}\n\nvoid DestroyFont( Font* font )\n{\n\tif ( font->programId != 0 )\n\t{\n\t\tglDeleteProgram( font->programId );\n\t}\n\n\tglDeleteBuffers( 1, &font->vboId );\n\tglDeleteVertexArrays( 1, &font->vaoId );\n\n\tif ( font->textureId != 0 )\n\t{\n\t\tglDeleteTextures( 1, &font->textureId );\n\t}\n\n\tfree( font->characters );\n\n\tFontVertexArray_Destroy( &font->vertices );\n}\n\nvoid AddText( Font* font, float x, float y, b2HexColor color, const char* text )\n{\n\tif ( text == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Vec2 position = { x, y };\n\tRGBA8 c = MakeRGBA8( color, 1.0f );\n\tint pw = FONT_ATLAS_WIDTH;\n\tint ph = FONT_ATLAS_HEIGHT;\n\n\tint i = 0;\n\twhile ( text[i] != 0 )\n\t{\n\t\tint index = (int)text[i] - FONT_FIRST_CHARACTER;\n\n\t\tif ( 0 <= index && index < FONT_CHARACTER_COUNT )\n\t\t{\n\t\t\t// 1=opengl\n\t\t\tstbtt_aligned_quad q;\n\t\t\tstbtt_GetBakedQuad( font->characters, pw, ph, index, &position.x, &position.y, &q, 1 );\n\n\t\t\tFontVertex v1 = { { q.x0, q.y0 }, { q.s0, q.t0 }, c };\n\t\t\tFontVertex v2 = { { q.x1, q.y0 }, { q.s1, q.t0 }, c };\n\t\t\tFontVertex v3 = { { q.x1, q.y1 }, { q.s1, q.t1 }, c };\n\t\t\tFontVertex v4 = { { q.x0, q.y1 }, { q.s0, q.t1 }, c };\n\n\t\t\tFontVertexArray_Push( &font->vertices, v1 );\n\t\t\tFontVertexArray_Push( &font->vertices, v3 );\n\t\t\tFontVertexArray_Push( &font->vertices, v2 );\n\t\t\tFontVertexArray_Push( &font->vertices, v1 );\n\t\t\tFontVertexArray_Push( &font->vertices, v4 );\n\t\t\tFontVertexArray_Push( &font->vertices, v3 );\n\t\t}\n\n\t\ti += 1;\n\t}\n}\n\nvoid FlushText( Font* font, Camera* camera )\n{\n\tfloat projectionMatrix[16];\n\tMakeOrthographicMatrix( projectionMatrix, 0.0f, camera->width, camera->height, 0.0f, -1.0f, 1.0f );\n\n\tglUseProgram( font->programId );\n\n\tglEnable( GL_BLEND );\n\tglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );\n\n\tint slot = 0;\n\tglActiveTexture( GL_TEXTURE0 + slot );\n\tglBindTexture( GL_TEXTURE_2D, font->textureId );\n\n\tglBindVertexArray( font->vaoId );\n\tglBindBuffer( GL_ARRAY_BUFFER, font->vboId );\n\n\tint textureUniform = glGetUniformLocation( font->programId, \"FontAtlas\" );\n\tglUniform1i( textureUniform, slot );\n\n\tint matrixUniform = glGetUniformLocation( font->programId, \"ProjectionMatrix\" );\n\tglUniformMatrix4fv( matrixUniform, 1, GL_FALSE, projectionMatrix );\n\n\tint totalVertexCount = font->vertices.count;\n\tint drawCallCount = ( totalVertexCount / FONT_BATCH_SIZE ) + 1;\n\n\tfor ( int i = 0; i < drawCallCount; i++ )\n\t{\n\t\tconst FontVertex* data = font->vertices.data + i * FONT_BATCH_SIZE;\n\n\t\tint vertexCount;\n\t\tif ( i == drawCallCount - 1 )\n\t\t{\n\t\t\tvertexCount = totalVertexCount % FONT_BATCH_SIZE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvertexCount = FONT_BATCH_SIZE;\n\t\t}\n\n\t\tglBufferSubData( GL_ARRAY_BUFFER, 0, vertexCount * sizeof( FontVertex ), data );\n\t\tglDrawArrays( GL_TRIANGLES, 0, vertexCount );\n\t}\n\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\tglBindTexture( GL_TEXTURE_2D, 0 );\n\n\tglDisable( GL_BLEND );\n\n\tCheckOpenGL();\n\n\tfont->vertices.count = 0;\n}\n\ntypedef struct\n{\n\tGLuint vaoId;\n\tGLuint vboId;\n\tGLuint programId;\n\tGLint timeUniform;\n\tGLint resolutionUniform;\n\tGLint baseColorUniform;\n} Background;\n\nBackground CreateBackground()\n{\n\tBackground background = { 0 };\n\n\tbackground.programId = CreateProgramFromFiles( \"samples/data/background.vs\", \"samples/data/background.fs\" );\n\tbackground.timeUniform = glGetUniformLocation( background.programId, \"time\" );\n\tbackground.resolutionUniform = glGetUniformLocation( background.programId, \"resolution\" );\n\tbackground.baseColorUniform = glGetUniformLocation( background.programId, \"baseColor\" );\n\tint vertexAttribute = 0;\n\n\t// Generate\n\tglGenVertexArrays( 1, &background.vaoId );\n\tglGenBuffers( 1, &background.vboId );\n\n\tglBindVertexArray( background.vaoId );\n\tglEnableVertexAttribArray( vertexAttribute );\n\n\t// Single quad\n\tb2Vec2 vertices[] = { { -1.0f, 1.0f }, { -1.0f, -1.0f }, { 1.0f, 1.0f }, { 1.0f, -1.0f } };\n\tglBindBuffer( GL_ARRAY_BUFFER, background.vboId );\n\tglBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW );\n\tglVertexAttribPointer( vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET( 0 ) );\n\n\tCheckOpenGL();\n\n\t// Cleanup\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\n\treturn background;\n}\n\nvoid DestroyBackground( Background* background )\n{\n\tif ( background->vaoId )\n\t{\n\t\tglDeleteVertexArrays( 1, &background->vaoId );\n\t\tglDeleteBuffers( 1, &background->vboId );\n\t\tbackground->vaoId = 0;\n\t\tbackground->vboId = 0;\n\t}\n\n\tif ( background->programId )\n\t{\n\t\tglDeleteProgram( background->programId );\n\t\tbackground->programId = 0;\n\t}\n}\n\nvoid RenderBackground( Background* background, Camera* camera )\n{\n\tglUseProgram( background->programId );\n\n\tfloat time = (float)glfwGetTime();\n\ttime = fmodf( time, 100.0f );\n\n\tglUniform1f( background->timeUniform, time );\n\tglUniform2f( background->resolutionUniform, (float)camera->width, (float)camera->height );\n\n\t// struct RGBA8 c8 = MakeRGBA8( b2_colorGray2, 1.0f );\n\t// glUniform3f(baseColorUniform, c8.r/255.0f, c8.g/255.0f, c8.b/255.0f);\n\tglUniform3f( background->baseColorUniform, 0.2f, 0.2f, 0.2f );\n\n\tglBindVertexArray( background->vaoId );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, background->vboId );\n\tglDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\tglUseProgram( 0 );\n}\n\n#define POINT_BATCH_SIZE 2048\n\ntypedef struct\n{\n\tb2Vec2 position;\n\tfloat size;\n\tRGBA8 rgba;\n} PointData;\n\nARRAY_DECLARE( PointData );\nARRAY_INLINE( PointData );\nARRAY_SOURCE( PointData );\n\ntypedef struct\n{\n\tPointDataArray points;\n\tGLuint vaoId;\n\tGLuint vboId;\n\tGLuint programId;\n\tGLint projectionUniform;\n} PointRender;\n\nPointRender CreatePointDrawData()\n{\n\tPointRender render = { 0 };\n\trender.points = PointDataArray_Create( POINT_BATCH_SIZE );\n\trender.programId = CreateProgramFromFiles( \"samples/data/point.vs\", \"samples/data/point.fs\" );\n\trender.projectionUniform = glGetUniformLocation( render.programId, \"projectionMatrix\" );\n\tint vertexAttribute = 0;\n\tint sizeAttribute = 1;\n\tint colorAttribute = 2;\n\n\t// Generate\n\tglGenVertexArrays( 1, &render.vaoId );\n\tglGenBuffers( 1, &render.vboId );\n\n\tglBindVertexArray( render.vaoId );\n\tglEnableVertexAttribArray( vertexAttribute );\n\tglEnableVertexAttribArray( sizeAttribute );\n\tglEnableVertexAttribArray( colorAttribute );\n\n\t// Vertex buffer\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboId );\n\tglBufferData( GL_ARRAY_BUFFER, POINT_BATCH_SIZE * sizeof( PointData ), NULL, GL_DYNAMIC_DRAW );\n\n\tglVertexAttribPointer( vertexAttribute, 2, GL_FLOAT, GL_FALSE, sizeof( PointData ), (void*)offsetof( PointData, position ) );\n\tglVertexAttribPointer( sizeAttribute, 1, GL_FLOAT, GL_FALSE, sizeof( PointData ), (void*)offsetof( PointData, size ) );\n\t// save bandwidth by expanding color to floats in the shader\n\tglVertexAttribPointer( colorAttribute, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof( PointData ),\n\t\t\t\t\t\t   (void*)offsetof( PointData, rgba ) );\n\n\tCheckOpenGL();\n\n\t// Cleanup\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\n\treturn render;\n}\n\nvoid DestroyPointDrawData( PointRender* render )\n{\n\tif ( render->vaoId )\n\t{\n\t\tglDeleteVertexArrays( 1, &render->vaoId );\n\t\tglDeleteBuffers( 1, &render->vboId );\n\t}\n\n\tif ( render->programId )\n\t{\n\t\tglDeleteProgram( render->programId );\n\t}\n\n\tPointDataArray_Destroy( &render->points );\n\n\t*render = (PointRender){ 0 };\n}\n\nvoid AddPoint( PointRender* render, b2Vec2 v, float size, b2HexColor c )\n{\n\tRGBA8 rgba = MakeRGBA8( c, 1.0f );\n\tPointDataArray_Push( &render->points, (PointData){ v, size, rgba } );\n}\n\nvoid FlushPoints( PointRender* render, Camera* camera )\n{\n\tint count = render->points.count;\n\tif ( count == 0 )\n\t{\n\t\treturn;\n\t}\n\n\tglUseProgram( render->programId );\n\n\tfloat proj[16] = { 0.0f };\n\tBuildProjectionMatrix( camera, proj, 0.0f );\n\n\tglUniformMatrix4fv( render->projectionUniform, 1, GL_FALSE, proj );\n\tglBindVertexArray( render->vaoId );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, render->vboId );\n\tglEnable( GL_PROGRAM_POINT_SIZE );\n\n\tint base = 0;\n\twhile ( count > 0 )\n\t{\n\t\tint batchCount = b2MinInt( count, POINT_BATCH_SIZE );\n\t\tglBufferSubData( GL_ARRAY_BUFFER, 0, batchCount * sizeof( PointData ), render->points.data + base );\n\t\tglDrawArrays( GL_POINTS, 0, batchCount );\n\n\t\tCheckOpenGL();\n\n\t\tcount -= POINT_BATCH_SIZE;\n\t\tbase += POINT_BATCH_SIZE;\n\t}\n\n\tglDisable( GL_PROGRAM_POINT_SIZE );\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\tglUseProgram( 0 );\n\n\trender->points.count = 0;\n}\n\n#define LINE_BATCH_SIZE ( 2 * 2048 )\n\ntypedef struct\n{\n\tb2Vec2 position;\n\tRGBA8 rgba;\n} VertexData;\n\nARRAY_DECLARE( VertexData );\nARRAY_INLINE( VertexData );\nARRAY_SOURCE( VertexData );\n\ntypedef struct\n{\n\tVertexDataArray points;\n\tGLuint vaoId;\n\tGLuint vboId;\n\tGLuint programId;\n\tGLint projectionUniform;\n} LineRender;\n\nLineRender CreateLineRender()\n{\n\tLineRender render = { 0 };\n\trender.points = VertexDataArray_Create( LINE_BATCH_SIZE );\n\trender.programId = CreateProgramFromFiles( \"samples/data/line.vs\", \"samples/data/line.fs\" );\n\trender.projectionUniform = glGetUniformLocation( render.programId, \"projectionMatrix\" );\n\tint vertexAttribute = 0;\n\tint colorAttribute = 1;\n\n\t// Generate\n\tglGenVertexArrays( 1, &render.vaoId );\n\tglGenBuffers( 1, &render.vboId );\n\n\tglBindVertexArray( render.vaoId );\n\tglEnableVertexAttribArray( vertexAttribute );\n\tglEnableVertexAttribArray( colorAttribute );\n\n\t// Vertex buffer\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboId );\n\tglBufferData( GL_ARRAY_BUFFER, LINE_BATCH_SIZE * sizeof( VertexData ), NULL, GL_DYNAMIC_DRAW );\n\n\tglVertexAttribPointer( vertexAttribute, 2, GL_FLOAT, GL_FALSE, sizeof( VertexData ),\n\t\t\t\t\t\t   (void*)offsetof( VertexData, position ) );\n\t// save bandwidth by expanding color to floats in the shader\n\tglVertexAttribPointer( colorAttribute, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof( VertexData ),\n\t\t\t\t\t\t   (void*)offsetof( VertexData, rgba ) );\n\n\tCheckOpenGL();\n\n\t// Cleanup\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\n\treturn render;\n}\n\nvoid DestroyLineRender( LineRender* render )\n{\n\tif ( render->vaoId )\n\t{\n\t\tglDeleteVertexArrays( 1, &render->vaoId );\n\t\tglDeleteBuffers( 1, &render->vboId );\n\t}\n\n\tif ( render->programId )\n\t{\n\t\tglDeleteProgram( render->programId );\n\t}\n\n\tVertexDataArray_Destroy( &render->points );\n\n\t*render = (LineRender){ 0 };\n}\n\nvoid AddLine( LineRender* render, b2Vec2 p1, b2Vec2 p2, b2HexColor c )\n{\n\tRGBA8 rgba = MakeRGBA8( c, 1.0f );\n\tVertexDataArray_Push( &render->points, (VertexData){ p1, rgba } );\n\tVertexDataArray_Push( &render->points, (VertexData){ p2, rgba } );\n}\n\nvoid FlushLines( LineRender* render, Camera* camera )\n{\n\tint count = render->points.count;\n\tif ( count == 0 )\n\t{\n\t\treturn;\n\t}\n\n\tassert( count % 2 == 0 );\n\n\tglEnable( GL_BLEND );\n\tglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );\n\n\tglUseProgram( render->programId );\n\n\tfloat proj[16] = { 0 };\n\tBuildProjectionMatrix( camera, proj, 0.1f );\n\n\tglUniformMatrix4fv( render->projectionUniform, 1, GL_FALSE, proj );\n\n\tglBindVertexArray( render->vaoId );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, render->vboId );\n\n\tint base = 0;\n\twhile ( count > 0 )\n\t{\n\t\tint batchCount = b2MinInt( count, LINE_BATCH_SIZE );\n\t\tglBufferSubData( GL_ARRAY_BUFFER, 0, batchCount * sizeof( VertexData ), render->points.data + base );\n\n\t\tglDrawArrays( GL_LINES, 0, batchCount );\n\n\t\tCheckOpenGL();\n\n\t\tcount -= LINE_BATCH_SIZE;\n\t\tbase += LINE_BATCH_SIZE;\n\t}\n\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\tglUseProgram( 0 );\n\n\tglDisable( GL_BLEND );\n\n\trender->points.count = 0;\n}\n\n#define CIRCLE_BATCH_SIZE 2048\n\ntypedef struct\n{\n\tb2Vec2 position;\n\tfloat radius;\n\tRGBA8 rgba;\n} CircleData;\n\nARRAY_DECLARE( CircleData );\nARRAY_INLINE( CircleData );\nARRAY_SOURCE( CircleData );\n\ntypedef struct\n{\n\tCircleDataArray circles;\n\tGLuint vaoId;\n\tGLuint vboIds[2];\n\tGLuint programId;\n\tGLint projectionUniform;\n\tGLint pixelScaleUniform;\n} CircleRender;\n\nCircleRender CreateCircles()\n{\n\tCircleRender render = { 0 };\n\trender.circles = CircleDataArray_Create( CIRCLE_BATCH_SIZE );\n\trender.programId = CreateProgramFromFiles( \"samples/data/circle.vs\", \"samples/data/circle.fs\" );\n\trender.projectionUniform = glGetUniformLocation( render.programId, \"projectionMatrix\" );\n\trender.pixelScaleUniform = glGetUniformLocation( render.programId, \"pixelScale\" );\n\tint vertexAttribute = 0;\n\tint positionInstance = 1;\n\tint radiusInstance = 2;\n\tint colorInstance = 3;\n\n\t// Generate\n\tglGenVertexArrays( 1, &render.vaoId );\n\tglGenBuffers( 2, render.vboIds );\n\n\tglBindVertexArray( render.vaoId );\n\tglEnableVertexAttribArray( vertexAttribute );\n\tglEnableVertexAttribArray( positionInstance );\n\tglEnableVertexAttribArray( radiusInstance );\n\tglEnableVertexAttribArray( colorInstance );\n\n\t// Vertex buffer for single quad\n\tfloat a = 1.1f;\n\tb2Vec2 vertices[] = { { -a, -a }, { a, -a }, { -a, a }, { a, -a }, { a, a }, { -a, a } };\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboIds[0] );\n\tglBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW );\n\tglVertexAttribPointer( vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET( 0 ) );\n\n\t// Circle buffer\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboIds[1] );\n\tglBufferData( GL_ARRAY_BUFFER, CIRCLE_BATCH_SIZE * sizeof( CircleData ), NULL, GL_DYNAMIC_DRAW );\n\n\tglVertexAttribPointer( positionInstance, 2, GL_FLOAT, GL_FALSE, sizeof( CircleData ),\n\t\t\t\t\t\t   (void*)offsetof( CircleData, position ) );\n\tglVertexAttribPointer( radiusInstance, 1, GL_FLOAT, GL_FALSE, sizeof( CircleData ), (void*)offsetof( CircleData, radius ) );\n\tglVertexAttribPointer( colorInstance, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof( CircleData ),\n\t\t\t\t\t\t   (void*)offsetof( CircleData, rgba ) );\n\n\tglVertexAttribDivisor( positionInstance, 1 );\n\tglVertexAttribDivisor( radiusInstance, 1 );\n\tglVertexAttribDivisor( colorInstance, 1 );\n\n\tCheckOpenGL();\n\n\t// Cleanup\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\n\treturn render;\n}\n\nvoid DestroyCircles( CircleRender* render )\n{\n\tif ( render->vaoId )\n\t{\n\t\tglDeleteVertexArrays( 1, &render->vaoId );\n\t\tglDeleteBuffers( 2, render->vboIds );\n\t}\n\n\tif ( render->programId )\n\t{\n\t\tglDeleteProgram( render->programId );\n\t}\n\n\tCircleDataArray_Destroy( &render->circles );\n\n\t*render = (CircleRender){ 0 };\n}\n\nvoid AddCircle( CircleRender* render, b2Vec2 center, float radius, b2HexColor color )\n{\n\tRGBA8 rgba = MakeRGBA8( color, 1.0f );\n\tCircleDataArray_Push( &render->circles, (CircleData){ center, radius, rgba } );\n}\n\nvoid FlushCircles( CircleRender* render, Camera* camera )\n{\n\tint count = render->circles.count;\n\tif ( count == 0 )\n\t{\n\t\treturn;\n\t}\n\n\tglUseProgram( render->programId );\n\n\tfloat proj[16] = { 0.0f };\n\tBuildProjectionMatrix( camera, proj, 0.2f );\n\n\tglUniformMatrix4fv( render->projectionUniform, 1, GL_FALSE, proj );\n\tglUniform1f( render->pixelScaleUniform, camera->height / camera->zoom );\n\n\tglBindVertexArray( render->vaoId );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, render->vboIds[1] );\n\tglEnable( GL_BLEND );\n\tglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );\n\n\tint base = 0;\n\twhile ( count > 0 )\n\t{\n\t\tint batchCount = b2MinInt( count, CIRCLE_BATCH_SIZE );\n\n\t\tglBufferSubData( GL_ARRAY_BUFFER, 0, batchCount * sizeof( CircleData ), render->circles.data + base );\n\t\tglDrawArraysInstanced( GL_TRIANGLES, 0, 6, batchCount );\n\n\t\tCheckOpenGL();\n\n\t\tcount -= CIRCLE_BATCH_SIZE;\n\t\tbase += CIRCLE_BATCH_SIZE;\n\t}\n\n\tglDisable( GL_BLEND );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\tglUseProgram( 0 );\n\n\trender->circles.count = 0;\n}\n\ntypedef struct\n{\n\tb2Transform transform;\n\tfloat radius;\n\tRGBA8 rgba;\n} SolidCircle;\n\nARRAY_DECLARE( SolidCircle );\nARRAY_INLINE( SolidCircle );\nARRAY_SOURCE( SolidCircle );\n\n#define SOLID_CIRCLE_BATCH_SIZE 2048\n\n// Draws SDF circles using quad instancing. Apparently instancing of quads can be slow on older GPUs.\n// https://www.reddit.com/r/opengl/comments/q7yikr/how_to_draw_several_quads_through_instancing/\n// https://www.g-truc.net/post-0666.html\ntypedef struct\n{\n\tSolidCircleArray circles;\n\tGLuint vaoId;\n\tGLuint vboIds[2];\n\tGLuint programId;\n\tGLint projectionUniform;\n\tGLint pixelScaleUniform;\n} SolidCircles;\n\nSolidCircles CreateSolidCircles()\n{\n\tSolidCircles render = { 0 };\n\trender.circles = SolidCircleArray_Create( SOLID_CIRCLE_BATCH_SIZE );\n\trender.programId = CreateProgramFromFiles( \"samples/data/solid_circle.vs\", \"samples/data/solid_circle.fs\" );\n\trender.projectionUniform = glGetUniformLocation( render.programId, \"projectionMatrix\" );\n\trender.pixelScaleUniform = glGetUniformLocation( render.programId, \"pixelScale\" );\n\n\t// Generate\n\tglGenVertexArrays( 1, &render.vaoId );\n\tglGenBuffers( 2, render.vboIds );\n\n\tglBindVertexArray( render.vaoId );\n\n\tint vertexAttribute = 0;\n\tint transformInstance = 1;\n\tint radiusInstance = 2;\n\tint colorInstance = 3;\n\tglEnableVertexAttribArray( vertexAttribute );\n\tglEnableVertexAttribArray( transformInstance );\n\tglEnableVertexAttribArray( radiusInstance );\n\tglEnableVertexAttribArray( colorInstance );\n\n\t// Vertex buffer for single quad\n\tfloat a = 1.1f;\n\tb2Vec2 vertices[] = { { -a, -a }, { a, -a }, { -a, a }, { a, -a }, { a, a }, { -a, a } };\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboIds[0] );\n\tglBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW );\n\tglVertexAttribPointer( vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET( 0 ) );\n\n\t// Circle buffer\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboIds[1] );\n\tglBufferData( GL_ARRAY_BUFFER, SOLID_CIRCLE_BATCH_SIZE * sizeof( SolidCircle ), NULL, GL_DYNAMIC_DRAW );\n\n\tglVertexAttribPointer( transformInstance, 4, GL_FLOAT, GL_FALSE, sizeof( SolidCircle ),\n\t\t\t\t\t\t   (void*)offsetof( SolidCircle, transform ) );\n\tglVertexAttribPointer( radiusInstance, 1, GL_FLOAT, GL_FALSE, sizeof( SolidCircle ), (void*)offsetof( SolidCircle, radius ) );\n\tglVertexAttribPointer( colorInstance, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof( SolidCircle ),\n\t\t\t\t\t\t   (void*)offsetof( SolidCircle, rgba ) );\n\n\tglVertexAttribDivisor( transformInstance, 1 );\n\tglVertexAttribDivisor( radiusInstance, 1 );\n\tglVertexAttribDivisor( colorInstance, 1 );\n\n\tCheckOpenGL();\n\n\t// Cleanup\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\n\treturn render;\n}\n\nvoid DestroySolidCircles( SolidCircles* render )\n{\n\tif ( render->vaoId )\n\t{\n\t\tglDeleteVertexArrays( 1, &render->vaoId );\n\t\tglDeleteBuffers( 2, render->vboIds );\n\t}\n\n\tif ( render->programId )\n\t{\n\t\tglDeleteProgram( render->programId );\n\t}\n\n\tSolidCircleArray_Destroy( &render->circles );\n\n\t*render = (SolidCircles){ 0 };\n}\n\nvoid AddSolidCircle( SolidCircles* render, b2Transform transform, float radius, b2HexColor color )\n{\n\tRGBA8 rgba = MakeRGBA8( color, 1.0f );\n\tSolidCircleArray_Push( &render->circles, (SolidCircle){ transform, radius, rgba } );\n}\n\nvoid FlushSolidCircles( SolidCircles* render, Camera* camera )\n{\n\tint count = render->circles.count;\n\tif ( count == 0 )\n\t{\n\t\treturn;\n\t}\n\n\tglUseProgram( render->programId );\n\n\tfloat proj[16] = { 0.0f };\n\tBuildProjectionMatrix( camera, proj, 0.2f );\n\n\tglUniformMatrix4fv( render->projectionUniform, 1, GL_FALSE, proj );\n\tglUniform1f( render->pixelScaleUniform, camera->height / camera->zoom );\n\n\tglBindVertexArray( render->vaoId );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, render->vboIds[1] );\n\tglEnable( GL_BLEND );\n\tglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );\n\n\tint base = 0;\n\twhile ( count > 0 )\n\t{\n\t\tint batchCount = b2MinInt( count, SOLID_CIRCLE_BATCH_SIZE );\n\n\t\tglBufferSubData( GL_ARRAY_BUFFER, 0, batchCount * sizeof( SolidCircle ), render->circles.data + base );\n\t\tglDrawArraysInstanced( GL_TRIANGLES, 0, 6, batchCount );\n\n\t\tCheckOpenGL();\n\n\t\tcount -= SOLID_CIRCLE_BATCH_SIZE;\n\t\tbase += SOLID_CIRCLE_BATCH_SIZE;\n\t}\n\n\tglDisable( GL_BLEND );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\tglUseProgram( 0 );\n\n\trender->circles.count = 0;\n}\n\ntypedef struct\n{\n\tb2Transform transform;\n\tfloat radius;\n\tfloat length;\n\tRGBA8 rgba;\n} Capsule;\n\nARRAY_DECLARE( Capsule );\nARRAY_INLINE( Capsule );\nARRAY_SOURCE( Capsule );\n\n#define CAPSULE_BATCH_SIZE 2048\n\n// Draw capsules using SDF-based shader\ntypedef struct\n{\n\tCapsuleArray capsules;\n\tGLuint vaoId;\n\tGLuint vboIds[2];\n\tGLuint programId;\n\tGLint projectionUniform;\n\tGLint pixelScaleUniform;\n} Capsules;\n\nCapsules CreateCapsules()\n{\n\tCapsules render = { 0 };\n\trender.capsules = CapsuleArray_Create( CAPSULE_BATCH_SIZE );\n\trender.programId = CreateProgramFromFiles( \"samples/data/solid_capsule.vs\", \"samples/data/solid_capsule.fs\" );\n\trender.projectionUniform = glGetUniformLocation( render.programId, \"projectionMatrix\" );\n\trender.pixelScaleUniform = glGetUniformLocation( render.programId, \"pixelScale\" );\n\n\tint vertexAttribute = 0;\n\tint transformInstance = 1;\n\tint radiusInstance = 2;\n\tint lengthInstance = 3;\n\tint colorInstance = 4;\n\n\t// Generate\n\tglGenVertexArrays( 1, &render.vaoId );\n\tglGenBuffers( 2, render.vboIds );\n\n\tglBindVertexArray( render.vaoId );\n\tglEnableVertexAttribArray( vertexAttribute );\n\tglEnableVertexAttribArray( transformInstance );\n\tglEnableVertexAttribArray( radiusInstance );\n\tglEnableVertexAttribArray( lengthInstance );\n\tglEnableVertexAttribArray( colorInstance );\n\n\t// Vertex buffer for single quad\n\tfloat a = 1.1f;\n\tb2Vec2 vertices[] = { { -a, -a }, { a, -a }, { -a, a }, { a, -a }, { a, a }, { -a, a } };\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboIds[0] );\n\tglBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW );\n\tglVertexAttribPointer( vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET( 0 ) );\n\n\t// Capsule buffer\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboIds[1] );\n\tglBufferData( GL_ARRAY_BUFFER, CAPSULE_BATCH_SIZE * sizeof( Capsule ), NULL, GL_DYNAMIC_DRAW );\n\n\tglVertexAttribPointer( transformInstance, 4, GL_FLOAT, GL_FALSE, sizeof( Capsule ), (void*)offsetof( Capsule, transform ) );\n\tglVertexAttribPointer( radiusInstance, 1, GL_FLOAT, GL_FALSE, sizeof( Capsule ), (void*)offsetof( Capsule, radius ) );\n\tglVertexAttribPointer( lengthInstance, 1, GL_FLOAT, GL_FALSE, sizeof( Capsule ), (void*)offsetof( Capsule, length ) );\n\tglVertexAttribPointer( colorInstance, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof( Capsule ), (void*)offsetof( Capsule, rgba ) );\n\n\tglVertexAttribDivisor( transformInstance, 1 );\n\tglVertexAttribDivisor( radiusInstance, 1 );\n\tglVertexAttribDivisor( lengthInstance, 1 );\n\tglVertexAttribDivisor( colorInstance, 1 );\n\n\tCheckOpenGL();\n\n\t// Cleanup\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\n\treturn render;\n}\n\nvoid DestroyCapsules( Capsules* render )\n{\n\tif ( render->vaoId )\n\t{\n\t\tglDeleteVertexArrays( 1, &render->vaoId );\n\t\tglDeleteBuffers( 2, render->vboIds );\n\t}\n\n\tif ( render->programId )\n\t{\n\t\tglDeleteProgram( render->programId );\n\t}\n\n\tCapsuleArray_Destroy( &render->capsules );\n\n\t*render = (Capsules){ 0 };\n}\n\nvoid AddCapsule( Capsules* render, b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor c )\n{\n\tb2Vec2 d = b2Sub( p2, p1 );\n\tfloat length = b2Length( d );\n\tif ( length < 0.001f )\n\t{\n\t\tprintf( \"WARNING: sample app: capsule too short!\\n\" );\n\t\treturn;\n\t}\n\n\tb2Vec2 axis = { d.x / length, d.y / length };\n\tb2Transform transform;\n\ttransform.p = b2Lerp( p1, p2, 0.5f );\n\ttransform.q.c = axis.x;\n\ttransform.q.s = axis.y;\n\n\tRGBA8 rgba = MakeRGBA8( c, 1.0f );\n\n\tCapsuleArray_Push( &render->capsules, (Capsule){ transform, radius, length, rgba } );\n}\n\nvoid FlushCapsules( Capsules* render, Camera* camera )\n{\n\tint count = render->capsules.count;\n\tif ( count == 0 )\n\t{\n\t\treturn;\n\t}\n\n\tglUseProgram( render->programId );\n\n\tfloat proj[16] = { 0.0f };\n\tBuildProjectionMatrix( camera, proj, 0.2f );\n\n\tglUniformMatrix4fv( render->projectionUniform, 1, GL_FALSE, proj );\n\tglUniform1f( render->pixelScaleUniform, camera->height / camera->zoom );\n\n\tglBindVertexArray( render->vaoId );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, render->vboIds[1] );\n\tglEnable( GL_BLEND );\n\tglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );\n\n\tint base = 0;\n\twhile ( count > 0 )\n\t{\n\t\tint batchCount = b2MinInt( count, CAPSULE_BATCH_SIZE );\n\n\t\tglBufferSubData( GL_ARRAY_BUFFER, 0, batchCount * sizeof( Capsule ), render->capsules.data + base );\n\t\tglDrawArraysInstanced( GL_TRIANGLES, 0, 6, batchCount );\n\n\t\tCheckOpenGL();\n\n\t\tcount -= CAPSULE_BATCH_SIZE;\n\t\tbase += CAPSULE_BATCH_SIZE;\n\t}\n\n\tglDisable( GL_BLEND );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\tglUseProgram( 0 );\n\n\trender->capsules.count = 0;\n}\n\ntypedef struct\n{\n\tb2Transform transform;\n\tb2Vec2 p1, p2, p3, p4, p5, p6, p7, p8;\n\tint count;\n\tfloat radius;\n\n\t// keep color small\n\tRGBA8 color;\n} Polygon;\n\nARRAY_DECLARE( Polygon );\nARRAY_INLINE( Polygon );\nARRAY_SOURCE( Polygon );\n\n#define POLYGON_BATCH_SIZE 2048\n\n// Rounded and non-rounded convex polygons using an SDF-based shader.\ntypedef struct\n{\n\tPolygonArray polygons;\n\tGLuint vaoId;\n\tGLuint vboIds[2];\n\tGLuint programId;\n\tGLint projectionUniform;\n\tGLint pixelScaleUniform;\n} Polygons;\n\nPolygons CreatePolygons()\n{\n\tPolygons render = { 0 };\n\trender.polygons = PolygonArray_Create( POLYGON_BATCH_SIZE );\n\trender.programId = CreateProgramFromFiles( \"samples/data/solid_polygon.vs\", \"samples/data/solid_polygon.fs\" );\n\trender.projectionUniform = glGetUniformLocation( render.programId, \"projectionMatrix\" );\n\trender.pixelScaleUniform = glGetUniformLocation( render.programId, \"pixelScale\" );\n\n\tint vertexAttribute = 0;\n\tint instanceTransform = 1;\n\tint instancePoint12 = 2;\n\tint instancePoint34 = 3;\n\tint instancePoint56 = 4;\n\tint instancePoint78 = 5;\n\tint instancePointCount = 6;\n\tint instanceRadius = 7;\n\tint instanceColor = 8;\n\n\t// Generate\n\tglGenVertexArrays( 1, &render.vaoId );\n\tglGenBuffers( 2, render.vboIds );\n\n\tglBindVertexArray( render.vaoId );\n\tglEnableVertexAttribArray( vertexAttribute );\n\tglEnableVertexAttribArray( instanceTransform );\n\tglEnableVertexAttribArray( instancePoint12 );\n\tglEnableVertexAttribArray( instancePoint34 );\n\tglEnableVertexAttribArray( instancePoint56 );\n\tglEnableVertexAttribArray( instancePoint78 );\n\tglEnableVertexAttribArray( instancePointCount );\n\tglEnableVertexAttribArray( instanceRadius );\n\tglEnableVertexAttribArray( instanceColor );\n\n\t// Vertex buffer for single quad\n\tfloat a = 1.1f;\n\tb2Vec2 vertices[] = { { -a, -a }, { a, -a }, { -a, a }, { a, -a }, { a, a }, { -a, a } };\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboIds[0] );\n\tglBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW );\n\tglVertexAttribPointer( vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET( 0 ) );\n\n\t// Polygon buffer\n\tglBindBuffer( GL_ARRAY_BUFFER, render.vboIds[1] );\n\tglBufferData( GL_ARRAY_BUFFER, POLYGON_BATCH_SIZE * sizeof( Polygon ), NULL, GL_DYNAMIC_DRAW );\n\tglVertexAttribPointer( instanceTransform, 4, GL_FLOAT, GL_FALSE, sizeof( Polygon ), (void*)offsetof( Polygon, transform ) );\n\tglVertexAttribPointer( instancePoint12, 4, GL_FLOAT, GL_FALSE, sizeof( Polygon ), (void*)offsetof( Polygon, p1 ) );\n\tglVertexAttribPointer( instancePoint34, 4, GL_FLOAT, GL_FALSE, sizeof( Polygon ), (void*)offsetof( Polygon, p3 ) );\n\tglVertexAttribPointer( instancePoint56, 4, GL_FLOAT, GL_FALSE, sizeof( Polygon ), (void*)offsetof( Polygon, p5 ) );\n\tglVertexAttribPointer( instancePoint78, 4, GL_FLOAT, GL_FALSE, sizeof( Polygon ), (void*)offsetof( Polygon, p7 ) );\n\tglVertexAttribIPointer( instancePointCount, 1, GL_INT, sizeof( Polygon ), (void*)offsetof( Polygon, count ) );\n\tglVertexAttribPointer( instanceRadius, 1, GL_FLOAT, GL_FALSE, sizeof( Polygon ), (void*)offsetof( Polygon, radius ) );\n\t// color will get automatically expanded to floats in the shader\n\tglVertexAttribPointer( instanceColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof( Polygon ), (void*)offsetof( Polygon, color ) );\n\n\t// These divisors tell glsl how to distribute per instance data\n\tglVertexAttribDivisor( instanceTransform, 1 );\n\tglVertexAttribDivisor( instancePoint12, 1 );\n\tglVertexAttribDivisor( instancePoint34, 1 );\n\tglVertexAttribDivisor( instancePoint56, 1 );\n\tglVertexAttribDivisor( instancePoint78, 1 );\n\tglVertexAttribDivisor( instancePointCount, 1 );\n\tglVertexAttribDivisor( instanceRadius, 1 );\n\tglVertexAttribDivisor( instanceColor, 1 );\n\n\tCheckOpenGL();\n\n\t// Cleanup\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\n\treturn render;\n}\n\nvoid DestroyPolygons( Polygons* render )\n{\n\tif ( render->vaoId )\n\t{\n\t\tglDeleteVertexArrays( 1, &render->vaoId );\n\t\tglDeleteBuffers( 2, render->vboIds );\n\t}\n\n\tif ( render->programId )\n\t{\n\t\tglDeleteProgram( render->programId );\n\t}\n\n\tPolygonArray_Destroy( &render->polygons );\n\n\t*render = (Polygons){ 0 };\n}\n\nvoid AddPolygon( Polygons* render, b2Transform transform, const b2Vec2* points, int count, float radius, b2HexColor color )\n{\n\tPolygon data = {};\n\tdata.transform = transform;\n\n\tint n = count < 8 ? count : 8;\n\tb2Vec2* ps = &data.p1;\n\tfor ( int i = 0; i < n; ++i )\n\t{\n\t\tps[i] = points[i];\n\t}\n\n\tdata.count = n;\n\tdata.radius = radius;\n\tdata.color = MakeRGBA8( color, 1.0f );\n\n\tPolygonArray_Push( &render->polygons, data );\n}\n\nvoid FlushPolygons( Polygons* render, Camera* camera )\n{\n\tint count = render->polygons.count;\n\tif ( count == 0 )\n\t{\n\t\treturn;\n\t}\n\n\tglUseProgram( render->programId );\n\n\tfloat proj[16] = { 0.0f };\n\tBuildProjectionMatrix( camera, proj, 0.2f );\n\n\tglUniformMatrix4fv( render->projectionUniform, 1, GL_FALSE, proj );\n\tglUniform1f( render->pixelScaleUniform, camera->height / camera->zoom );\n\n\tglBindVertexArray( render->vaoId );\n\tglBindBuffer( GL_ARRAY_BUFFER, render->vboIds[1] );\n\n\tglEnable( GL_BLEND );\n\tglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );\n\n\tint base = 0;\n\twhile ( count > 0 )\n\t{\n\t\tint batchCount = b2MinInt( count, POLYGON_BATCH_SIZE );\n\n\t\tglBufferSubData( GL_ARRAY_BUFFER, 0, batchCount * sizeof( Polygon ), render->polygons.data + base );\n\t\tglDrawArraysInstanced( GL_TRIANGLES, 0, 6, batchCount );\n\t\tCheckOpenGL();\n\n\t\tcount -= POLYGON_BATCH_SIZE;\n\t\tbase += POLYGON_BATCH_SIZE;\n\t}\n\n\tglDisable( GL_BLEND );\n\n\tglBindBuffer( GL_ARRAY_BUFFER, 0 );\n\tglBindVertexArray( 0 );\n\tglUseProgram( 0 );\n\n\trender->polygons.count = 0;\n}\n\ntypedef struct Draw\n{\n\tBackground background;\n\tPointRender points;\n\tLineRender lines;\n\tCircleRender hollowCircles;\n\tSolidCircles circles;\n\tCapsules capsules;\n\tPolygons polygons;\n\tFont font;\n} Draw;\n\nDraw* CreateDraw( void )\n{\n\tDraw* draw = malloc( sizeof( Draw ) );\n\t*draw = (Draw){ 0 };\n\tdraw->background = CreateBackground();\n\tdraw->points = CreatePointDrawData();\n\tdraw->lines = CreateLineRender();\n\tdraw->hollowCircles = CreateCircles();\n\tdraw->circles = CreateSolidCircles();\n\tdraw->capsules = CreateCapsules();\n\tdraw->polygons = CreatePolygons();\n\tdraw->font = CreateFont( \"samples/data/droid_sans.ttf\", 18.0f );\n\treturn draw;\n}\n\nvoid DestroyDraw( Draw* draw )\n{\n\tDestroyBackground( &draw->background );\n\tDestroyPointDrawData( &draw->points );\n\tDestroyLineRender( &draw->lines );\n\tDestroyCircles( &draw->hollowCircles );\n\tDestroySolidCircles( &draw->circles );\n\tDestroyCapsules( &draw->capsules );\n\tDestroyPolygons( &draw->polygons );\n\tDestroyFont( &draw->font );\n\tfree( draw );\n}\n\nvoid DrawPoint( Draw* draw, b2Vec2 p, float size, b2HexColor color )\n{\n\tAddPoint( &draw->points, p, size, color );\n}\n\nvoid DrawLine( Draw* draw, b2Vec2 p1, b2Vec2 p2, b2HexColor color )\n{\n\tAddLine( &draw->lines, p1, p2, color );\n}\n\nvoid DrawCircle( Draw* draw, b2Vec2 center, float radius, b2HexColor color )\n{\n\tAddCircle( &draw->hollowCircles, center, radius, color );\n}\n\nvoid DrawSolidCircle( Draw* draw, b2Transform transform, float radius, b2HexColor color )\n{\n\tAddSolidCircle( &draw->circles, transform, radius, color );\n}\n\nvoid DrawSolidCapsule( Draw* draw, b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color )\n{\n\tAddCapsule( &draw->capsules, p1, p2, radius, color );\n}\n\nvoid DrawPolygon( Draw* draw, const b2Vec2* vertices, int vertexCount, b2HexColor color )\n{\n\tb2Vec2 p1 = vertices[vertexCount - 1];\n\tfor ( int i = 0; i < vertexCount; ++i )\n\t{\n\t\tb2Vec2 p2 = vertices[i];\n\t\tAddLine( &draw->lines, p1, p2, color );\n\t\tp1 = p2;\n\t}\n}\n\nvoid DrawSolidPolygon( Draw* draw, b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius,\n\t\t\t\t\t   b2HexColor color )\n{\n\tAddPolygon( &draw->polygons, transform, vertices, vertexCount, radius, color );\n}\n\nvoid DrawTransform( Draw* draw, b2Transform transform, float scale )\n{\n\tb2Vec2 p1 = transform.p;\n\n\tb2Vec2 p2 = b2MulAdd( p1, scale, b2Rot_GetXAxis( transform.q ) );\n\tAddLine( &draw->lines, p1, p2, b2_colorRed );\n\n\tp2 = b2MulAdd( p1, scale, b2Rot_GetYAxis( transform.q ) );\n\tAddLine( &draw->lines, p1, p2, b2_colorGreen );\n}\n\nvoid DrawBounds( Draw* draw, b2AABB aabb, b2HexColor color )\n{\n\tb2Vec2 p1 = aabb.lowerBound;\n\tb2Vec2 p2 = { aabb.upperBound.x, aabb.lowerBound.y };\n\tb2Vec2 p3 = aabb.upperBound;\n\tb2Vec2 p4 = { aabb.lowerBound.x, aabb.upperBound.y };\n\n\tAddLine( &draw->lines, p1, p2, color );\n\tAddLine( &draw->lines, p2, p3, color );\n\tAddLine( &draw->lines, p3, p4, color );\n\tAddLine( &draw->lines, p4, p1, color );\n}\n\nvoid DrawScreenString( Draw* draw, float x, float y, b2HexColor color, const char* string, ... )\n{\n\tchar buffer[256];\n\tva_list arg;\n\tva_start( arg, string );\n\tvsnprintf( buffer, 256, string, arg );\n\tva_end( arg );\n\n\tbuffer[255] = 0;\n\tAddText( &draw->font, x, y, color, buffer );\n}\n\nvoid DrawWorldString( Draw* draw, Camera* camera, b2Vec2 p, b2HexColor color, const char* string, ... )\n{\n\tb2Vec2 ps = ConvertWorldToScreen( camera, p );\n\n\tchar buffer[256];\n\tva_list arg;\n\tva_start( arg, string );\n\tvsnprintf( buffer, 256, string, arg );\n\tva_end( arg );\n\n\tbuffer[255] = 0;\n\tAddText( &draw->font, ps.x, ps.y, color, buffer );\n}\n\nvoid FlushDraw( Draw* draw, Camera* camera )\n{\n\t// order matters\n\tFlushSolidCircles( &draw->circles, camera );\n\tFlushCapsules( &draw->capsules, camera );\n\tFlushPolygons( &draw->polygons, camera );\n\tFlushCircles( &draw->hollowCircles, camera );\n\tFlushLines( &draw->lines, camera );\n\tFlushPoints( &draw->points, camera );\n\tFlushText( &draw->font, camera );\n\tCheckOpenGL();\n}\n\nvoid DrawBackground( Draw* draw, Camera* camera )\n{\n\tRenderBackground( &draw->background, camera );\n}\n"
  },
  {
    "path": "samples/draw.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"box2d/types.h\"\n\ntypedef struct Camera\n{\n\tb2Vec2 center;\n\tfloat zoom;\n\tfloat width;\n\tfloat height;\n} Camera;\n\ntypedef struct Draw Draw;\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\nCamera GetDefaultCamera( void );\nvoid ResetView( Camera* camera );\nb2Vec2 ConvertScreenToWorld( Camera* camera, b2Vec2 screenPoint );\nb2Vec2 ConvertWorldToScreen( Camera* camera, b2Vec2 worldPoint );\nb2AABB GetViewBounds( Camera* camera );\n\nDraw* CreateDraw( void );\nvoid DestroyDraw( Draw* draw );\n\nvoid DrawPoint( Draw* draw, b2Vec2 p, float size, b2HexColor color );\nvoid DrawLine( Draw* draw, b2Vec2 p1, b2Vec2 p2, b2HexColor color );\nvoid DrawCircle( Draw* draw, b2Vec2 center, float radius, b2HexColor color );\nvoid DrawSolidCircle( Draw* draw, b2Transform transform, float radius, b2HexColor color );\nvoid DrawSolidCapsule( Draw* draw, b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color );\nvoid DrawPolygon( Draw* draw, const b2Vec2* vertices, int vertexCount, b2HexColor color );\nvoid DrawSolidPolygon( Draw* draw, b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius,\n\t\t\t\t\t   b2HexColor color );\nvoid DrawTransform( Draw* draw, b2Transform transform, float scale );\nvoid DrawBounds( Draw* draw, b2AABB aabb, b2HexColor color );\nvoid DrawScreenString( Draw* draw, float x, float y, b2HexColor color, const char* string, ... );\nvoid DrawWorldString( Draw* draw, Camera* camera, b2Vec2 p, b2HexColor color, const char* string, ... );\n\nvoid FlushDraw( Draw* draw, Camera* camera );\n\nvoid DrawBackground( Draw* draw, Camera* camera );\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "samples/main.cpp",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER )\n#ifndef _CRT_SECURE_NO_WARNINGS\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n#define _CRTDBG_MAP_ALLOC\n#endif\n\n#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 1\n\n#include \"TaskScheduler.h\"\n#include \"draw.h\"\n#include \"sample.h\"\n\n#include \"box2d/base.h\"\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n// clang-format off\n#include \"glad/glad.h\"\n#include \"GLFW/glfw3.h\"\n// clang-format on\n\n#include \"imgui.h\"\n#include \"imgui_impl_glfw.h\"\n#include \"imgui_impl_opengl3.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#ifdef BOX2D_PROFILE\n#include <tracy/Tracy.hpp>\n#else\n#define FrameMark\n#endif\n\n#if defined( _MSC_VER ) && 0\n#include <crtdbg.h>\n\nstatic int MyAllocHook( int allocType, void* userData, size_t size, int blockType, long requestNumber,\n\t\t\t\t\t\tconst unsigned char* filename, int lineNumber )\n{\n\t// This hook can help find leaks\n\tif ( size == 143 )\n\t{\n\t\tsize += 0;\n\t}\n\n\treturn 1;\n}\n#endif\n\nstatic SampleContext s_context;\nstatic int32_t s_selection = 0;\nstatic Sample* s_sample = nullptr;\nstatic bool s_rightMouseDown = false;\nstatic b2Vec2 s_clickPointWS = b2Vec2_zero;\nstatic float s_framebufferScale = 1.0f;\n\ninline bool IsPowerOfTwo( int32_t x )\n{\n\treturn ( x != 0 ) && ( ( x & ( x - 1 ) ) == 0 );\n}\n\nvoid* AllocFcn( unsigned int size, int32_t alignment )\n{\n\t// Allocation must be a multiple of alignment or risk a seg fault\n\t// https://en.cppreference.com/w/c/memory/aligned_alloc\n\tassert( IsPowerOfTwo( alignment ) );\n\tsize_t sizeAligned = ( ( size - 1 ) | ( alignment - 1 ) ) + 1;\n\tassert( ( sizeAligned & ( alignment - 1 ) ) == 0 );\n\n#if defined( _MSC_VER ) || defined( __MINGW32__ ) || defined( __MINGW64__ )\n\tvoid* ptr = _aligned_malloc( sizeAligned, alignment );\n#else\n\tvoid* ptr = aligned_alloc( alignment, sizeAligned );\n#endif\n\tassert( ptr != nullptr );\n\treturn ptr;\n}\n\nvoid FreeFcn( void* mem, unsigned int size )\n{\n\t(void)size;\n\n#if defined( _MSC_VER ) || defined( __MINGW32__ ) || defined( __MINGW64__ )\n\t_aligned_free( mem );\n#else\n\tfree( mem );\n#endif\n}\n\nint AssertFcn( const char* condition, const char* fileName, int lineNumber )\n{\n\tprintf( \"SAMPLE ASSERTION: %s, %s, line %d\\n\", condition, fileName, lineNumber );\n\treturn 1;\n}\n\nvoid glfwErrorCallback( int error, const char* description )\n{\n\tfprintf( stderr, \"GLFW error occurred. Code: %d. Description: %s\\n\", error, description );\n}\n\nstatic int CompareSamples( const void* a, const void* b )\n{\n\tSampleEntry* sa = (SampleEntry*)a;\n\tSampleEntry* sb = (SampleEntry*)b;\n\n\tint result = strcmp( sa->category, sb->category );\n\tif ( result == 0 )\n\t{\n\t\tresult = strcmp( sa->name, sb->name );\n\t}\n\n\treturn result;\n}\n\nstatic void SortSamples()\n{\n\tqsort( g_sampleEntries, g_sampleCount, sizeof( SampleEntry ), CompareSamples );\n}\n\nstatic void RestartSample()\n{\n\tdelete s_sample;\n\ts_sample = nullptr;\n\ts_context.restart = true;\n\ts_sample = g_sampleEntries[s_context.sampleIndex].createFcn( &s_context );\n\ts_context.restart = false;\n}\n\nstatic void CreateUI( GLFWwindow* window, const char* glslVersion )\n{\n\tIMGUI_CHECKVERSION();\n\tImGui::CreateContext();\n\n\tbool success = ImGui_ImplGlfw_InitForOpenGL( window, false );\n\tif ( success == false )\n\t{\n\t\tprintf( \"ImGui_ImplGlfw_InitForOpenGL failed\\n\" );\n\t\tassert( false );\n\t}\n\n\tsuccess = ImGui_ImplOpenGL3_Init( glslVersion );\n\tif ( success == false )\n\t{\n\t\tprintf( \"ImGui_ImplOpenGL3_Init failed\\n\" );\n\t\tassert( false );\n\t}\n\n\tImGui::GetStyle().ScaleAllSizes( s_context.uiScale );\n\n\tconst char* fontPath = \"samples/data/droid_sans.ttf\";\n\tFILE* file = fopen( fontPath, \"rb\" );\n\n\tif ( file != nullptr )\n\t{\n\t\tImFontConfig fontConfig;\n\t\tfontConfig.RasterizerMultiply = s_context.uiScale * s_framebufferScale;\n\n\t\tfloat regularSize = floorf( 13.0f * s_context.uiScale );\n\t\tfloat mediumSize = floorf( 40.0f * s_context.uiScale );\n\t\tfloat largeSize = floorf( 64.0f * s_context.uiScale );\n\n\t\tImGuiIO& io = ImGui::GetIO();\n\t\ts_context.regularFont = io.Fonts->AddFontFromFileTTF( fontPath, regularSize, &fontConfig );\n\t\ts_context.mediumFont = io.Fonts->AddFontFromFileTTF( fontPath, mediumSize, &fontConfig );\n\t\ts_context.largeFont = io.Fonts->AddFontFromFileTTF( fontPath, largeSize, &fontConfig );\n\n\t\tImGui::GetIO().FontDefault = s_context.regularFont;\n\t}\n\telse\n\t{\n\t\tprintf( \"\\n\\nERROR: the Box2D samples working directory must be the top level Box2D directory (same as README.md)\\n\\n\" );\n\t\texit( EXIT_FAILURE );\n\t}\n}\n\nstatic void DestroyUI()\n{\n\tImGui_ImplOpenGL3_Shutdown();\n\tImGui_ImplGlfw_Shutdown();\n\tImGui::DestroyContext();\n}\n\nstatic void ResizeWindowCallback( GLFWwindow*, int width, int height )\n{\n\ts_context.camera.width = float( width );\n\ts_context.camera.height = float( height );\n}\n\nstatic void KeyCallback( GLFWwindow* window, int key, int scancode, int action, int mods )\n{\n\tImGui_ImplGlfw_KeyCallback( window, key, scancode, action, mods );\n\tif ( ImGui::GetIO().WantCaptureKeyboard )\n\t{\n\t\treturn;\n\t}\n\n\tif ( action == GLFW_PRESS )\n\t{\n\t\tswitch ( key )\n\t\t{\n\t\t\tcase GLFW_KEY_ESCAPE:\n\t\t\t\t// Quit\n\t\t\t\tglfwSetWindowShouldClose( s_context.window, GL_TRUE );\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_LEFT:\n\t\t\t\t// Pan left\n\t\t\t\tif ( mods == GLFW_MOD_CONTROL )\n\t\t\t\t{\n\t\t\t\t\tb2Vec2 newOrigin = { 2.0f, 0.0f };\n\t\t\t\t\ts_sample->ShiftOrigin( newOrigin );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ts_context.camera.center.x -= 0.5f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_RIGHT:\n\t\t\t\t// Pan right\n\t\t\t\tif ( mods == GLFW_MOD_CONTROL )\n\t\t\t\t{\n\t\t\t\t\tb2Vec2 newOrigin = { -2.0f, 0.0f };\n\t\t\t\t\ts_sample->ShiftOrigin( newOrigin );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ts_context.camera.center.x += 0.5f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_DOWN:\n\t\t\t\t// Pan down\n\t\t\t\tif ( mods == GLFW_MOD_CONTROL )\n\t\t\t\t{\n\t\t\t\t\tb2Vec2 newOrigin = { 0.0f, 2.0f };\n\t\t\t\t\ts_sample->ShiftOrigin( newOrigin );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ts_context.camera.center.y -= 0.5f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_UP:\n\t\t\t\t// Pan up\n\t\t\t\tif ( mods == GLFW_MOD_CONTROL )\n\t\t\t\t{\n\t\t\t\t\tb2Vec2 newOrigin = { 0.0f, -2.0f };\n\t\t\t\t\ts_sample->ShiftOrigin( newOrigin );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ts_context.camera.center.y += 0.5f;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_HOME:\n\t\t\t\tResetView( &s_context.camera );\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_R:\n\t\t\t\tRestartSample();\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_O:\n\t\t\t\ts_context.singleStep = true;\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_P:\n\t\t\t\ts_context.pause = !s_context.pause;\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_LEFT_BRACKET:\n\t\t\t\t// Switch to previous test\n\t\t\t\t--s_selection;\n\t\t\t\tif ( s_selection < 0 )\n\t\t\t\t{\n\t\t\t\t\ts_selection = g_sampleCount - 1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_RIGHT_BRACKET:\n\t\t\t\t// Switch to next test\n\t\t\t\t++s_selection;\n\t\t\t\tif ( s_selection == g_sampleCount )\n\t\t\t\t{\n\t\t\t\t\ts_selection = 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_TAB:\n\t\t\t\ts_context.showUI = !s_context.showUI;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tif ( s_sample )\n\t\t\t\t{\n\t\t\t\t\ts_sample->Keyboard( key );\n\t\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void CharCallback( GLFWwindow* window, unsigned int c )\n{\n\tImGui_ImplGlfw_CharCallback( window, c );\n}\n\nstatic void MouseButtonCallback( GLFWwindow* window, int button, int action, int modifiers )\n{\n\tImGui_ImplGlfw_MouseButtonCallback( window, button, action, modifiers );\n\n\tif ( ImGui::GetIO().WantCaptureMouse )\n\t{\n\t\treturn;\n\t}\n\n\tdouble xd, yd;\n\tglfwGetCursorPos( window, &xd, &yd );\n\tb2Vec2 ps = { float( xd ), float( yd ) };\n\n\t// Use the mouse to move things around.\n\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t{\n\t\tb2Vec2 pw = ConvertScreenToWorld( &s_context.camera, ps );\n\t\tif ( action == GLFW_PRESS )\n\t\t{\n\t\t\ts_sample->MouseDown( pw, button, modifiers );\n\t\t}\n\n\t\tif ( action == GLFW_RELEASE )\n\t\t{\n\t\t\ts_sample->MouseUp( pw, button );\n\t\t}\n\t}\n\telse if ( button == GLFW_MOUSE_BUTTON_2 )\n\t{\n\t\tif ( action == GLFW_PRESS )\n\t\t{\n\t\t\ts_clickPointWS = ConvertScreenToWorld( &s_context.camera, ps );\n\t\t\ts_rightMouseDown = true;\n\t\t}\n\n\t\tif ( action == GLFW_RELEASE )\n\t\t{\n\t\t\ts_rightMouseDown = false;\n\t\t}\n\t}\n}\n\nstatic void MouseMotionCallback( GLFWwindow* window, double xd, double yd )\n{\n\tb2Vec2 ps = { float( xd ), float( yd ) };\n\n\tImGui_ImplGlfw_CursorPosCallback( window, ps.x, ps.y );\n\n\tb2Vec2 pw = ConvertScreenToWorld( &s_context.camera, ps );\n\ts_sample->MouseMove( pw );\n\n\tif ( s_rightMouseDown )\n\t{\n\t\tb2Vec2 diff = b2Sub( pw, s_clickPointWS );\n\t\ts_context.camera.center.x -= diff.x;\n\t\ts_context.camera.center.y -= diff.y;\n\t\ts_clickPointWS = ConvertScreenToWorld( &s_context.camera, ps );\n\t}\n}\n\nstatic void ScrollCallback( GLFWwindow* window, double dx, double dy )\n{\n\tImGui_ImplGlfw_ScrollCallback( window, dx, dy );\n\tif ( ImGui::GetIO().WantCaptureMouse )\n\t{\n\t\treturn;\n\t}\n\n\tif ( dy > 0 )\n\t{\n\t\ts_context.camera.zoom /= 1.1f;\n\t}\n\telse\n\t{\n\t\ts_context.camera.zoom *= 1.1f;\n\t}\n}\n\nstatic void UpdateUI()\n{\n\tint maxWorkers = enki::GetNumHardwareThreads();\n\n\tfloat fontSize = ImGui::GetFontSize();\n\tfloat menuWidth = 13.0f * fontSize;\n\tif ( s_context.showUI )\n\t{\n\t\tImGui::SetNextWindowPos( { s_context.camera.width - menuWidth - 0.5f * fontSize, 0.5f * fontSize } );\n\t\tImGui::SetNextWindowSize( { menuWidth, s_context.camera.height - fontSize } );\n\n\t\tImGui::Begin( \"Tools\", &s_context.showUI,\n\t\t\t\t\t  ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse );\n\n\t\tif ( ImGui::BeginTabBar( \"ControlTabs\", ImGuiTabBarFlags_None ) )\n\t\t{\n\t\t\tif ( ImGui::BeginTabItem( \"Controls\" ) )\n\t\t\t{\n\t\t\t\tImGui::PushItemWidth( 100.0f );\n\t\t\t\tImGui::SliderInt( \"Sub-steps\", &s_context.subStepCount, 1, 32 );\n\t\t\t\tImGui::SliderFloat( \"Hertz\", &s_context.hertz, 5.0f, 240.0f, \"%.0f hz\" );\n\n\t\t\t\tif ( ImGui::SliderInt( \"Workers\", &s_context.workerCount, 1, maxWorkers ) )\n\t\t\t\t{\n\t\t\t\t\ts_context.workerCount = b2ClampInt( s_context.workerCount, 1, maxWorkers );\n\t\t\t\t\tRestartSample();\n\t\t\t\t}\n\t\t\t\tImGui::PopItemWidth();\n\n\t\t\t\tImGui::Separator();\n\n\t\t\t\tImGui::Checkbox( \"Sleep\", &s_context.enableSleep );\n\t\t\t\tImGui::Checkbox( \"Warm Starting\", &s_context.enableWarmStarting );\n\t\t\t\tImGui::Checkbox( \"Continuous\", &s_context.enableContinuous );\n\n\t\t\t\tImGui::Separator();\n\n\t\t\t\tImGui::Checkbox( \"Shapes\", &s_context.debugDraw.drawShapes );\n\t\t\t\tImGui::Checkbox( \"Joints\", &s_context.debugDraw.drawJoints );\n\t\t\t\tImGui::Checkbox( \"Joint Extras\", &s_context.debugDraw.drawJointExtras );\n\t\t\t\tImGui::Checkbox( \"Bounds\", &s_context.debugDraw.drawBounds );\n\t\t\t\tImGui::Checkbox( \"Contact Points\", &s_context.debugDraw.drawContactPoints );\n\t\t\t\tImGui::Checkbox( \"Contact Normals\", &s_context.debugDraw.drawContactNormals );\n\t\t\t\tImGui::Checkbox( \"Contact Features\", &s_context.debugDraw.drawContactFeatures );\n\t\t\t\tImGui::Checkbox( \"Contact Forces\", &s_context.debugDraw.drawContactForces );\n\t\t\t\tImGui::Checkbox( \"Friction Forces\", &s_context.debugDraw.drawFrictionForces );\n\t\t\t\tImGui::Checkbox( \"Mass\", &s_context.debugDraw.drawMass );\n\t\t\t\tImGui::Checkbox( \"Body Names\", &s_context.debugDraw.drawBodyNames );\n\t\t\t\tImGui::Checkbox( \"Graph Colors\", &s_context.debugDraw.drawGraphColors );\n\t\t\t\tImGui::Checkbox( \"Islands\", &s_context.debugDraw.drawIslands );\n\t\t\t\tImGui::Checkbox( \"Counters\", &s_context.drawCounters );\n\t\t\t\tImGui::Checkbox( \"Profile\", &s_context.drawProfile );\n\n\t\t\t\tImGui::PushItemWidth( 80.0f );\n\t\t\t\tImGui::InputFloat( \"Joint Scale\", &s_context.debugDraw.jointScale );\n\t\t\t\tImGui::InputFloat( \"Force Scale\", &s_context.debugDraw.forceScale );\n\t\t\t\tImGui::PopItemWidth();\n\n\t\t\t\tImVec2 button_sz = ImVec2( -1, 0 );\n\t\t\t\tif ( ImGui::Button( \"Pause (P)\", button_sz ) )\n\t\t\t\t{\n\t\t\t\t\ts_context.pause = !s_context.pause;\n\t\t\t\t}\n\n\t\t\t\tif ( ImGui::Button( \"Single Step (O)\", button_sz ) )\n\t\t\t\t{\n\t\t\t\t\ts_context.singleStep = !s_context.singleStep;\n\t\t\t\t}\n\n\t\t\t\tif ( ImGui::Button( \"Dump Mem Stats\", button_sz ) )\n\t\t\t\t{\n\t\t\t\t\tb2World_DumpMemoryStats( s_sample->m_worldId );\n\t\t\t\t}\n\n\t\t\t\tif ( ImGui::Button( \"Reset Profile\", button_sz ) )\n\t\t\t\t{\n\t\t\t\t\ts_sample->ResetProfile();\n\t\t\t\t}\n\n\t\t\t\tif ( ImGui::Button( \"Restart (R)\", button_sz ) )\n\t\t\t\t{\n\t\t\t\t\tRestartSample();\n\t\t\t\t}\n\n\t\t\t\tif ( ImGui::Button( \"Quit\", button_sz ) )\n\t\t\t\t{\n\t\t\t\t\tglfwSetWindowShouldClose( s_context.window, GL_TRUE );\n\t\t\t\t}\n\n\t\t\t\tImGui::EndTabItem();\n\t\t\t}\n\n\t\t\tImGuiTreeNodeFlags leafNodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;\n\t\t\tleafNodeFlags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;\n\n\t\t\tImGuiTreeNodeFlags nodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;\n\n\t\t\tif ( ImGui::BeginTabItem( \"Samples\" ) )\n\t\t\t{\n\t\t\t\tint categoryIndex = 0;\n\t\t\t\tconst char* category = g_sampleEntries[categoryIndex].category;\n\t\t\t\tint i = 0;\n\t\t\t\twhile ( i < g_sampleCount )\n\t\t\t\t{\n\t\t\t\t\tbool categorySelected = strcmp( category, g_sampleEntries[s_context.sampleIndex].category ) == 0;\n\t\t\t\t\tImGuiTreeNodeFlags nodeSelectionFlags = categorySelected ? ImGuiTreeNodeFlags_Selected : 0;\n\t\t\t\t\tbool nodeOpen = ImGui::TreeNodeEx( category, nodeFlags | nodeSelectionFlags );\n\n\t\t\t\t\tif ( nodeOpen )\n\t\t\t\t\t{\n\t\t\t\t\t\twhile ( i < g_sampleCount && strcmp( category, g_sampleEntries[i].category ) == 0 )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tImGuiTreeNodeFlags selectionFlags = 0;\n\t\t\t\t\t\t\tif ( s_context.sampleIndex == i )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tselectionFlags = ImGuiTreeNodeFlags_Selected;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tImGui::TreeNodeEx( (void*)(intptr_t)i, leafNodeFlags | selectionFlags, \"%s\",\n\t\t\t\t\t\t\t\t\t\t\t   g_sampleEntries[i].name );\n\t\t\t\t\t\t\tif ( ImGui::IsItemClicked() )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ts_selection = i;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t++i;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tImGui::TreePop();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\twhile ( i < g_sampleCount && strcmp( category, g_sampleEntries[i].category ) == 0 )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t++i;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( i < g_sampleCount )\n\t\t\t\t\t{\n\t\t\t\t\t\tcategory = g_sampleEntries[i].category;\n\t\t\t\t\t\tcategoryIndex = i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tImGui::EndTabItem();\n\t\t\t}\n\t\t\tImGui::EndTabBar();\n\t\t}\n\n\t\tImGui::End();\n\n\t\ts_sample->UpdateGui();\n\t}\n}\n\nint main( int, char** )\n{\n#if defined( _MSC_VER )\n\t// Enable memory-leak reports\n\t_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE );\n\t_CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );\n\t//_CrtSetAllocHook(MyAllocHook);\n\n// How to break at the leaking allocation, in the watch window enter this variable\n// and set it to the allocation number in {}. Do this at the first line in main.\n// {,,ucrtbased.dll}_crtBreakAlloc = <allocation number>\n#endif\n\n\t// Install memory hooks\n\tb2SetAllocator( AllocFcn, FreeFcn );\n\tb2SetAssertFcn( AssertFcn );\n\n\tchar buffer[128];\n\n\ts_context.Load();\n\ts_context.workerCount = b2MinInt( 8, (int)enki::GetNumHardwareThreads() / 2 );\n\n\tSortSamples();\n\n\tglfwSetErrorCallback( glfwErrorCallback );\n\n\tif ( glfwInit() == 0 )\n\t{\n\t\tfprintf( stderr, \"Failed to initialize GLFW\\n\" );\n\t\treturn -1;\n\t}\n\n#if __APPLE__\n\tconst char* glslVersion = \"#version 150\";\n#else\n\tconst char* glslVersion = nullptr;\n#endif\n\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\n\t// MSAA\n\tglfwWindowHint( GLFW_SAMPLES, 4 );\n\n\tb2Version version = b2GetVersion();\n\tsnprintf( buffer, 128, \"Box2D Version %d.%d.%d\", version.major, version.minor, version.revision );\n\n\tif ( GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor() )\n\t{\n#ifdef __APPLE__\n\t\tglfwGetMonitorContentScale( primaryMonitor, &s_framebufferScale, &s_framebufferScale );\n#else\n\t\tfloat uiScale = 1.0f;\n\t\tglfwGetMonitorContentScale( primaryMonitor, &uiScale, &uiScale );\n\t\ts_context.uiScale = uiScale;\n#endif\n\t}\n\n\tbool fullscreen = false;\n\tif ( fullscreen )\n\t{\n\t\ts_context.window = glfwCreateWindow( 1920, 1080, buffer, glfwGetPrimaryMonitor(), nullptr );\n\t}\n\telse\n\t{\n\t\ts_context.window =\n\t\t\tglfwCreateWindow( int( s_context.camera.width ), int( s_context.camera.height ), buffer, nullptr, nullptr );\n\t}\n\n\tif ( s_context.window == nullptr )\n\t{\n\t\tfprintf( stderr, \"Failed to open GLFW window.\\n\" );\n\t\tglfwTerminate();\n\t\treturn -1;\n\t}\n\n\tglfwMakeContextCurrent( s_context.window );\n\n\t// Load OpenGL functions using glad\n\tif ( !gladLoadGL() )\n\t{\n\t\tfprintf( stderr, \"Failed to initialize glad\\n\" );\n\t\tglfwTerminate();\n\t\treturn -1;\n\t}\n\n\t{\n\t\tconst char* glVersionString = (const char*)glGetString( GL_VERSION );\n\t\tconst char* glslVersionString = (const char*)glGetString( GL_SHADING_LANGUAGE_VERSION );\n\t\tprintf( \"OpenGL %s, GLSL %s\\n\", glVersionString, glslVersionString );\n\t}\n\n\tglfwSetWindowSizeCallback( s_context.window, ResizeWindowCallback );\n\tglfwSetKeyCallback( s_context.window, KeyCallback );\n\tglfwSetCharCallback( s_context.window, CharCallback );\n\tglfwSetMouseButtonCallback( s_context.window, MouseButtonCallback );\n\tglfwSetCursorPosCallback( s_context.window, MouseMotionCallback );\n\tglfwSetScrollCallback( s_context.window, ScrollCallback );\n\n\tCreateUI( s_context.window, glslVersion );\n\ts_context.draw = CreateDraw();\n\n\ts_context.sampleIndex = b2ClampInt( s_context.sampleIndex, 0, g_sampleCount - 1 );\n\ts_selection = s_context.sampleIndex;\n\n\tglClearColor( 0.2f, 0.2f, 0.2f, 1.0f );\n\n\tfloat frameTime = 0.0;\n\n\twhile ( !glfwWindowShouldClose( s_context.window ) )\n\t{\n\t\tdouble time1 = glfwGetTime();\n\n\t\tif ( glfwGetKey( s_context.window, GLFW_KEY_Z ) == GLFW_PRESS )\n\t\t{\n\t\t\t// Zoom out\n\t\t\ts_context.camera.zoom = b2MinFloat( 1.005f * s_context.camera.zoom, 100.0f );\n\t\t}\n\t\telse if ( glfwGetKey( s_context.window, GLFW_KEY_X ) == GLFW_PRESS )\n\t\t{\n\t\t\t// Zoom in\n\t\t\ts_context.camera.zoom = b2MaxFloat( 0.995f * s_context.camera.zoom, 0.5f );\n\t\t}\n\n\t\tint width, height;\n\t\tglfwGetWindowSize( s_context.window, &width, &height );\n\t\ts_context.camera.width = width;\n\t\ts_context.camera.height = height;\n\n\t\tint bufferWidth, bufferHeight;\n\t\tglfwGetFramebufferSize( s_context.window, &bufferWidth, &bufferHeight );\n\t\tglViewport( 0, 0, bufferWidth, bufferHeight );\n\n\t\tglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );\n\n\t\t// s_context.draw.DrawBackground();\n\n\t\t// double cursorPosX = 0, cursorPosY = 0;\n\t\t// glfwGetCursorPos( s_context.window, &cursorPosX, &cursorPosY );\n\t\t// ImGui_ImplGlfw_CursorPosCallback( s_context.window, cursorPosX / s_windowScale, cursorPosY / s_windowScale );\n\t\tImGui_ImplOpenGL3_NewFrame();\n\t\tImGui_ImplGlfw_NewFrame();\n\t\t// ImGui_ImplGlfw_CursorPosCallback( s_context.window, cursorPosX / s_windowScale, cursorPosY / s_windowScale );\n\n\t\tImGuiIO& io = ImGui::GetIO();\n\t\tio.DisplaySize.x = s_context.camera.width;\n\t\tio.DisplaySize.y = s_context.camera.height;\n\t\tio.DisplayFramebufferScale.x = bufferWidth / s_context.camera.width;\n\t\tio.DisplayFramebufferScale.y = bufferHeight / s_context.camera.height;\n\n\t\tImGui::NewFrame();\n\n\t\tif ( s_sample == nullptr )\n\t\t{\n\t\t\t// delayed creation because imgui doesn't create fonts until NewFrame() is called\n\t\t\ts_sample = g_sampleEntries[s_context.sampleIndex].createFcn( &s_context );\n\t\t}\n\n\t\ts_sample->ResetText();\n\n\t\tconst SampleEntry& entry = g_sampleEntries[s_context.sampleIndex];\n\t\ts_sample->DrawColoredTextLine(b2_colorYellow, \"%s : %s\", entry.category, entry.name );\n\n\t\ts_sample->Step();\n\n\t\tDrawScreenString( s_context.draw, 5.0f, s_context.camera.height - 10.0f, b2_colorSeaGreen,\n\t\t\t\t\t\t  \"%.1f ms - step %d - camera (%g, %g, %g)\", 1000.0f * frameTime, s_sample->m_stepCount,\n\t\t\t\t\t\t  s_context.camera.center.x, s_context.camera.center.y, s_context.camera.zoom );\n\n\t\tFlushDraw( s_context.draw, &s_context.camera );\n\n\t\tUpdateUI();\n\n\t\t// ImGui::ShowDemoWindow();\n\n\t\tImGui::Render();\n\t\tImGui_ImplOpenGL3_RenderDrawData( ImGui::GetDrawData() );\n\n\t\tglfwSwapBuffers( s_context.window );\n\n\t\t// For the Tracy profiler\n\t\tFrameMark;\n\n\t\tif ( s_selection != s_context.sampleIndex )\n\t\t{\n\t\t\tResetView( &s_context.camera );\n\t\t\ts_context.sampleIndex = s_selection;\n\t\t\ts_context.subStepCount = 4;\n\t\t\ts_context.debugDraw.drawJoints = true;\n\n\t\t\tdelete s_sample;\n\t\t\ts_sample = nullptr;\n\t\t\ts_sample = g_sampleEntries[s_context.sampleIndex].createFcn( &s_context );\n\t\t}\n\n\t\tglfwPollEvents();\n\n\t\t// Limit frame rate to 60Hz\n\t\tdouble time2 = glfwGetTime();\n\t\tdouble targetTime = time1 + 1.0 / 60.0;\n\t\twhile ( time2 < targetTime )\n\t\t{\n\t\t\tb2Yield();\n\t\t\ttime2 = glfwGetTime();\n\t\t}\n\n\t\tframeTime = float( time2 - time1 );\n\t}\n\n\tdelete s_sample;\n\ts_sample = nullptr;\n\n\tDestroyDraw( s_context.draw );\n\n\tDestroyUI();\n\tglfwTerminate();\n\n\ts_context.Save();\n\n#if defined( _MSC_VER )\n\t_CrtDumpMemoryLeaks();\n#endif\n\n\treturn 0;\n}\n"
  },
  {
    "path": "samples/sample.cpp",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"sample.h\"\n\n#include \"TaskScheduler.h\"\n#include \"draw.h\"\n#include \"imgui.h\"\n#include \"random.h\"\n\n// consider using https://github.com/skeeto/pdjson\n#include \"jsmn.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n#include <ctype.h>\n#include <stdio.h>\n\nstatic const char* fileName = \"settings.ini\";\n\n// Load a file. You must free the character array.\nstatic bool ReadFile( char*& data, int& size, const char* filename )\n{\n\tFILE* file = fopen( filename, \"rb\" );\n\tif ( file == nullptr )\n\t{\n\t\treturn false;\n\t}\n\n\tfseek( file, 0, SEEK_END );\n\tsize = (int)ftell( file );\n\tfseek( file, 0, SEEK_SET );\n\n\tif ( size == 0 )\n\t{\n\t\treturn false;\n\t}\n\n\tdata = (char*)malloc( size + 1 );\n\tsize_t count = fread( data, size, 1, file );\n\t(void)count;\n\tfclose( file );\n\tdata[size] = 0;\n\n\treturn true;\n}\n\nvoid SampleContext::Save()\n{\n\tFILE* file = fopen( fileName, \"w\" );\n\tfprintf( file, \"{\\n\" );\n\tfprintf( file, \"  \\\"sampleIndex\\\": %d,\\n\", sampleIndex );\n\tfprintf( file, \"  \\\"drawShapes\\\": %s,\\n\", debugDraw.drawShapes ? \"true\" : \"false\" );\n\tfprintf( file, \"  \\\"drawJoints\\\": %s,\\n\", debugDraw.drawJoints ? \"true\" : \"false\" );\n\tfprintf( file, \"}\\n\" );\n\tfclose( file );\n}\n\nstatic int jsoneq( const char* json, jsmntok_t* tok, const char* s )\n{\n\tif ( tok->type == JSMN_STRING && (int)strlen( s ) == tok->end - tok->start &&\n\t\t strncmp( json + tok->start, s, tok->end - tok->start ) == 0 )\n\t{\n\t\treturn 0;\n\t}\n\treturn -1;\n}\n\nvoid DrawPolygonFcn( const b2Vec2* vertices, int vertexCount, b2HexColor color, void* context )\n{\n\tSampleContext* sampleContext = static_cast<SampleContext*>( context );\n\tDrawPolygon( sampleContext->draw, vertices, vertexCount, color );\n}\n\nvoid DrawSolidPolygonFcn( b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius, b2HexColor color,\n\t\t\t\t\t\t  void* context )\n{\n\tSampleContext* sampleContext = static_cast<SampleContext*>( context );\n\tDrawSolidPolygon( sampleContext->draw, transform, vertices, vertexCount, radius, color );\n}\n\nvoid DrawCircleFcn( b2Vec2 center, float radius, b2HexColor color, void* context )\n{\n\tSampleContext* sampleContext = static_cast<SampleContext*>( context );\n\tDrawCircle( sampleContext->draw, center, radius, color );\n}\n\nvoid DrawSolidCircleFcn( b2Transform transform, float radius, b2HexColor color, void* context )\n{\n\tSampleContext* sampleContext = static_cast<SampleContext*>( context );\n\tDrawSolidCircle( sampleContext->draw, transform, radius, color );\n}\n\nvoid DrawSolidCapsuleFcn( b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color, void* context )\n{\n\tSampleContext* sampleContext = static_cast<SampleContext*>( context );\n\tDrawSolidCapsule( sampleContext->draw, p1, p2, radius, color );\n}\n\nvoid DrawLineFcn( b2Vec2 p1, b2Vec2 p2, b2HexColor color, void* context )\n{\n\tSampleContext* sampleContext = static_cast<SampleContext*>( context );\n\tDrawLine( sampleContext->draw, p1, p2, color );\n}\n\nvoid DrawTransformFcn( b2Transform transform, void* context )\n{\n\tSampleContext* sampleContext = static_cast<SampleContext*>( context );\n\tDrawTransform( sampleContext->draw, transform, 1.0f );\n}\n\nvoid DrawPointFcn( b2Vec2 p, float size, b2HexColor color, void* context )\n{\n\tSampleContext* sampleContext = static_cast<SampleContext*>( context );\n\tDrawPoint( sampleContext->draw, p, size, color );\n}\n\nvoid DrawStringFcn( b2Vec2 p, const char* s, b2HexColor color, void* context )\n{\n\tSampleContext* sampleContext = static_cast<SampleContext*>( context );\n\tDrawWorldString( sampleContext->draw, &sampleContext->camera, p, color, s );\n}\n\n#define MAX_TOKENS 32\n\nvoid SampleContext::Load()\n{\n\tcamera = GetDefaultCamera();\n\tdebugDraw = b2DefaultDebugDraw();\n\tdebugDraw.DrawPolygonFcn = DrawPolygonFcn;\n\tdebugDraw.DrawSolidPolygonFcn = DrawSolidPolygonFcn;\n\tdebugDraw.DrawCircleFcn = DrawCircleFcn;\n\tdebugDraw.DrawSolidCircleFcn = DrawSolidCircleFcn;\n\tdebugDraw.DrawSolidCapsuleFcn = DrawSolidCapsuleFcn;\n\tdebugDraw.DrawLineFcn = DrawLineFcn;\n\tdebugDraw.DrawTransformFcn = DrawTransformFcn;\n\tdebugDraw.DrawPointFcn = DrawPointFcn;\n\tdebugDraw.DrawStringFcn = DrawStringFcn;\n\tdebugDraw.context = this;\n\n\tchar* data = nullptr;\n\tint size = 0;\n\tbool found = ReadFile( data, size, fileName );\n\tif ( found == false )\n\t{\n\t\treturn;\n\t}\n\n\tjsmn_parser parser;\n\tjsmntok_t tokens[MAX_TOKENS];\n\n\tjsmn_init( &parser );\n\n\t// js - pointer to JSON string\n\t// tokens - an array of tokens available\n\t// 10 - number of tokens available\n\tint tokenCount = jsmn_parse( &parser, data, size, tokens, MAX_TOKENS );\n\tchar buffer[32];\n\n\tfor ( int i = 0; i < tokenCount; ++i )\n\t{\n\t\tif ( jsoneq( data, &tokens[i], \"sampleIndex\" ) == 0 )\n\t\t{\n\t\t\tint count = tokens[i + 1].end - tokens[i + 1].start;\n\t\t\tassert( count < 32 );\n\t\t\tconst char* s = data + tokens[i + 1].start;\n\t\t\tstrncpy( buffer, s, count );\n\t\t\tbuffer[count] = 0;\n\t\t\tchar* dummy;\n\t\t\tsampleIndex = (int)strtol( buffer, &dummy, 10 );\n\t\t}\n\t\telse if ( jsoneq( data, &tokens[i], \"drawShapes\" ) == 0 )\n\t\t{\n\t\t\tconst char* s = data + tokens[i + 1].start;\n\t\t\tif ( strncmp( s, \"true\", 4 ) == 0 )\n\t\t\t{\n\t\t\t\tdebugDraw.drawShapes = true;\n\t\t\t}\n\t\t\telse if ( strncmp( s, \"false\", 5 ) == 0 )\n\t\t\t{\n\t\t\t\tdebugDraw.drawShapes = false;\n\t\t\t}\n\t\t}\n\t\telse if ( jsoneq( data, &tokens[i], \"drawJoints\" ) == 0 )\n\t\t{\n\t\t\tconst char* s = data + tokens[i + 1].start;\n\t\t\tif ( strncmp( s, \"true\", 4 ) == 0 )\n\t\t\t{\n\t\t\t\tdebugDraw.drawJoints = true;\n\t\t\t}\n\t\t\telse if ( strncmp( s, \"false\", 5 ) == 0 )\n\t\t\t{\n\t\t\t\tdebugDraw.drawJoints = false;\n\t\t\t}\n\t\t}\n\t}\n\n\tfree( data );\n}\n\nclass SampleTask : public enki::ITaskSet\n{\npublic:\n\tSampleTask() = default;\n\n\tvoid ExecuteRange( enki::TaskSetPartition range, uint32_t threadIndex ) override\n\t{\n\t\tm_task( range.start, range.end, threadIndex, m_taskContext );\n\t}\n\n\tb2TaskCallback* m_task = nullptr;\n\tvoid* m_taskContext = nullptr;\n};\n\nstatic void* EnqueueTask( b2TaskCallback* task, int32_t itemCount, int32_t minRange, void* taskContext, void* userContext )\n{\n\tSample* sample = static_cast<Sample*>( userContext );\n\tif ( sample->m_taskCount < Sample::m_maxTasks )\n\t{\n\t\tSampleTask& sampleTask = sample->m_tasks[sample->m_taskCount];\n\t\tsampleTask.m_SetSize = itemCount;\n\t\tsampleTask.m_MinRange = minRange;\n\t\tsampleTask.m_task = task;\n\t\tsampleTask.m_taskContext = taskContext;\n\t\tsample->m_scheduler->AddTaskSetToPipe( &sampleTask );\n\t\t++sample->m_taskCount;\n\t\treturn &sampleTask;\n\t}\n\telse\n\t{\n\t\t// This is not fatal but the maxTasks should be increased\n\t\tassert( false );\n\t\ttask( 0, itemCount, 0, taskContext );\n\t\treturn nullptr;\n\t}\n}\n\nstatic void FinishTask( void* taskPtr, void* userContext )\n{\n\tif ( taskPtr != nullptr )\n\t{\n\t\tSampleTask* sampleTask = static_cast<SampleTask*>( taskPtr );\n\t\tSample* sample = static_cast<Sample*>( userContext );\n\t\tsample->m_scheduler->WaitforTask( sampleTask );\n\t}\n}\n\nstatic void TestMathCpp()\n{\n\tb2Vec2 a = { 1.0f, 2.0f };\n\tb2Vec2 b = { 3.0f, 4.0f };\n\n\tb2Vec2 c = a;\n\tc += b;\n\tc -= b;\n\tc *= 2.0f;\n\tc = -a;\n\tc = c + b;\n\tc = c - a;\n\tc = 2.0f * a;\n\tc = a * 2.0f;\n\n\tif ( b == a )\n\t{\n\t\tc = a;\n\t}\n\n\tif ( b != a )\n\t{\n\t\tc = b;\n\t}\n\n\tc += c;\n}\n\nSample::Sample( SampleContext* context )\n{\n\tm_context = context;\n\tm_camera = &context->camera;\n\tm_draw = context->draw;\n\n\tm_scheduler = new enki::TaskScheduler;\n\tm_scheduler->Initialize( m_context->workerCount );\n\n\tm_tasks = new SampleTask[m_maxTasks];\n\tm_taskCount = 0;\n\n\tm_threadCount = 1 + m_context->workerCount;\n\n\tm_worldId = b2_nullWorldId;\n\n\tm_textIncrement = 26;\n\tm_textLine = m_textIncrement;\n\tm_mouseJointId = b2_nullJointId;\n\n\tm_stepCount = 0;\n\n\tm_mouseBodyId = b2_nullBodyId;\n\tm_mousePoint = {};\n\tm_mouseForceScale = 100.0f;\n\n\tm_maxProfile = {};\n\tm_totalProfile = {};\n\n\tg_randomSeed = RAND_SEED;\n\n\tCreateWorld();\n\tTestMathCpp();\n}\n\nSample::~Sample()\n{\n\t// By deleting the world, we delete the bomb, mouse joint, etc.\n\tb2DestroyWorld( m_worldId );\n\n\tdelete m_scheduler;\n\tdelete[] m_tasks;\n}\n\nvoid Sample::CreateWorld()\n{\n\tif ( B2_IS_NON_NULL( m_worldId ) )\n\t{\n\t\tb2DestroyWorld( m_worldId );\n\t\tm_worldId = b2_nullWorldId;\n\t}\n\n\tb2WorldDef worldDef = b2DefaultWorldDef();\n\tworldDef.workerCount = m_context->workerCount;\n\tworldDef.enqueueTask = EnqueueTask;\n\tworldDef.finishTask = FinishTask;\n\tworldDef.userTaskContext = this;\n\tworldDef.enableSleep = m_context->enableSleep;\n\n\t// todo experimental\n\t// worldDef.enableContactSoftening = true;\n\n\tm_worldId = b2CreateWorld( &worldDef );\n}\n\nvoid Sample::ResetText()\n{\n\tm_textLine = m_textIncrement;\n}\n\nstruct QueryContext\n{\n\tb2Vec2 point;\n\tb2BodyId bodyId = b2_nullBodyId;\n};\n\nbool QueryCallback( b2ShapeId shapeId, void* context )\n{\n\tQueryContext* queryContext = static_cast<QueryContext*>( context );\n\n\tb2BodyId bodyId = b2Shape_GetBody( shapeId );\n\tb2BodyType bodyType = b2Body_GetType( bodyId );\n\tif ( bodyType != b2_dynamicBody )\n\t{\n\t\t// continue query\n\t\treturn true;\n\t}\n\n\tbool overlap = b2Shape_TestPoint( shapeId, queryContext->point );\n\tif ( overlap )\n\t{\n\t\t// found shape\n\t\tqueryContext->bodyId = bodyId;\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid Sample::MouseDown( b2Vec2 p, int button, int mod )\n{\n\tif ( B2_IS_NON_NULL( m_mouseJointId ) )\n\t{\n\t\treturn;\n\t}\n\n\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t{\n\t\t// Make a small box.\n\t\tb2AABB box;\n\t\tb2Vec2 d = { 0.001f, 0.001f };\n\t\tbox.lowerBound = b2Sub( p, d );\n\t\tbox.upperBound = b2Add( p, d );\n\n\t\tm_mousePoint = p;\n\n\t\t// Query the world for overlapping shapes.\n\t\tQueryContext queryContext = { p, b2_nullBodyId };\n\t\tb2World_OverlapAABB( m_worldId, box, b2DefaultQueryFilter(), QueryCallback, &queryContext );\n\n\t\tif ( B2_IS_NON_NULL( queryContext.bodyId ) )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.position = m_mousePoint;\n\t\t\tbodyDef.enableSleep = false;\n\t\t\tm_mouseBodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2MotorJointDef jointDef = b2DefaultMotorJointDef();\n\t\t\tjointDef.base.bodyIdA = m_mouseBodyId;\n\t\t\tjointDef.base.bodyIdB = queryContext.bodyId;\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( queryContext.bodyId, p );\n\t\t\tjointDef.linearHertz = 7.5f;\n\t\t\tjointDef.linearDampingRatio = 1.0f;\n\n\t\t\tb2MassData massData = b2Body_GetMassData( queryContext.bodyId );\n\t\t\tfloat g = b2Length( b2World_GetGravity( m_worldId ) );\n\t\t\tfloat mg = massData.mass * g;\n\n\t\t\tjointDef.maxSpringForce = m_mouseForceScale * mg;\n\n\t\t\tif ( massData.mass > 0.0f )\n\t\t\t{\n\t\t\t\t// This acts like angular friction\n\t\t\t\tfloat lever = sqrtf( massData.rotationalInertia / massData.mass );\n\t\t\t\tjointDef.maxVelocityTorque = 0.25f * lever * mg;\n\t\t\t}\n\n\t\t\tm_mouseJointId = b2CreateMotorJoint( m_worldId, &jointDef );\n\t\t}\n\t}\n}\n\nvoid Sample::MouseUp( b2Vec2 p, int button )\n{\n\tif ( B2_IS_NON_NULL( m_mouseJointId ) && button == GLFW_MOUSE_BUTTON_1 )\n\t{\n\t\tb2DestroyJoint( m_mouseJointId, true );\n\t\tm_mouseJointId = b2_nullJointId;\n\n\t\tb2DestroyBody( m_mouseBodyId );\n\t\tm_mouseBodyId = b2_nullBodyId;\n\t}\n}\n\nvoid Sample::MouseMove( b2Vec2 p )\n{\n\tif ( b2Joint_IsValid( m_mouseJointId ) == false )\n\t{\n\t\t// The world or attached body was destroyed.\n\t\tm_mouseJointId = b2_nullJointId;\n\t}\n\n\tm_mousePoint = p;\n}\n\nvoid Sample::DrawColoredTextLine( b2HexColor color, const char* text,... )\n{\n\tif (m_context->showUI == false)\n\t{\n\t\treturn;\n\t}\n\n\tchar buffer[256];\n\tva_list arg;\n\tva_start( arg, text );\n\tvsnprintf( buffer, 256, text, arg );\n\tva_end( arg );\n\tbuffer[255] = 0;\n\tDrawScreenString( m_draw, 5, m_textLine, color, buffer );\n\tm_textLine += m_textIncrement;\n}\n\nvoid Sample::DrawTextLine( const char* text, ... )\n{\n\tif (m_context->showUI == false)\n\t{\n\t\treturn;\n\t}\n\n\tchar buffer[256];\n\tva_list arg;\n\tva_start( arg, text );\n\tvsnprintf( buffer, 256, text, arg );\n\tva_end( arg );\n\tbuffer[255] = 0;\n\tDrawScreenString( m_draw, 5, m_textLine, b2_colorWhite, buffer );\n\tm_textLine += m_textIncrement;\n}\n\nvoid Sample::ResetProfile()\n{\n\tm_totalProfile = {};\n\tm_maxProfile = {};\n\tm_stepCount = 0;\n}\n\nvoid Sample::Step()\n{\n\tfloat timeStep = m_context->hertz > 0.0f ? 1.0f / m_context->hertz : 0.0f;\n\n\tif ( m_context->pause )\n\t{\n\t\tif ( m_context->singleStep )\n\t\t{\n\t\t\tm_context->singleStep = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttimeStep = 0.0f;\n\t\t}\n\n\t\tif ( m_context->showUI )\n\t\t{\n\t\t\tDrawTextLine( \"****PAUSED****\" );\n\t\t\tm_textLine += m_textIncrement;\n\t\t}\n\t}\n\n\tif ( B2_IS_NON_NULL( m_mouseJointId ) && b2Joint_IsValid( m_mouseJointId ) == false )\n\t{\n\t\t// The world or attached body was destroyed.\n\t\tm_mouseJointId = b2_nullJointId;\n\n\t\tif ( B2_IS_NON_NULL( m_mouseBodyId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_mouseBodyId );\n\t\t\tm_mouseBodyId = b2_nullBodyId;\n\t\t}\n\t}\n\n\tif ( B2_IS_NON_NULL( m_mouseBodyId ) && timeStep > 0.0f )\n\t{\n\t\tbool wake = true;\n\t\tb2Body_SetTargetTransform( m_mouseBodyId, { m_mousePoint, b2Rot_identity }, timeStep, wake );\n\t}\n\n\tm_context->debugDraw.drawingBounds = GetViewBounds( &m_context->camera );\n\tb2World_EnableSleeping( m_worldId, m_context->enableSleep );\n\tb2World_EnableWarmStarting( m_worldId, m_context->enableWarmStarting );\n\tb2World_EnableContinuous( m_worldId, m_context->enableContinuous );\n\n\tfor ( int i = 0; i < 1; ++i )\n\t{\n\t\tb2World_Step( m_worldId, timeStep, m_context->subStepCount );\n\t\tm_taskCount = 0;\n\t}\n\n\tb2World_Draw( m_worldId, &m_context->debugDraw );\n\n\tif ( timeStep > 0.0f )\n\t{\n\t\t++m_stepCount;\n\t}\n\n\tif ( m_context->drawCounters )\n\t{\n\t\tb2Counters s = b2World_GetCounters( m_worldId );\n\n\t\tDrawTextLine( \"bodies/shapes/contacts/joints = %d/%d/%d/%d\", s.bodyCount, s.shapeCount, s.contactCount, s.jointCount );\n\t\tDrawTextLine( \"islands/tasks = %d/%d\", s.islandCount, s.taskCount );\n\t\tDrawTextLine( \"tree height static/movable = %d/%d\", s.staticTreeHeight, s.treeHeight );\n\n\t\tint totalCount = 0;\n\t\tchar buffer[256] = { 0 };\n\t\tint colorCount = sizeof( s.colorCounts ) / sizeof( s.colorCounts[0] );\n\n\t\t// todo fix this\n\t\tint offset = snprintf( buffer, 256, \"colors: \" );\n\t\tfor ( int i = 0; i < colorCount; ++i )\n\t\t{\n\t\t\toffset += snprintf( buffer + offset, 256 - offset, \"%d/\", s.colorCounts[i] );\n\t\t\ttotalCount += s.colorCounts[i];\n\t\t}\n\t\tsnprintf( buffer + offset, 256 - offset, \"[%d]\", totalCount );\n\t\tDrawTextLine( buffer );\n\t\tDrawTextLine( \"stack allocator size = %d K\", s.stackUsed / 1024 );\n\t\tDrawTextLine( \"total allocation = %d K\", s.byteCount / 1024 );\n\t}\n\n\t// Track maximum profile times\n\t{\n\t\tb2Profile p = b2World_GetProfile( m_worldId );\n\t\tm_maxProfile.step = b2MaxFloat( m_maxProfile.step, p.step );\n\t\tm_maxProfile.pairs = b2MaxFloat( m_maxProfile.pairs, p.pairs );\n\t\tm_maxProfile.collide = b2MaxFloat( m_maxProfile.collide, p.collide );\n\t\tm_maxProfile.solve = b2MaxFloat( m_maxProfile.solve, p.solve );\n\t\tm_maxProfile.prepareStages = b2MaxFloat( m_maxProfile.prepareStages, p.prepareStages );\n\t\tm_maxProfile.solveConstraints = b2MaxFloat( m_maxProfile.solveConstraints, p.solveConstraints );\n\t\tm_maxProfile.prepareConstraints = b2MaxFloat( m_maxProfile.prepareConstraints, p.prepareConstraints );\n\t\tm_maxProfile.integrateVelocities = b2MaxFloat( m_maxProfile.integrateVelocities, p.integrateVelocities );\n\t\tm_maxProfile.warmStart = b2MaxFloat( m_maxProfile.warmStart, p.warmStart );\n\t\tm_maxProfile.solveImpulses = b2MaxFloat( m_maxProfile.solveImpulses, p.solveImpulses );\n\t\tm_maxProfile.integratePositions = b2MaxFloat( m_maxProfile.integratePositions, p.integratePositions );\n\t\tm_maxProfile.relaxImpulses = b2MaxFloat( m_maxProfile.relaxImpulses, p.relaxImpulses );\n\t\tm_maxProfile.applyRestitution = b2MaxFloat( m_maxProfile.applyRestitution, p.applyRestitution );\n\t\tm_maxProfile.storeImpulses = b2MaxFloat( m_maxProfile.storeImpulses, p.storeImpulses );\n\t\tm_maxProfile.transforms = b2MaxFloat( m_maxProfile.transforms, p.transforms );\n\t\tm_maxProfile.splitIslands = b2MaxFloat( m_maxProfile.splitIslands, p.splitIslands );\n\t\tm_maxProfile.jointEvents = b2MaxFloat( m_maxProfile.jointEvents, p.jointEvents );\n\t\tm_maxProfile.hitEvents = b2MaxFloat( m_maxProfile.hitEvents, p.hitEvents );\n\t\tm_maxProfile.refit = b2MaxFloat( m_maxProfile.refit, p.refit );\n\t\tm_maxProfile.bullets = b2MaxFloat( m_maxProfile.bullets, p.bullets );\n\t\tm_maxProfile.sleepIslands = b2MaxFloat( m_maxProfile.sleepIslands, p.sleepIslands );\n\t\tm_maxProfile.sensors = b2MaxFloat( m_maxProfile.sensors, p.sensors );\n\n\t\tm_totalProfile.step += p.step;\n\t\tm_totalProfile.pairs += p.pairs;\n\t\tm_totalProfile.collide += p.collide;\n\t\tm_totalProfile.solve += p.solve;\n\t\tm_totalProfile.prepareStages += p.prepareStages;\n\t\tm_totalProfile.solveConstraints += p.solveConstraints;\n\t\tm_totalProfile.prepareConstraints += p.prepareConstraints;\n\t\tm_totalProfile.integrateVelocities += p.integrateVelocities;\n\t\tm_totalProfile.warmStart += p.warmStart;\n\t\tm_totalProfile.solveImpulses += p.solveImpulses;\n\t\tm_totalProfile.integratePositions += p.integratePositions;\n\t\tm_totalProfile.relaxImpulses += p.relaxImpulses;\n\t\tm_totalProfile.applyRestitution += p.applyRestitution;\n\t\tm_totalProfile.storeImpulses += p.storeImpulses;\n\t\tm_totalProfile.transforms += p.transforms;\n\t\tm_totalProfile.splitIslands += p.splitIslands;\n\t\tm_totalProfile.jointEvents += p.jointEvents;\n\t\tm_totalProfile.hitEvents += p.hitEvents;\n\t\tm_totalProfile.refit += p.refit;\n\t\tm_totalProfile.bullets += p.bullets;\n\t\tm_totalProfile.sleepIslands += p.sleepIslands;\n\t\tm_totalProfile.sensors += p.sensors;\n\t}\n\n\tif ( m_context->drawProfile )\n\t{\n\t\tb2Profile p = b2World_GetProfile( m_worldId );\n\n\t\tb2Profile aveProfile = {};\n\t\tif ( m_stepCount > 0 )\n\t\t{\n\t\t\tfloat scale = 1.0f / m_stepCount;\n\t\t\taveProfile.step = scale * m_totalProfile.step;\n\t\t\taveProfile.pairs = scale * m_totalProfile.pairs;\n\t\t\taveProfile.collide = scale * m_totalProfile.collide;\n\t\t\taveProfile.solve = scale * m_totalProfile.solve;\n\t\t\taveProfile.prepareStages = scale * m_totalProfile.prepareStages;\n\t\t\taveProfile.solveConstraints = scale * m_totalProfile.solveConstraints;\n\t\t\taveProfile.prepareConstraints = scale * m_totalProfile.prepareConstraints;\n\t\t\taveProfile.integrateVelocities = scale * m_totalProfile.integrateVelocities;\n\t\t\taveProfile.warmStart = scale * m_totalProfile.warmStart;\n\t\t\taveProfile.solveImpulses = scale * m_totalProfile.solveImpulses;\n\t\t\taveProfile.integratePositions = scale * m_totalProfile.integratePositions;\n\t\t\taveProfile.relaxImpulses = scale * m_totalProfile.relaxImpulses;\n\t\t\taveProfile.applyRestitution = scale * m_totalProfile.applyRestitution;\n\t\t\taveProfile.storeImpulses = scale * m_totalProfile.storeImpulses;\n\t\t\taveProfile.transforms = scale * m_totalProfile.transforms;\n\t\t\taveProfile.splitIslands = scale * m_totalProfile.splitIslands;\n\t\t\taveProfile.jointEvents = scale * m_totalProfile.jointEvents;\n\t\t\taveProfile.hitEvents = scale * m_totalProfile.hitEvents;\n\t\t\taveProfile.refit = scale * m_totalProfile.refit;\n\t\t\taveProfile.bullets = scale * m_totalProfile.bullets;\n\t\t\taveProfile.sleepIslands = scale * m_totalProfile.sleepIslands;\n\t\t\taveProfile.sensors = scale * m_totalProfile.sensors;\n\t\t}\n\n\t\tDrawTextLine( \"step [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.step, aveProfile.step, m_maxProfile.step );\n\t\tDrawTextLine( \"pairs [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.pairs, aveProfile.pairs, m_maxProfile.pairs );\n\t\tDrawTextLine( \"collide [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.collide, aveProfile.collide, m_maxProfile.collide );\n\t\tDrawTextLine( \"solve [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.solve, aveProfile.solve, m_maxProfile.solve );\n\t\tDrawTextLine( \"> prepare tasks [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.prepareStages, aveProfile.prepareStages,\n\t\t\t\t\t  m_maxProfile.prepareStages );\n\t\tDrawTextLine( \"> solve constraints [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.solveConstraints, aveProfile.solveConstraints,\n\t\t\t\t\t  m_maxProfile.solveConstraints );\n\t\tDrawTextLine( \">> prepare constraints [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.prepareConstraints,\n\t\t\t\t\t  aveProfile.prepareConstraints, m_maxProfile.prepareConstraints );\n\t\tDrawTextLine( \">> integrate velocities [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.integrateVelocities,\n\t\t\t\t\t  aveProfile.integrateVelocities, m_maxProfile.integrateVelocities );\n\t\tDrawTextLine( \">> warm start [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.warmStart, aveProfile.warmStart,\n\t\t\t\t\t  m_maxProfile.warmStart );\n\t\tDrawTextLine( \">> solve impulses [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.solveImpulses, aveProfile.solveImpulses,\n\t\t\t\t\t  m_maxProfile.solveImpulses );\n\t\tDrawTextLine( \">> integrate positions [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.integratePositions,\n\t\t\t\t\t  aveProfile.integratePositions, m_maxProfile.integratePositions );\n\t\tDrawTextLine( \">> relax impulses [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.relaxImpulses, aveProfile.relaxImpulses,\n\t\t\t\t\t  m_maxProfile.relaxImpulses );\n\t\tDrawTextLine( \">> apply restitution [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.applyRestitution, aveProfile.applyRestitution,\n\t\t\t\t\t  m_maxProfile.applyRestitution );\n\t\tDrawTextLine( \">> store impulses [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.storeImpulses, aveProfile.storeImpulses,\n\t\t\t\t\t  m_maxProfile.storeImpulses );\n\t\tDrawTextLine( \">> split islands [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.splitIslands, aveProfile.splitIslands,\n\t\t\t\t\t  m_maxProfile.splitIslands );\n\t\tDrawTextLine( \"> update transforms [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.transforms, aveProfile.transforms,\n\t\t\t\t\t  m_maxProfile.transforms );\n\t\tDrawTextLine( \"> joint events [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.jointEvents, aveProfile.jointEvents,\n\t\t\t\t\t  m_maxProfile.jointEvents );\n\t\tDrawTextLine( \"> hit events [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.hitEvents, aveProfile.hitEvents,\n\t\t\t\t\t  m_maxProfile.hitEvents );\n\t\tDrawTextLine( \"> refit BVH [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.refit, aveProfile.refit, m_maxProfile.refit );\n\t\tDrawTextLine( \"> sleep islands [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.sleepIslands, aveProfile.sleepIslands,\n\t\t\t\t\t  m_maxProfile.sleepIslands );\n\t\tDrawTextLine( \"> bullets [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.bullets, aveProfile.bullets, m_maxProfile.bullets );\n\t\tDrawTextLine( \"sensors [ave] (max) = %5.2f [%6.2f] (%6.2f)\", p.sensors, aveProfile.sensors, m_maxProfile.sensors );\n\t}\n}\n\nvoid Sample::ShiftOrigin( b2Vec2 newOrigin )\n{\n\t// m_world->ShiftOrigin(newOrigin);\n}\n\n// Parse an SVG path element with only straight lines. Example:\n// \"M 47.625004,185.20833 H 161.39585 l 29.10417,-2.64583 26.45834,-7.9375 26.45833,-13.22917 23.81251,-21.16666 h \"\n// \"13.22916 v 44.97916 H 592.66669 V 0 h 21.16671 v 206.375 l -566.208398,-1e-5 z\"\nint Sample::ParsePath( const char* svgPath, b2Vec2 offset, b2Vec2* points, int capacity, float scale, bool reverseOrder )\n{\n\tint pointCount = 0;\n\tb2Vec2 currentPoint = {};\n\tconst char* ptr = svgPath;\n\tchar command = *ptr;\n\n\twhile ( *ptr != '\\0' )\n\t{\n\t\tif ( isdigit( *ptr ) == 0 && *ptr != '-' )\n\t\t{\n\t\t\t// note: command can be implicitly repeated\n\t\t\tcommand = *ptr;\n\n\t\t\tif ( command == 'M' || command == 'L' || command == 'H' || command == 'V' || command == 'm' || command == 'l' ||\n\t\t\t\t command == 'h' || command == 'v' )\n\t\t\t{\n\t\t\t\tptr += 2; // Skip the command character and space\n\t\t\t}\n\n\t\t\tif ( command == 'z' )\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tassert( isdigit( *ptr ) != 0 || *ptr == '-' );\n\n\t\tfloat x = 0.0f, y = 0.0f;\n\t\tswitch ( command )\n\t\t{\n\t\t\tcase 'M':\n\t\t\tcase 'L':\n\t\t\t\tif ( sscanf( ptr, \"%f,%f\", &x, &y ) == 2 )\n\t\t\t\t{\n\t\t\t\t\tcurrentPoint.x = x;\n\t\t\t\t\tcurrentPoint.y = y;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( false );\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'H':\n\t\t\t\tif ( sscanf( ptr, \"%f\", &x ) == 1 )\n\t\t\t\t{\n\t\t\t\t\tcurrentPoint.x = x;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( false );\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'V':\n\t\t\t\tif ( sscanf( ptr, \"%f\", &y ) == 1 )\n\t\t\t\t{\n\t\t\t\t\tcurrentPoint.y = y;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( false );\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'm':\n\t\t\tcase 'l':\n\t\t\t\tif ( sscanf( ptr, \"%f,%f\", &x, &y ) == 2 )\n\t\t\t\t{\n\t\t\t\t\tcurrentPoint.x += x;\n\t\t\t\t\tcurrentPoint.y += y;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( false );\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'h':\n\t\t\t\tif ( sscanf( ptr, \"%f\", &x ) == 1 )\n\t\t\t\t{\n\t\t\t\t\tcurrentPoint.x += x;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( false );\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'v':\n\t\t\t\tif ( sscanf( ptr, \"%f\", &y ) == 1 )\n\t\t\t\t{\n\t\t\t\t\tcurrentPoint.y += y;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( false );\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tassert( false );\n\t\t\t\tbreak;\n\t\t}\n\n\t\tpoints[pointCount] = { scale * ( currentPoint.x + offset.x ), -scale * ( currentPoint.y + offset.y ) };\n\t\tpointCount += 1;\n\t\tif ( pointCount == capacity )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t// Move to the next space or end of string\n\t\twhile ( *ptr != '\\0' && isspace( *ptr ) == 0 )\n\t\t{\n\t\t\tptr++;\n\t\t}\n\n\t\t// Skip contiguous spaces\n\t\twhile ( isspace( *ptr ) )\n\t\t{\n\t\t\tptr++;\n\t\t}\n\n\t\tptr += 0;\n\t}\n\n\tif ( pointCount == 0 )\n\t{\n\t\treturn 0;\n\t}\n\n\tif ( reverseOrder )\n\t{\n\t}\n\treturn pointCount;\n}\n\nSampleEntry g_sampleEntries[MAX_SAMPLES] = {};\nint g_sampleCount = 0;\n\nint RegisterSample( const char* category, const char* name, SampleCreateFcn* fcn )\n{\n\tint index = g_sampleCount;\n\tif ( index < MAX_SAMPLES )\n\t{\n\t\tg_sampleEntries[index] = { category, name, fcn };\n\t\t++g_sampleCount;\n\t\treturn index;\n\t}\n\n\treturn -1;\n}\n"
  },
  {
    "path": "samples/sample.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"box2d/id.h\"\n#include \"box2d/types.h\"\n#include \"draw.h\"\n\n#define ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) )\n\nnamespace enki\n{\nclass TaskScheduler;\n};\n\nstruct ImFont;\n\nstruct SampleContext\n{\n\tvoid Save();\n\tvoid Load();\n\n\tstruct GLFWwindow* window = nullptr;\n\tCamera camera;\n\tDraw* draw;\n\tfloat uiScale = 1.0f;\n\tfloat hertz = 60.0f;\n\tint subStepCount = 4;\n\tint workerCount = 1;\n\tbool restart = false;\n\tbool pause = false;\n\tbool singleStep = false;\n\tbool drawCounters = false;\n\tbool drawProfile = false;\n\tbool enableWarmStarting = true;\n\tbool enableContinuous = true;\n\tbool enableSleep = true;\n\tbool showUI = true;\n\n\t// These are persisted\n\tint sampleIndex = 0;\n\n\tb2DebugDraw debugDraw;\n\tImFont* regularFont;\n\tImFont* mediumFont;\n\tImFont* largeFont;\n};\n\nclass Sample\n{\npublic:\n\texplicit Sample( SampleContext* context );\n\tvirtual ~Sample();\n\n\tvoid CreateWorld( );\n\n\tvoid ResetText();\n\tvirtual void Step( );\n\tvirtual void UpdateGui()\n\t{\n\t}\n\tvirtual void Keyboard( int )\n\t{\n\t}\n\tvirtual void MouseDown( b2Vec2 p, int button, int mod );\n\tvirtual void MouseUp( b2Vec2 p, int button );\n\tvirtual void MouseMove( b2Vec2 p );\n\n\tvoid DrawTextLine( const char* text, ... );\n\tvoid DrawColoredTextLine( b2HexColor color, const char* text, ... );\n\tvoid ResetProfile();\n\tvoid ShiftOrigin( b2Vec2 newOrigin );\n\n\tstatic int ParsePath( const char* svgPath, b2Vec2 offset, b2Vec2* points, int capacity, float scale, bool reverseOrder );\n\n\tfriend class DestructionListener;\n\tfriend class BoundaryListener;\n\tfriend class ContactListener;\n\n\tstatic constexpr int m_maxTasks = 64;\n\tstatic constexpr int m_maxThreads = 64;\n\n#ifdef NDEBUG\n\tstatic constexpr bool m_isDebug = false;\n#else\n\tstatic constexpr bool m_isDebug = true;\n#endif\n\n\tSampleContext* m_context;\n\tCamera* m_camera;\n\tDraw* m_draw;\n\n\tenki::TaskScheduler* m_scheduler;\n\tclass SampleTask* m_tasks;\n\tint m_taskCount;\n\tint m_threadCount;\n\n\tb2BodyId m_mouseBodyId;\n\n\tb2WorldId m_worldId;\n\tb2JointId m_mouseJointId;\n\tb2Vec2 m_mousePoint;\n\tfloat m_mouseForceScale;\n\tint m_stepCount;\n\tb2Profile m_maxProfile;\n\tb2Profile m_totalProfile;\n\nprivate:\n\tint m_textLine;\n\tint m_textIncrement;\n};\n\ntypedef Sample* SampleCreateFcn( SampleContext* context );\n\nint RegisterSample( const char* category, const char* name, SampleCreateFcn* fcn );\n\nstruct SampleEntry\n{\n\tconst char* category;\n\tconst char* name;\n\tSampleCreateFcn* createFcn;\n};\n\n#define MAX_SAMPLES 256\nextern SampleEntry g_sampleEntries[MAX_SAMPLES];\nextern int g_sampleCount;\n"
  },
  {
    "path": "samples/sample_benchmark.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"benchmarks.h\"\n#include \"draw.h\"\n#include \"human.h\"\n#include \"random.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <imgui.h>\n#include <limits.h>\n#include <set>\n#include <stdint.h>\n#include <vector>\n\n#if defined( _MSC_VER )\n#include <intrin.h>\n#define GET_CYCLES __rdtsc()\n#else\n#define GET_CYCLES b2GetTicks()\n#endif\n\ninline bool operator<( b2BodyId a, b2BodyId b )\n{\n\tuint64_t ua = b2StoreBodyId( a );\n\tuint64_t ub = b2StoreBodyId( b );\n\treturn ua < ub;\n}\n\n// these are not accessible in some build types\n// extern \"C\" int b2_toiCalls;\n// extern \"C\" int b2_toiHitCount;\n\n// Note: resetting the scene is non-deterministic because the world uses freelists\nclass BenchmarkBarrel : public Sample\n{\npublic:\n\tenum ShapeType\n\t{\n\t\te_circleShape = 0,\n\t\te_capsuleShape,\n\t\te_mixShape,\n\t\te_compoundShape,\n\t\te_humanShape,\n\t};\n\n\tenum\n\t{\n\t\te_maxColumns = 26,\n\t\te_maxRows = 150,\n\t};\n\n\texplicit BenchmarkBarrel( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 8.0f, 53.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 2.35f;\n\t\t}\n\n\t\tm_context->debugDraw.drawJoints = false;\n\n\t\t{\n\t\t\tfloat gridSize = 1.0f;\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tfloat y = 0.0f;\n\t\t\tfloat x = -40.0f * gridSize;\n\t\t\tfor ( int i = 0; i < 81; ++i )\n\t\t\t{\n\t\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f * gridSize, 0.5f * gridSize, { x, y }, b2Rot_identity );\n\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t\t\tx += gridSize;\n\t\t\t}\n\n\t\t\ty = gridSize;\n\t\t\tx = -40.0f * gridSize;\n\t\t\tfor ( int i = 0; i < 100; ++i )\n\t\t\t{\n\t\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f * gridSize, 0.5f * gridSize, { x, y }, b2Rot_identity );\n\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t\t\ty += gridSize;\n\t\t\t}\n\n\t\t\ty = gridSize;\n\t\t\tx = 40.0f * gridSize;\n\t\t\tfor ( int i = 0; i < 100; ++i )\n\t\t\t{\n\t\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f * gridSize, 0.5f * gridSize, { x, y }, b2Rot_identity );\n\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t\t\ty += gridSize;\n\t\t\t}\n\n\t\t\tb2Segment segment = { { -800.0f, -80.0f }, { 800.0f, -80.f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tfor ( int i = 0; i < e_maxRows * e_maxColumns; ++i )\n\t\t{\n\t\t\tm_bodies[i] = b2_nullBodyId;\n\t\t}\n\n\t\tmemset( m_humans, 0, sizeof( m_humans ) );\n\n\t\tm_shapeType = e_compoundShape;\n\n\t\tCreateScene();\n\t}\n\n\tvoid CreateScene()\n\t{\n\t\tg_randomSeed = 42;\n\n\t\tfor ( int i = 0; i < e_maxRows * e_maxColumns; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_bodies[i] ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_bodies[i] );\n\t\t\t\tm_bodies[i] = b2_nullBodyId;\n\t\t\t}\n\n\t\t\tif ( m_humans[i].isSpawned )\n\t\t\t{\n\t\t\t\tDestroyHuman( m_humans + i );\n\t\t\t}\n\t\t}\n\n\t\tm_columnCount = m_isDebug ? 10 : e_maxColumns;\n\t\tm_rowCount = m_isDebug ? 40 : e_maxRows;\n\n\t\tif ( m_shapeType == e_compoundShape )\n\t\t{\n\t\t\tif constexpr ( m_isDebug == false )\n\t\t\t{\n\t\t\t\tm_columnCount = 20;\n\t\t\t}\n\t\t}\n\t\telse if ( m_shapeType == e_humanShape )\n\t\t{\n\t\t\tif constexpr ( m_isDebug )\n\t\t\t{\n\t\t\t\tm_rowCount = 5;\n\t\t\t\tm_columnCount = 10;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_rowCount = 30;\n\t\t\t}\n\t\t}\n\n\t\tfloat rad = 0.5f;\n\n\t\tfloat shift = 1.15f;\n\t\tfloat centerx = shift * m_columnCount / 2.0f;\n\t\tfloat centery = shift / 2.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t// todo eliminate this once rolling resistance is added\n\t\tif ( m_shapeType == e_mixShape )\n\t\t{\n\t\t\tbodyDef.angularDamping = 0.3f;\n\t\t}\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.friction = 0.5f;\n\n\t\tb2Capsule capsule = { { 0.0f, -0.25f }, { 0.0f, 0.25f }, rad };\n\t\tb2Circle circle = { { 0.0f, 0.0f }, rad };\n\n\t\tb2Vec2 points[3] = { { -0.1f, -0.5f }, { 0.1f, -0.5f }, { 0.0f, 0.5f } };\n\t\tb2Hull wedgeHull = b2ComputeHull( points, 3 );\n\t\tb2Polygon wedge = b2MakePolygon( &wedgeHull, 0.0f );\n\n\t\tb2Vec2 vertices[3];\n\t\tvertices[0] = { -1.0f, 0.0f };\n\t\tvertices[1] = { 0.5f, 1.0f };\n\t\tvertices[2] = { 0.0f, 2.0f };\n\t\tb2Hull hull = b2ComputeHull( vertices, 3 );\n\t\tb2Polygon left = b2MakePolygon( &hull, 0.0f );\n\n\t\tvertices[0] = { 1.0f, 0.0f };\n\t\tvertices[1] = { -0.5f, 1.0f };\n\t\tvertices[2] = { 0.0f, 2.0f };\n\t\thull = b2ComputeHull( vertices, 3 );\n\t\tb2Polygon right = b2MakePolygon( &hull, 0.0f );\n\n\t\t// b2Polygon top = b2MakeOffsetBox(0.8f, 0.2f, {0.0f, 0.8f}, 0.0f);\n\t\t// b2Polygon leftLeg = b2MakeOffsetBox(0.2f, 0.5f, {-0.6f, 0.5f}, 0.0f);\n\t\t// b2Polygon rightLeg = b2MakeOffsetBox(0.2f, 0.5f, {0.6f, 0.5f}, 0.0f);\n\n\t\tfloat side = -0.1f;\n\t\tfloat extray = 0.5f;\n\n\t\tif ( m_shapeType == e_compoundShape )\n\t\t{\n\t\t\textray = 0.25f;\n\t\t\tside = 0.25f;\n\t\t\tshift = 2.0f;\n\t\t\tcenterx = shift * m_columnCount / 2.0f - 1.0f;\n\t\t}\n\t\telse if ( m_shapeType == e_humanShape )\n\t\t{\n\t\t\textray = 0.5f;\n\t\t\tside = 0.55f;\n\t\t\tshift = 2.5f;\n\t\t\tcenterx = shift * m_columnCount / 2.0f;\n\t\t}\n\n\t\tint index = 0;\n\t\tfloat yStart = m_shapeType == e_humanShape ? 2.0f : 100.0f;\n\n\t\tfor ( int i = 0; i < m_columnCount; ++i )\n\t\t{\n\t\t\tfloat x = i * shift - centerx;\n\n\t\t\tfor ( int j = 0; j < m_rowCount; ++j )\n\t\t\t{\n\t\t\t\tfloat y = j * ( shift + extray ) + centery + yStart;\n\n\t\t\t\tbodyDef.position = { x + side, y };\n\t\t\t\tside = -side;\n\n\t\t\t\tif ( m_shapeType == e_circleShape )\n\t\t\t\t{\n\t\t\t\t\tm_bodies[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\t\tcircle.radius = RandomFloatRange( 0.25f, 0.75f );\n\t\t\t\t\tshapeDef.material.rollingResistance = 0.2f;\n\t\t\t\t\tb2CreateCircleShape( m_bodies[index], &shapeDef, &circle );\n\t\t\t\t}\n\t\t\t\telse if ( m_shapeType == e_capsuleShape )\n\t\t\t\t{\n\t\t\t\t\tm_bodies[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\t\tcapsule.radius = RandomFloatRange( 0.25f, 0.5f );\n\t\t\t\t\tfloat length = RandomFloatRange( 0.25f, 1.0f );\n\t\t\t\t\tcapsule.center1 = { 0.0f, -0.5f * length };\n\t\t\t\t\tcapsule.center2 = { 0.0f, 0.5f * length };\n\t\t\t\t\tshapeDef.material.rollingResistance = 0.2f;\n\t\t\t\t\tb2CreateCapsuleShape( m_bodies[index], &shapeDef, &capsule );\n\t\t\t\t}\n\t\t\t\telse if ( m_shapeType == e_mixShape )\n\t\t\t\t{\n\t\t\t\t\tm_bodies[index] = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\t\tint mod = index % 3;\n\t\t\t\t\tif ( mod == 0 )\n\t\t\t\t\t{\n\t\t\t\t\t\tcircle.radius = RandomFloatRange( 0.25f, 0.75f );\n\t\t\t\t\t\tb2CreateCircleShape( m_bodies[index], &shapeDef, &circle );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( mod == 1 )\n\t\t\t\t\t{\n\t\t\t\t\t\tcapsule.radius = RandomFloatRange( 0.25f, 0.5f );\n\t\t\t\t\t\tfloat length = RandomFloatRange( 0.25f, 1.0f );\n\t\t\t\t\t\tcapsule.center1 = { 0.0f, -0.5f * length };\n\t\t\t\t\t\tcapsule.center2 = { 0.0f, 0.5f * length };\n\t\t\t\t\t\tb2CreateCapsuleShape( m_bodies[index], &shapeDef, &capsule );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( mod == 2 )\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat width = RandomFloatRange( 0.1f, 0.5f );\n\t\t\t\t\t\tfloat height = RandomFloatRange( 0.5f, 0.75f );\n\t\t\t\t\t\tb2Polygon box = b2MakeBox( width, height );\n\n\t\t\t\t\t\t// Don't put a function call into a macro.\n\t\t\t\t\t\tfloat value = RandomFloatRange( -1.0f, 1.0f );\n\t\t\t\t\t\tbox.radius = 0.25f * b2MaxFloat( 0.0f, value );\n\t\t\t\t\t\tb2CreatePolygonShape( m_bodies[index], &shapeDef, &box );\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\twedge.radius = RandomFloatRange( 0.1f, 0.25f );\n\t\t\t\t\t\tb2CreatePolygonShape( m_bodies[index], &shapeDef, &wedge );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ( m_shapeType == e_compoundShape )\n\t\t\t\t{\n\t\t\t\t\tm_bodies[index] = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\t\tb2CreatePolygonShape( m_bodies[index], &shapeDef, &left );\n\t\t\t\t\tb2CreatePolygonShape( m_bodies[index], &shapeDef, &right );\n\t\t\t\t\t// b2CreatePolygonShape(m_bodies[index], &shapeDef, &top);\n\t\t\t\t\t// b2CreatePolygonShape(m_bodies[index], &shapeDef, &leftLeg);\n\t\t\t\t\t// b2CreatePolygonShape(m_bodies[index], &shapeDef, &rightLeg);\n\t\t\t\t}\n\t\t\t\telse if ( m_shapeType == e_humanShape )\n\t\t\t\t{\n\t\t\t\t\tfloat scale = 3.5f;\n\t\t\t\t\tfloat jointFriction = 0.05f;\n\t\t\t\t\tfloat jointHertz = 5.0f;\n\t\t\t\t\tfloat jointDamping = 0.5f;\n\t\t\t\t\tCreateHuman( m_humans + index, m_worldId, bodyDef.position, scale, jointFriction, jointHertz, jointDamping,\n\t\t\t\t\t\t\t\t index + 1, nullptr, false );\n\t\t\t\t}\n\n\t\t\t\tindex += 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 6.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 15.0f * fontSize, height ) );\n\t\tImGui::Begin( \"Benchmark: Barrel\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tbool changed = false;\n\t\tconst char* shapeTypes[] = { \"Circle\", \"Capsule\", \"Mix\", \"Compound\", \"Human\" };\n\n\t\tint shapeType = int( m_shapeType );\n\t\tchanged = changed || ImGui::Combo( \"Shape\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) );\n\t\tm_shapeType = ShapeType( shapeType );\n\n\t\tchanged = changed || ImGui::Button( \"Reset Scene\" );\n\n\t\tif ( changed )\n\t\t{\n\t\t\tCreateScene();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkBarrel( context );\n\t}\n\n\tb2BodyId m_bodies[e_maxRows * e_maxColumns];\n\tHuman m_humans[e_maxRows * e_maxColumns];\n\tint m_columnCount;\n\tint m_rowCount;\n\n\tShapeType m_shapeType;\n};\n\nstatic int benchmarkBarrel = RegisterSample( \"Benchmark\", \"Barrel\", BenchmarkBarrel::Create );\n\n// This is used to compare performance with Box2D v2.4\nclass BenchmarkBarrel24 : public Sample\n{\npublic:\n\texplicit BenchmarkBarrel24( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 8.0f, 53.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 2.35f;\n\t\t}\n\n\t\tfloat groundSize = 25.0f;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( groundSize, 1.2f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbodyDef.rotation = b2MakeRot( 0.5f * B2_PI );\n\t\t\tbodyDef.position = { groundSize, 2.0f * groundSize };\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tbox = b2MakeBox( 2.0f * groundSize, 1.2f );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbodyDef.position = { -groundSize, 2.0f * groundSize };\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tint32_t num = 26;\n\t\tfloat rad = 0.5f;\n\n\t\tfloat shift = rad * 2.0f;\n\t\tfloat centerx = shift * num / 2.0f;\n\t\tfloat centery = shift / 2.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.friction = 0.5f;\n\n\t\tb2Polygon cuboid = b2MakeSquare( 0.5f );\n\n\t\t// b2Polygon top = b2MakeOffsetBox(0.8f, 0.2f, {0.0f, 0.8f}, 0.0f);\n\t\t// b2Polygon leftLeg = b2MakeOffsetBox(0.2f, 0.5f, {-0.6f, 0.5f}, 0.0f);\n\t\t// b2Polygon rightLeg = b2MakeOffsetBox(0.2f, 0.5f, {0.6f, 0.5f}, 0.0f);\n\n#ifdef _DEBUG\n\t\tint numj = 5;\n#else\n\t\tint numj = 5 * num;\n#endif\n\t\tfor ( int i = 0; i < num; ++i )\n\t\t{\n\t\t\tfloat x = i * shift - centerx;\n\n\t\t\tfor ( int j = 0; j < numj; ++j )\n\t\t\t{\n\t\t\t\tfloat y = j * shift + centery + 2.0f;\n\n\t\t\t\tbodyDef.position = { x, y };\n\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &cuboid );\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkBarrel24( context );\n\t}\n};\n\nstatic int benchmarkBarrel24 = RegisterSample( \"Benchmark\", \"Barrel 2.4\", BenchmarkBarrel24::Create );\n\nclass BenchmarkTumbler : public Sample\n{\npublic:\n\texplicit BenchmarkTumbler( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 1.5f, 10.0f };\n\t\t\tm_context->camera.zoom = 15.0f;\n\t\t}\n\n\t\tCreateTumbler( m_worldId );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkTumbler( context );\n\t}\n};\n\nstatic int benchmarkTumbler = RegisterSample( \"Benchmark\", \"Tumbler\", BenchmarkTumbler::Create );\n\nclass BenchmarkWasher : public Sample\n{\npublic:\n\texplicit BenchmarkWasher( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 1.5f, 10.0f };\n\t\t\tm_context->camera.zoom = 20.0f;\n\t\t}\n\n\t\tCreateWasher( m_worldId );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkWasher( context );\n\t}\n};\n\nstatic int benchmarkWasher = RegisterSample( \"Benchmark\", \"Washer\", BenchmarkWasher::Create );\n\n// todo try removing kinematics from graph coloring\nclass BenchmarkManyTumblers : public Sample\n{\npublic:\n\texplicit BenchmarkManyTumblers( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 1.0f, -5.5 };\n\t\t\tm_context->camera.zoom = 25.0f * 3.4f;\n\t\t\tm_context->debugDraw.drawJoints = false;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tm_groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tm_rowCount = m_isDebug ? 2 : 19;\n\t\tm_columnCount = m_isDebug ? 2 : 19;\n\n\t\tm_tumblerIds = nullptr;\n\t\tm_positions = nullptr;\n\t\tm_tumblerCount = 0;\n\n\t\tm_bodyIds = nullptr;\n\t\tm_bodyCount = 0;\n\t\tm_bodyIndex = 0;\n\n\t\tm_angularSpeed = 25.0f;\n\n\t\tCreateScene();\n\t}\n\n\t~BenchmarkManyTumblers() override\n\t{\n\t\tfree( m_tumblerIds );\n\t\tfree( m_positions );\n\t\tfree( m_bodyIds );\n\t}\n\n\tvoid CreateTumbler( b2Vec2 position, int index )\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_kinematicBody;\n\t\tbodyDef.position = { position.x, position.y };\n\t\tbodyDef.angularVelocity = ( B2_PI / 180.0f ) * m_angularSpeed;\n\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\tm_tumblerIds[index] = bodyId;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 50.0f;\n\n\t\tb2Polygon polygon;\n\t\tpolygon = b2MakeOffsetBox( 0.25f, 2.0f, { 2.0f, 0.0f }, b2Rot_identity );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\tpolygon = b2MakeOffsetBox( 0.25f, 2.0f, { -2.0f, 0.0f }, b2Rot_identity );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\tpolygon = b2MakeOffsetBox( 2.0f, 0.25f, { 0.0f, 2.0f }, b2Rot_identity );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\tpolygon = b2MakeOffsetBox( 2.0f, 0.25f, { 0.0f, -2.0f }, b2Rot_identity );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t}\n\n\tvoid CreateScene()\n\t{\n\t\tfor ( int i = 0; i < m_bodyCount; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_bodyIds[i] ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t\t}\n\t\t}\n\n\t\tfor ( int i = 0; i < m_tumblerCount; ++i )\n\t\t{\n\t\t\tb2DestroyBody( m_tumblerIds[i] );\n\t\t}\n\n\t\tfree( m_tumblerIds );\n\t\tfree( m_positions );\n\n\t\tm_tumblerCount = m_rowCount * m_columnCount;\n\t\tm_tumblerIds = static_cast<b2BodyId*>( malloc( m_tumblerCount * sizeof( b2BodyId ) ) );\n\t\tm_positions = static_cast<b2Vec2*>( malloc( m_tumblerCount * sizeof( b2Vec2 ) ) );\n\n\t\tint index = 0;\n\t\tfloat x = -4.0f * m_rowCount;\n\t\tfor ( int i = 0; i < m_rowCount; ++i )\n\t\t{\n\t\t\tfloat y = -4.0f * m_columnCount;\n\t\t\tfor ( int j = 0; j < m_columnCount; ++j )\n\t\t\t{\n\t\t\t\tm_positions[index] = { x, y };\n\t\t\t\tCreateTumbler( m_positions[index], index );\n\t\t\t\t++index;\n\t\t\t\ty += 8.0f;\n\t\t\t}\n\n\t\t\tx += 8.0f;\n\t\t}\n\n\t\tfree( m_bodyIds );\n\n\t\tint bodiesPerTumbler = m_isDebug ? 8 : 50;\n\t\tm_bodyCount = bodiesPerTumbler * m_tumblerCount;\n\n\t\tm_bodyIds = static_cast<b2BodyId*>( malloc( m_bodyCount * sizeof( b2BodyId ) ) );\n\n\t\tmemset( m_bodyIds, 0, m_bodyCount * sizeof( b2BodyId ) );\n\t\tm_bodyIndex = 0;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 8.5f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 15.5f * fontSize, height ) );\n\t\tImGui::Begin( \"Benchmark: Many Tumblers\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 8.0f * fontSize );\n\n\t\tbool changed = false;\n\t\tchanged = changed || ImGui::SliderInt( \"Row Count\", &m_rowCount, 1, 32 );\n\t\tchanged = changed || ImGui::SliderInt( \"Column Count\", &m_columnCount, 1, 32 );\n\n\t\tif ( changed )\n\t\t{\n\t\t\tCreateScene();\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Speed\", &m_angularSpeed, 0.0f, 100.0f, \"%.f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < m_tumblerCount; ++i )\n\t\t\t{\n\t\t\t\tb2Body_SetAngularVelocity( m_tumblerIds[i], ( B2_PI / 180.0f ) * m_angularSpeed );\n\t\t\t\tb2Body_SetAwake( m_tumblerIds[i], true );\n\t\t\t}\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tif ( m_bodyIndex < m_bodyCount && ( m_stepCount & 0x7 ) == 0 )\n\t\t{\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2Capsule capsule = { { -0.1f, 0.0f }, { 0.1f, 0.0f }, 0.075f };\n\n\t\t\tfor ( int i = 0; i < m_tumblerCount; ++i )\n\t\t\t{\n\t\t\t\tassert( m_bodyIndex < m_bodyCount );\n\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t\tbodyDef.position = m_positions[i];\n\t\t\t\tm_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreateCapsuleShape( m_bodyIds[m_bodyIndex], &shapeDef, &capsule );\n\n\t\t\t\tm_bodyIndex += 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkManyTumblers( context );\n\t}\n\n\tb2BodyId m_groundId;\n\n\tint m_rowCount;\n\tint m_columnCount;\n\n\tb2BodyId* m_tumblerIds;\n\tb2Vec2* m_positions;\n\tint m_tumblerCount;\n\n\tb2BodyId* m_bodyIds;\n\tint m_bodyCount;\n\tint m_bodyIndex;\n\n\tfloat m_angularSpeed;\n};\n\nstatic int benchmarkManyTumblers = RegisterSample( \"Benchmark\", \"Many Tumblers\", BenchmarkManyTumblers::Create );\n\nclass BenchmarkLargePyramid : public Sample\n{\npublic:\n\texplicit BenchmarkLargePyramid( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 50.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 2.2f;\n\t\t\tm_context->enableSleep = false;\n\t\t}\n\n\t\tCreateLargePyramid( m_worldId );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkLargePyramid( context );\n\t}\n};\n\nstatic int benchmarkLargePyramid = RegisterSample( \"Benchmark\", \"Large Pyramid\", BenchmarkLargePyramid::Create );\n\nclass BenchmarkManyPyramids : public Sample\n{\npublic:\n\texplicit BenchmarkManyPyramids( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 16.0f, 110.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 5.0f;\n\t\t\tm_context->enableSleep = false;\n\t\t}\n\n\t\tCreateManyPyramids( m_worldId );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkManyPyramids( context );\n\t}\n};\n\nstatic int benchmarkManyPyramids = RegisterSample( \"Benchmark\", \"Many Pyramids\", BenchmarkManyPyramids::Create );\n\nclass BenchmarkCreateDestroy : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_maxBaseCount = 100,\n\t\te_maxBodyCount = e_maxBaseCount * ( e_maxBaseCount + 1 ) / 2\n\t};\n\n\texplicit BenchmarkCreateDestroy( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 50.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 2.2f;\n\t\t}\n\n\t\tfloat groundSize = 100.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2Polygon box = b2MakeBox( groundSize, 1.0f );\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\tfor ( int i = 0; i < e_maxBodyCount; ++i )\n\t\t{\n\t\t\tm_bodies[i] = b2_nullBodyId;\n\t\t}\n\n\t\tm_createTime = 0.0f;\n\t\tm_destroyTime = 0.0f;\n\n\t\tm_baseCount = m_isDebug ? 40 : 100;\n\t\tm_iterations = m_isDebug ? 1 : 10;\n\t\tm_bodyCount = 0;\n\t}\n\n\tvoid CreateScene()\n\t{\n\t\tuint64_t ticks = b2GetTicks();\n\n\t\tfor ( int i = 0; i < e_maxBodyCount; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_bodies[i] ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_bodies[i] );\n\t\t\t\tm_bodies[i] = b2_nullBodyId;\n\t\t\t}\n\t\t}\n\n\t\tm_destroyTime += b2GetMillisecondsAndReset( &ticks );\n\n\t\tint count = m_baseCount;\n\t\tfloat rad = 0.5f;\n\t\tfloat shift = rad * 2.0f;\n\t\tfloat centerx = shift * count / 2.0f;\n\t\tfloat centery = shift / 2.0f + 1.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.friction = 0.5f;\n\n\t\tfloat h = 0.5f;\n\t\tb2Polygon box = b2MakeRoundedBox( h, h, 0.0f );\n\n\t\tint index = 0;\n\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tfloat y = i * shift + centery;\n\n\t\t\tfor ( int j = i; j < count; ++j )\n\t\t\t{\n\t\t\t\tfloat x = 0.5f * i * shift + ( j - i ) * shift - centerx;\n\t\t\t\tbodyDef.position = { x, y };\n\n\t\t\t\tassert( index < e_maxBodyCount );\n\t\t\t\tm_bodies[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( m_bodies[index], &shapeDef, &box );\n\n\t\t\t\tindex += 1;\n\t\t\t}\n\t\t}\n\n\t\tm_createTime += b2GetMilliseconds( ticks );\n\n\t\tm_bodyCount = index;\n\n\t\tb2World_Step( m_worldId, 1.0f / 60.0f, 4 );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tm_createTime = 0.0f;\n\t\tm_destroyTime = 0.0f;\n\n\t\tfor ( int i = 0; i < m_iterations; ++i )\n\t\t{\n\t\t\tCreateScene();\n\t\t}\n\n\t\tDrawTextLine( \"total: create = %g ms, destroy = %g ms\", m_createTime, m_destroyTime );\n\n\t\tfloat createPerBody = 1000.0f * m_createTime / m_iterations / m_bodyCount;\n\t\tfloat destroyPerBody = 1000.0f * m_destroyTime / m_iterations / m_bodyCount;\n\t\tDrawTextLine( \"body: create = %g us, destroy = %g us\", createPerBody, destroyPerBody );\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkCreateDestroy( context );\n\t}\n\n\tfloat m_createTime;\n\tfloat m_destroyTime;\n\tb2BodyId m_bodies[e_maxBodyCount];\n\tint m_bodyCount;\n\tint m_baseCount;\n\tint m_iterations;\n};\n\nstatic int benchmarkCreateDestroy = RegisterSample( \"Benchmark\", \"CreateDestroy\", BenchmarkCreateDestroy::Create );\n\nclass BenchmarkSleep : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_maxBaseCount = 100,\n\t\te_maxBodyCount = e_maxBaseCount * ( e_maxBaseCount + 1 ) / 2\n\t};\n\n\texplicit BenchmarkSleep( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 50.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 2.2f;\n\t\t}\n\n\t\t{\n\t\t\tfloat groundSize = 100.0f;\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( groundSize, 1.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tm_baseCount = m_isDebug ? 40 : 100;\n\t\tm_bodyCount = 0;\n\n\t\tint count = m_baseCount;\n\t\tfloat rad = 0.5f;\n\t\tfloat shift = rad * 2.0f;\n\t\tfloat centerx = shift * count / 2.0f;\n\t\tfloat centery = shift / 2.0f + 1.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.friction = 0.5f;\n\n\t\tfloat h = 0.5f;\n\t\tb2Polygon box = b2MakeRoundedBox( h, h, 0.0f );\n\n\t\tint index = 0;\n\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tfloat y = i * shift + centery;\n\n\t\t\tfor ( int j = i; j < count; ++j )\n\t\t\t{\n\t\t\t\tfloat x = 0.5f * i * shift + ( j - i ) * shift - centerx;\n\t\t\t\tbodyDef.position = { x, y };\n\n\t\t\t\tassert( index < e_maxBodyCount );\n\t\t\t\tm_bodies[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( m_bodies[index], &shapeDef, &box );\n\n\t\t\t\tindex += 1;\n\t\t\t}\n\t\t}\n\n\t\tm_bodyCount = index;\n\n\t\tm_wakeTotal = 0.0f;\n\t\tm_sleepTotal = 0.0f;\n\t}\n\n\tvoid Step() override\n\t{\n\t\t// These operations don't show up in b2Profile\n\t\tif ( m_stepCount > 20 )\n\t\t{\n\t\t\t// Creating and destroying a joint will engage the island splitter.\n\t\t\tb2FilterJointDef jointDef = b2DefaultFilterJointDef();\n\t\t\tjointDef.base.bodyIdA = m_bodies[0];\n\t\t\tjointDef.base.bodyIdB = m_bodies[1];\n\t\t\tb2JointId jointId = b2CreateFilterJoint( m_worldId, &jointDef );\n\n\t\t\tuint64_t ticks = b2GetTicks();\n\n\t\t\t// This will wake the island\n\t\t\tb2DestroyJoint( jointId, true );\n\t\t\tm_wakeTotal += b2GetMillisecondsAndReset( &ticks );\n\n\t\t\t// Put the island back to sleep. It must be split because a constraint was removed.\n\t\t\tb2Body_SetAwake( m_bodies[0], false );\n\t\t\tm_sleepTotal += b2GetMillisecondsAndReset( &ticks );\n\n\t\t\tint count = m_stepCount - 20;\n\t\t\tDrawTextLine( \"wake ave = %g ms\", m_wakeTotal / count );\n\t\t\tDrawTextLine( \"sleep ave = %g ms\", m_sleepTotal / count );\n\t\t}\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkSleep( context );\n\t}\n\n\tb2BodyId m_bodies[e_maxBodyCount];\n\tint m_bodyCount;\n\tint m_baseCount;\n\tfloat m_wakeTotal;\n\tfloat m_sleepTotal;\n\tbool m_awake;\n};\n\nstatic int benchmarkSleep = RegisterSample( \"Benchmark\", \"Sleep\", BenchmarkSleep::Create );\n\nclass BenchmarkJointGrid : public Sample\n{\npublic:\n\texplicit BenchmarkJointGrid( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 60.0f, -57.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 2.5f;\n\t\t\tm_context->enableSleep = false;\n\t\t}\n\n\t\tCreateJointGrid( m_worldId );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkJointGrid( context );\n\t}\n};\n\nstatic int benchmarkJointGridIndex = RegisterSample( \"Benchmark\", \"Joint Grid\", BenchmarkJointGrid::Create );\n\nclass BenchmarkSmash : public Sample\n{\npublic:\n\texplicit BenchmarkSmash( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 60.0f, 6.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 1.6f;\n\t\t}\n\n\t\tCreateSmash( m_worldId );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkSmash( context );\n\t}\n};\n\nstatic int sampleSmash = RegisterSample( \"Benchmark\", \"Smash\", BenchmarkSmash::Create );\n\nclass BenchmarkCompound : public Sample\n{\npublic:\n\texplicit BenchmarkCompound( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 18.0f, 115.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 5.5f;\n\t\t}\n\n\t\tfloat grid = 1.0f;\n#ifdef NDEBUG\n\t\tint height = 200;\n\t\tint width = 200;\n#else\n\t\tint height = 100;\n\t\tint width = 100;\n#endif\n\t\t{\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tfor ( int i = 0; i < height; ++i )\n\t\t\t{\n\t\t\t\tfloat y = grid * i;\n\t\t\t\tfor ( int j = i; j < width; ++j )\n\t\t\t\t{\n\t\t\t\t\tfloat x = grid * j;\n\t\t\t\t\tb2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity );\n\t\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &square );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor ( int i = 0; i < height; ++i )\n\t\t\t{\n\t\t\t\tfloat y = grid * i;\n\t\t\t\tfor ( int j = i; j < width; ++j )\n\t\t\t\t{\n\t\t\t\t\tfloat x = -grid * j;\n\t\t\t\t\tb2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity );\n\t\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &square );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t{\n#ifdef NDEBUG\n\t\t\tint span = 20;\n\t\t\tint count = 5;\n#else\n\t\t\tint span = 5;\n\t\t\tint count = 5;\n#endif\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t// defer mass properties to avoid n-squared mass computations\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.updateBodyMass = false;\n\n\t\t\tfor ( int m = 0; m < count; ++m )\n\t\t\t{\n\t\t\t\tfloat ybody = ( 100.0f + m * span ) * grid;\n\n\t\t\t\tfor ( int n = 0; n < count; ++n )\n\t\t\t\t{\n\t\t\t\t\tfloat xbody = -0.5f * grid * count * span + n * span * grid;\n\t\t\t\t\tbodyDef.position = { xbody, ybody };\n\t\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\t\tfor ( int i = 0; i < span; ++i )\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat y = i * grid;\n\t\t\t\t\t\tfor ( int j = 0; j < span; ++j )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfloat x = j * grid;\n\t\t\t\t\t\t\tb2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity );\n\t\t\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &square );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// All shapes have been added so I can efficiently compute the mass properties.\n\t\t\t\t\tb2Body_ApplyMassFromShapes( bodyId );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkCompound( context );\n\t}\n};\n\nstatic int sampleCompound = RegisterSample( \"Benchmark\", \"Compound\", BenchmarkCompound::Create );\n\nclass BenchmarkKinematic : public Sample\n{\npublic:\n\texplicit BenchmarkKinematic( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 150.0f;\n\t\t}\n\n\t\tfloat grid = 1.0f;\n\n#ifdef NDEBUG\n\t\tint span = 100;\n#else\n\t\tint span = 20;\n#endif\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_kinematicBody;\n\t\tbodyDef.angularVelocity = 1.0f;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.filter.categoryBits = 1;\n\t\tshapeDef.filter.maskBits = 2;\n\n\t\t// defer mass properties to avoid n-squared mass computations\n\t\tshapeDef.updateBodyMass = false;\n\n\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tfor ( int i = -span; i < span; ++i )\n\t\t{\n\t\t\tfloat y = i * grid;\n\t\t\tfor ( int j = -span; j < span; ++j )\n\t\t\t{\n\t\t\t\tfloat x = j * grid;\n\t\t\t\tb2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &square );\n\t\t\t}\n\t\t}\n\n\t\t// All shapes have been added so I can efficiently compute the mass properties.\n\t\tb2Body_ApplyMassFromShapes( bodyId );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkKinematic( context );\n\t}\n};\n\nstatic int sampleKinematic = RegisterSample( \"Benchmark\", \"Kinematic\", BenchmarkKinematic::Create );\n\nenum QueryType\n{\n\te_rayCast,\n\te_circleCast,\n\te_overlap,\n};\n\nclass BenchmarkCast : public Sample\n{\npublic:\n\texplicit BenchmarkCast( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 500.0f, 500.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 21.0f;\n\t\t\t// settings.drawShapes = m_isDebug;\n\t\t}\n\n\t\tm_queryType = e_circleCast;\n\t\tm_ratio = 5.0f;\n\t\tm_grid = 1.0f;\n\t\tm_fill = 0.1f;\n\t\tm_rowCount = m_isDebug ? 100 : 1000;\n\t\tm_columnCount = m_isDebug ? 100 : 1000;\n\t\tm_minTime = 1e6f;\n\t\tm_drawIndex = 0;\n\t\tm_topDown = false;\n\t\tm_buildTime = 0.0f;\n\t\tm_radius = 0.1f;\n\n\t\tg_randomSeed = 1234;\n\t\tint sampleCount = m_isDebug ? 100 : 10000;\n\t\tm_origins.resize( sampleCount );\n\t\tm_translations.resize( sampleCount );\n\t\tfloat extent = m_rowCount * m_grid;\n\n\t\t// Pre-compute rays to avoid randomizer overhead\n\t\tfor ( int i = 0; i < sampleCount; ++i )\n\t\t{\n\t\t\tb2Vec2 rayStart = RandomVec2( 0.0f, extent );\n\t\t\tb2Vec2 rayEnd = RandomVec2( 0.0f, extent );\n\n\t\t\tm_origins[i] = rayStart;\n\t\t\tm_translations[i] = rayEnd - rayStart;\n\t\t}\n\n\t\tBuildScene();\n\t}\n\n\tvoid BuildScene()\n\t{\n\t\tg_randomSeed = 1234;\n\t\tb2DestroyWorld( m_worldId );\n\t\tb2WorldDef worldDef = b2DefaultWorldDef();\n\t\tm_worldId = b2CreateWorld( &worldDef );\n\n\t\tuint64_t ticks = b2GetTicks();\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tfloat y = 0.0f;\n\n\t\tfor ( int i = 0; i < m_rowCount; ++i )\n\t\t{\n\t\t\tfloat x = 0.0f;\n\n\t\t\tfor ( int j = 0; j < m_columnCount; ++j )\n\t\t\t{\n\t\t\t\tfloat fillTest = RandomFloatRange( 0.0f, 1.0f );\n\t\t\t\tif ( fillTest <= m_fill )\n\t\t\t\t{\n\t\t\t\t\tbodyDef.position = { x, y };\n\t\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\t\tfloat ratio = RandomFloatRange( 1.0f, m_ratio );\n\t\t\t\t\tfloat halfWidth = RandomFloatRange( 0.05f, 0.25f );\n\n\t\t\t\t\tb2Polygon box;\n\t\t\t\t\tif ( RandomFloat() > 0.0f )\n\t\t\t\t\t{\n\t\t\t\t\t\tbox = b2MakeBox( ratio * halfWidth, halfWidth );\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tbox = b2MakeBox( halfWidth, ratio * halfWidth );\n\t\t\t\t\t}\n\n\t\t\t\t\tint category = RandomIntRange( 0, 2 );\n\t\t\t\t\tshapeDef.filter.categoryBits = 1 << category;\n\t\t\t\t\tif ( category == 0 )\n\t\t\t\t\t{\n\t\t\t\t\t\tshapeDef.material.customColor = b2_colorBox2DBlue;\n\t\t\t\t\t}\n\t\t\t\t\telse if ( category == 1 )\n\t\t\t\t\t{\n\t\t\t\t\t\tshapeDef.material.customColor = b2_colorBox2DYellow;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tshapeDef.material.customColor = b2_colorBox2DGreen;\n\t\t\t\t\t}\n\n\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\t\t}\n\n\t\t\t\tx += m_grid;\n\t\t\t}\n\n\t\t\ty += m_grid;\n\t\t}\n\n\t\tif ( m_topDown )\n\t\t{\n\t\t\tb2World_RebuildStaticTree( m_worldId );\n\t\t}\n\n\t\tm_buildTime = b2GetMilliseconds( ticks );\n\t\tm_minTime = 1e6f;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 17.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 13.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Cast\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tImGui::PushItemWidth( 7.5f * fontSize );\n\n\t\tbool changed = false;\n\n\t\tconst char* queryTypes[] = { \"Ray\", \"Circle\", \"Overlap\" };\n\t\tint queryType = int( m_queryType );\n\t\tif ( ImGui::Combo( \"Query\", &queryType, queryTypes, IM_ARRAYSIZE( queryTypes ) ) )\n\t\t{\n\t\t\tm_queryType = QueryType( queryType );\n\t\t\tif ( m_queryType == e_overlap )\n\t\t\t{\n\t\t\t\tm_radius = 5.0f;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_radius = 0.1f;\n\t\t\t}\n\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderInt( \"rows\", &m_rowCount, 0, 1000, \"%d\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderInt( \"columns\", &m_columnCount, 0, 1000, \"%d\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"fill\", &m_fill, 0.0f, 1.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"grid\", &m_grid, 0.5f, 2.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"ratio\", &m_ratio, 1.0f, 10.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"top down\", &m_topDown ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::Button( \"Draw Next\" ) )\n\t\t{\n\t\t\tm_drawIndex = ( m_drawIndex + 1 ) % m_origins.size();\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\n\t\tif ( changed )\n\t\t{\n\t\t\tBuildScene();\n\t\t}\n\t}\n\n\tstruct CastResult\n\t{\n\t\tb2Vec2 point;\n\t\tfloat fraction;\n\t\tbool hit;\n\t};\n\n\tstatic float CastCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )\n\t{\n\t\tCastResult* result = (CastResult*)context;\n\t\tresult->point = point;\n\t\tresult->fraction = fraction;\n\t\tresult->hit = true;\n\t\treturn fraction;\n\t}\n\n\tstruct OverlapResult\n\t{\n\t\tb2Vec2 points[32];\n\t\tint count;\n\t};\n\n\tstatic bool OverlapCallback( b2ShapeId shapeId, void* context )\n\t{\n\t\tOverlapResult* result = (OverlapResult*)context;\n\t\tif ( result->count < 32 )\n\t\t{\n\t\t\tb2AABB aabb = b2Shape_GetAABB( shapeId );\n\t\t\tresult->points[result->count] = b2AABB_Center( aabb );\n\t\t\tresult->count += 1;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2QueryFilter filter = b2DefaultQueryFilter();\n\t\tfilter.maskBits = 1;\n\t\tint hitCount = 0;\n\t\tint nodeVisits = 0;\n\t\tint leafVisits = 0;\n\t\tfloat ms = 0.0f;\n\t\tint sampleCount = (int)m_origins.size();\n\n\t\tif ( m_queryType == e_rayCast )\n\t\t{\n\t\t\tuint64_t ticks = b2GetTicks();\n\n\t\t\tb2RayResult drawResult = {};\n\n\t\t\tfor ( int i = 0; i < sampleCount; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 origin = m_origins[i];\n\t\t\t\tb2Vec2 translation = m_translations[i];\n\n\t\t\t\tb2RayResult result = b2World_CastRayClosest( m_worldId, origin, translation, filter );\n\n\t\t\t\tif ( i == m_drawIndex )\n\t\t\t\t{\n\t\t\t\t\tdrawResult = result;\n\t\t\t\t}\n\n\t\t\t\tnodeVisits += result.nodeVisits;\n\t\t\t\tleafVisits += result.leafVisits;\n\t\t\t\thitCount += result.hit ? 1 : 0;\n\t\t\t}\n\n\t\t\tms = b2GetMilliseconds( ticks );\n\n\t\t\tm_minTime = b2MinFloat( m_minTime, ms );\n\n\t\t\tb2Vec2 p1 = m_origins[m_drawIndex];\n\t\t\tb2Vec2 p2 = p1 + m_translations[m_drawIndex];\n\t\t\tDrawLine( m_context->draw, p1, p2, b2_colorWhite );\n\t\t\tDrawPoint( m_context->draw, p1, 5.0f, b2_colorGreen );\n\t\t\tDrawPoint( m_context->draw, p2, 5.0f, b2_colorRed );\n\t\t\tif ( drawResult.hit )\n\t\t\t{\n\t\t\t\tDrawPoint( m_context->draw, drawResult.point, 5.0f, b2_colorWhite );\n\t\t\t}\n\t\t}\n\t\telse if ( m_queryType == e_circleCast )\n\t\t{\n\t\t\tuint64_t ticks = b2GetTicks();\n\n\t\t\tCastResult drawResult = {};\n\n\t\t\tfor ( int i = 0; i < sampleCount; ++i )\n\t\t\t{\n\t\t\t\tb2ShapeProxy proxy = b2MakeProxy( &m_origins[i], 1, m_radius );\n\t\t\t\tb2Vec2 translation = m_translations[i];\n\n\t\t\t\tCastResult result;\n\t\t\t\tb2TreeStats traversalResult = b2World_CastShape( m_worldId, &proxy, translation, filter, CastCallback, &result );\n\n\t\t\t\tif ( i == m_drawIndex )\n\t\t\t\t{\n\t\t\t\t\tdrawResult = result;\n\t\t\t\t}\n\n\t\t\t\tnodeVisits += traversalResult.nodeVisits;\n\t\t\t\tleafVisits += traversalResult.leafVisits;\n\t\t\t\thitCount += result.hit ? 1 : 0;\n\t\t\t}\n\n\t\t\tms = b2GetMilliseconds( ticks );\n\n\t\t\tm_minTime = b2MinFloat( m_minTime, ms );\n\n\t\t\tb2Vec2 p1 = m_origins[m_drawIndex];\n\t\t\tb2Vec2 p2 = p1 + m_translations[m_drawIndex];\n\t\t\tDrawLine( m_context->draw, p1, p2, b2_colorWhite );\n\t\t\tDrawPoint( m_context->draw, p1, 5.0f, b2_colorGreen );\n\t\t\tDrawPoint( m_context->draw, p2, 5.0f, b2_colorRed );\n\t\t\tif ( drawResult.hit )\n\t\t\t{\n\t\t\t\tb2Vec2 t = b2Lerp( p1, p2, drawResult.fraction );\n\t\t\t\tDrawCircle( m_context->draw, t, m_radius, b2_colorWhite );\n\t\t\t\tDrawPoint( m_context->draw, drawResult.point, 5.0f, b2_colorWhite );\n\t\t\t}\n\t\t}\n\t\telse if ( m_queryType == e_overlap )\n\t\t{\n\t\t\tuint64_t ticks = b2GetTicks();\n\n\t\t\tOverlapResult drawResult = {};\n\t\t\tb2Vec2 extent = { m_radius, m_radius };\n\t\t\tOverlapResult result = {};\n\n\t\t\tfor ( int i = 0; i < sampleCount; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 origin = m_origins[i];\n\t\t\t\tb2AABB aabb = { origin - extent, origin + extent };\n\n\t\t\t\tresult.count = 0;\n\t\t\t\tb2TreeStats traversalResult = b2World_OverlapAABB( m_worldId, aabb, filter, OverlapCallback, &result );\n\n\t\t\t\tif ( i == m_drawIndex )\n\t\t\t\t{\n\t\t\t\t\tdrawResult = result;\n\t\t\t\t}\n\n\t\t\t\tnodeVisits += traversalResult.nodeVisits;\n\t\t\t\tleafVisits += traversalResult.leafVisits;\n\t\t\t\thitCount += result.count;\n\t\t\t}\n\n\t\t\tms = b2GetMilliseconds( ticks );\n\n\t\t\tm_minTime = b2MinFloat( m_minTime, ms );\n\n\t\t\tb2Vec2 origin = m_origins[m_drawIndex];\n\t\t\tb2AABB aabb = { origin - extent, origin + extent };\n\n\t\t\tDrawBounds( m_context->draw, aabb, b2_colorWhite );\n\n\t\t\tfor ( int i = 0; i < drawResult.count; ++i )\n\t\t\t{\n\t\t\t\tDrawPoint( m_context->draw, drawResult.points[i], 5.0f, b2_colorHotPink );\n\t\t\t}\n\t\t}\n\n\t\tDrawTextLine( \"build time ms = %g\", m_buildTime );\n\t\tDrawTextLine( \"hit count = %d, node visits = %d, leaf visits = %d\", hitCount, nodeVisits, leafVisits );\n\t\tDrawTextLine( \"total ms = %.3f\", ms );\n\t\tDrawTextLine( \"min total ms = %.3f\", m_minTime );\n\n\t\tfloat aveRayCost = 1000.0f * m_minTime / float( sampleCount );\n\t\tDrawTextLine( \"average us = %.2f\", aveRayCost );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkCast( context );\n\t}\n\n\tQueryType m_queryType;\n\n\tstd::vector<b2Vec2> m_origins;\n\tstd::vector<b2Vec2> m_translations;\n\tfloat m_minTime;\n\tfloat m_buildTime;\n\n\tint m_rowCount, m_columnCount;\n\tint m_updateType;\n\tint m_drawIndex;\n\tfloat m_radius;\n\tfloat m_fill;\n\tfloat m_ratio;\n\tfloat m_grid;\n\tbool m_topDown;\n};\n\nstatic int sampleCast = RegisterSample( \"Benchmark\", \"Cast\", BenchmarkCast::Create );\n\nclass BenchmarkSpinner : public Sample\n{\npublic:\n\texplicit BenchmarkSpinner( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 32.0f };\n\t\t\tm_context->camera.zoom = 42.0f;\n\t\t}\n\n\t\t// b2_toiCalls = 0;\n\t\t// b2_toiHitCount = 0;\n\n\t\tCreateSpinner( m_worldId );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tif ( m_stepCount == 1000 && false )\n\t\t{\n\t\t\t// 0.1 : 46544, 25752\n\t\t\t// 0.25 : 5745, 1947\n\t\t\t// 0.5 : 2197, 660\n\t\t\tm_context->pause = true;\n\t\t}\n\n\t\t// DrawTextLine( \"toi calls, hits = %d, %d\", b2_toiCalls, b2_toiHitCount );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkSpinner( context );\n\t}\n};\n\nstatic int sampleSpinner = RegisterSample( \"Benchmark\", \"Spinner\", BenchmarkSpinner::Create );\n\nclass BenchmarkRain : public Sample\n{\npublic:\n\texplicit BenchmarkRain( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 110.0f };\n\t\t\tm_context->camera.zoom = 125.0f;\n\t\t\tm_context->enableSleep = true;\n\t\t}\n\n\t\tm_context->debugDraw.drawJoints = false;\n\n\t\tCreateRain( m_worldId );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( m_context->pause == false || m_context->singleStep == true )\n\t\t{\n\t\t\tStepRain( m_worldId, m_stepCount );\n\t\t}\n\n\t\tSample::Step();\n\n\t\tif ( m_stepCount % 1000 == 0 )\n\t\t{\n\t\t\tm_stepCount += 0;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkRain( context );\n\t}\n};\n\nstatic int benchmarkRain = RegisterSample( \"Benchmark\", \"Rain\", BenchmarkRain::Create );\n\nclass BenchmarkShapeDistance : public Sample\n{\npublic:\n\texplicit BenchmarkShapeDistance( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 3.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2Vec2 points[8] = {};\n\t\t\tb2Rot q = b2MakeRot( 2.0f * B2_PI / 8.0f );\n\t\t\tb2Vec2 p = { 0.5f, 0.0f };\n\t\t\tpoints[0] = p;\n\t\t\tfor ( int i = 1; i < 8; ++i )\n\t\t\t{\n\t\t\t\tpoints[i] = b2RotateVector( q, points[i - 1] );\n\t\t\t}\n\n\t\t\tb2Hull hull = b2ComputeHull( points, 8 );\n\t\t\tm_polygonA = b2MakePolygon( &hull, 0.0f );\n\t\t}\n\n\t\t{\n\t\t\tb2Vec2 points[8] = {};\n\t\t\tb2Rot q = b2MakeRot( 2.0f * B2_PI / 8.0f );\n\t\t\tb2Vec2 p = { 0.5f, 0.0f };\n\t\t\tpoints[0] = p;\n\t\t\tfor ( int i = 1; i < 8; ++i )\n\t\t\t{\n\t\t\t\tpoints[i] = b2RotateVector( q, points[i - 1] );\n\t\t\t}\n\n\t\t\tb2Hull hull = b2ComputeHull( points, 8 );\n\t\t\tm_polygonB = b2MakePolygon( &hull, 0.1f );\n\t\t}\n\n\t\t// todo arena\n\t\tm_transformAs = (b2Transform*)malloc( m_count * sizeof( b2Transform ) );\n\t\tm_transformBs = (b2Transform*)malloc( m_count * sizeof( b2Transform ) );\n\t\tm_outputs = (b2DistanceOutput*)calloc( m_count, sizeof( b2DistanceOutput ) );\n\n\t\tg_randomSeed = 42;\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tm_transformAs[i] = { RandomVec2( -0.1f, 0.1f ), RandomRot() };\n\t\t\tm_transformBs[i] = { RandomVec2( 0.25f, 2.0f ), RandomRot() };\n\t\t}\n\n\t\tm_drawIndex = 0;\n\t\tm_minCycles = INT_MAX;\n\t\tm_minMilliseconds = FLT_MAX;\n\t}\n\n\t~BenchmarkShapeDistance() override\n\t{\n\t\tfree( m_transformAs );\n\t\tfree( m_transformBs );\n\t\tfree( m_outputs );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 5.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 17.0f * fontSize, height ) );\n\t\tImGui::Begin( \"Benchmark: Shape Distance\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::SliderInt( \"draw index\", &m_drawIndex, 0, m_count - 1 );\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( m_context->pause == false || m_context->singleStep == true )\n\t\t{\n\t\t\tb2DistanceInput input = {};\n\t\t\tinput.proxyA = b2MakeProxy( m_polygonA.vertices, m_polygonA.count, m_polygonA.radius );\n\t\t\tinput.proxyB = b2MakeProxy( m_polygonB.vertices, m_polygonB.count, m_polygonB.radius );\n\t\t\tinput.useRadii = true;\n\t\t\tint totalIterations = 0;\n\n\t\t\tuint64_t start = b2GetTicks();\n\t\t\tuint64_t startCycles = GET_CYCLES;\n\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t{\n\t\t\t\tb2SimplexCache cache = {};\n\t\t\t\tinput.transformA = m_transformAs[i];\n\t\t\t\tinput.transformB = m_transformBs[i];\n\t\t\t\tm_outputs[i] = b2ShapeDistance( &input, &cache, nullptr, 0 );\n\t\t\t\ttotalIterations += m_outputs[i].iterations;\n\t\t\t}\n\t\t\tuint64_t endCycles = GET_CYCLES;\n\n\t\t\tfloat ms = b2GetMilliseconds( start );\n\t\t\tm_minCycles = b2MinInt( m_minCycles, int( endCycles - startCycles ) );\n\t\t\tm_minMilliseconds = b2MinFloat( m_minMilliseconds, ms );\n\n\t\t\tDrawTextLine( \"count = %d\", m_count );\n\t\t\tDrawTextLine( \"min cycles = %d\", m_minCycles );\n\t\t\tDrawTextLine( \"ave cycles = %g\", float( m_minCycles ) / float( m_count ) );\n\t\t\tDrawTextLine( \"min ms = %g, ave us = %g\", m_minMilliseconds, 1000.0f * m_minMilliseconds / float( m_count ) );\n\t\t\tDrawTextLine( \"average iterations = %g\", totalIterations / float( m_count ) );\n\t\t}\n\n\t\tb2Transform xfA = m_transformAs[m_drawIndex];\n\t\tb2Transform xfB = m_transformBs[m_drawIndex];\n\t\tb2DistanceOutput output = m_outputs[m_drawIndex];\n\t\tDrawSolidPolygon( m_context->draw, xfA, m_polygonA.vertices, m_polygonA.count, m_polygonA.radius, b2_colorBox2DGreen );\n\t\tDrawSolidPolygon( m_context->draw, xfB, m_polygonB.vertices, m_polygonB.count, m_polygonB.radius, b2_colorBox2DBlue );\n\t\tDrawLine( m_context->draw, output.pointA, output.pointB, b2_colorDimGray );\n\t\tDrawPoint( m_context->draw, output.pointA, 10.0f, b2_colorWhite );\n\t\tDrawPoint( m_context->draw, output.pointB, 10.0f, b2_colorWhite );\n\t\tDrawLine( m_context->draw, output.pointA, output.pointA + 0.5f * output.normal, b2_colorYellow );\n\t\tDrawTextLine( \"distance = %g\", output.distance );\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkShapeDistance( context );\n\t}\n\n\tstatic constexpr int m_count = m_isDebug ? 100 : 10000;\n\tb2Transform* m_transformAs;\n\tb2Transform* m_transformBs;\n\tb2DistanceOutput* m_outputs;\n\tb2Polygon m_polygonA;\n\tb2Polygon m_polygonB;\n\tfloat m_minMilliseconds;\n\tint m_drawIndex;\n\tint m_minCycles;\n};\n\nstatic int benchmarkShapeDistance = RegisterSample( \"Benchmark\", \"Shape Distance\", BenchmarkShapeDistance::Create );\n\nstruct ShapeUserData\n{\n\tint row;\n\tbool active;\n};\n\nclass BenchmarkSensor : public Sample\n{\npublic:\n\texplicit BenchmarkSensor( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 105.0f };\n\t\t\tm_context->camera.zoom = 125.0f;\n\t\t}\n\n\t\tb2World_SetCustomFilterCallback( m_worldId, FilterFcn, this );\n\n\t\tm_activeSensor.row = 0;\n\t\tm_activeSensor.active = true;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t{\n\t\t\tfloat gridSize = 3.0f;\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.isSensor = true;\n\t\t\tshapeDef.enableSensorEvents = true;\n\t\t\tshapeDef.userData = &m_activeSensor;\n\n\t\t\tfloat y = 0.0f;\n\t\t\tfloat x = -40.0f * gridSize;\n\t\t\tfor ( int i = 0; i < 81; ++i )\n\t\t\t{\n\t\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f * gridSize, 0.5f * gridSize, { x, y }, b2Rot_identity );\n\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t\t\tx += gridSize;\n\t\t\t}\n\t\t}\n\n\t\tg_randomSeed = 42;\n\n\t\tfloat shift = 5.0f;\n\t\tfloat xCenter = 0.5f * shift * m_columnCount;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.isSensor = true;\n\t\tshapeDef.enableSensorEvents = true;\n\n\t\tfloat yStart = 10.0f;\n\t\tm_filterRow = m_rowCount >> 1;\n\n\t\tfor ( int j = 0; j < m_rowCount; ++j )\n\t\t{\n\t\t\tm_passiveSensors[j].row = j;\n\t\t\tm_passiveSensors[j].active = false;\n\t\t\tshapeDef.userData = m_passiveSensors + j;\n\n\t\t\tif ( j == m_filterRow )\n\t\t\t{\n\t\t\t\tshapeDef.enableCustomFiltering = true;\n\t\t\t\tshapeDef.material.customColor = b2_colorFuchsia;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tshapeDef.enableCustomFiltering = false;\n\t\t\t\tshapeDef.material.customColor = 0;\n\t\t\t}\n\n\t\t\tfloat y = j * shift + yStart;\n\t\t\tfor ( int i = 0; i < m_columnCount; ++i )\n\t\t\t{\n\t\t\t\tfloat x = i * shift - xCenter;\n\t\t\t\tb2Polygon box = b2MakeOffsetRoundedBox( 0.5f, 0.5f, { x, y }, b2Rot_identity, 0.1f );\n\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t\t}\n\t\t}\n\n\t\tm_maxBeginCount = 0;\n\t\tm_maxEndCount = 0;\n\t\tm_lastStepCount = 0;\n\t}\n\n\tvoid CreateRow( float y )\n\t{\n\t\tfloat shift = 5.0f;\n\t\tfloat xCenter = 0.5f * shift * m_columnCount;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.gravityScale = 0.0f;\n\t\tbodyDef.linearVelocity = { 0.0f, -5.0f };\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.enableSensorEvents = true;\n\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\tfor ( int i = 0; i < m_columnCount; ++i )\n\t\t{\n\t\t\t// stagger bodies to avoid bunching up events into a single update\n\t\t\tfloat yOffset = RandomFloatRange( -1.0f, 1.0f );\n\t\t\tbodyDef.position = { shift * i - xCenter, y + yOffset };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tif ( m_stepCount == m_lastStepCount )\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tstd::set<b2BodyId> zombies;\n\n\t\tb2SensorEvents events = b2World_GetSensorEvents( m_worldId );\n\t\tfor ( int i = 0; i < events.beginCount; ++i )\n\t\t{\n\t\t\tb2SensorBeginTouchEvent* event = events.beginEvents + i;\n\n\t\t\t// shapes on begin touch are always valid\n\n\t\t\tShapeUserData* userData = static_cast<ShapeUserData*>( b2Shape_GetUserData( event->sensorShapeId ) );\n\n\t\t\tif ( userData->active )\n\t\t\t{\n\t\t\t\tzombies.emplace( b2Shape_GetBody( event->visitorShapeId ) );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Check custom filter correctness\n\t\t\t\tassert( userData->row != m_filterRow );\n\n\t\t\t\t// Modify color while overlapped with a sensor\n\t\t\t\tb2SurfaceMaterial surfaceMaterial = b2Shape_GetSurfaceMaterial( event->visitorShapeId );\n\t\t\t\tsurfaceMaterial.customColor = b2_colorLime;\n\t\t\t\tb2Shape_SetSurfaceMaterial( event->visitorShapeId, &surfaceMaterial );\n\t\t\t}\n\t\t}\n\n\t\tfor ( int i = 0; i < events.endCount; ++i )\n\t\t{\n\t\t\tb2SensorEndTouchEvent* event = events.endEvents + i;\n\n\t\t\tif ( b2Shape_IsValid( event->visitorShapeId ) == false )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Restore color to default\n\t\t\tb2SurfaceMaterial surfaceMaterial = b2Shape_GetSurfaceMaterial( event->visitorShapeId );\n\t\t\tsurfaceMaterial.customColor = 0;\n\t\t\tb2Shape_SetSurfaceMaterial( event->visitorShapeId, &surfaceMaterial );\n\t\t}\n\n\t\tfor ( b2BodyId bodyId : zombies )\n\t\t{\n\t\t\tb2DestroyBody( bodyId );\n\t\t}\n\n\t\tint delay = 0x1F;\n\n\t\tif ( ( m_stepCount & delay ) == 0 )\n\t\t{\n\t\t\tCreateRow( 10.0f + m_rowCount * 5.0f );\n\t\t}\n\n\t\tm_lastStepCount = m_stepCount;\n\n\t\tm_maxBeginCount = b2MaxInt( events.beginCount, m_maxBeginCount );\n\t\tm_maxEndCount = b2MaxInt( events.endCount, m_maxEndCount );\n\t\tDrawTextLine( \"max begin touch events = %d\", m_maxBeginCount );\n\t\tDrawTextLine( \"max end touch events = %d\", m_maxEndCount );\n\t}\n\n\tbool Filter( b2ShapeId idA, b2ShapeId idB )\n\t{\n\t\tShapeUserData* userData = nullptr;\n\t\tif ( b2Shape_IsSensor( idA ) )\n\t\t{\n\t\t\tuserData = (ShapeUserData*)b2Shape_GetUserData( idA );\n\t\t}\n\t\telse if ( b2Shape_IsSensor( idB ) )\n\t\t{\n\t\t\tuserData = (ShapeUserData*)b2Shape_GetUserData( idB );\n\t\t}\n\n\t\tif ( userData != nullptr )\n\t\t{\n\t\t\treturn userData->active == true || userData->row != m_filterRow;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tstatic bool FilterFcn( b2ShapeId idA, b2ShapeId idB, void* context )\n\t{\n\t\tBenchmarkSensor* self = (BenchmarkSensor*)context;\n\t\treturn self->Filter( idA, idB );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkSensor( context );\n\t}\n\n\tstatic constexpr int m_columnCount = 40;\n\tstatic constexpr int m_rowCount = 40;\n\tint m_maxBeginCount;\n\tint m_maxEndCount;\n\tShapeUserData m_passiveSensors[m_rowCount];\n\tShapeUserData m_activeSensor;\n\tint m_lastStepCount;\n\tint m_filterRow;\n};\n\nstatic int benchmarkSensor = RegisterSample( \"Benchmark\", \"Sensor\", BenchmarkSensor::Create );\n\n// This benchmark pushes Box2D to the limit for a large pile. It terminates once simulation is deemed to be slow.\n// The higher the body count achieved, the better.\n// Note: this benchmark stresses the sleep system more than any other benchmark. Better results are achieved if sleeping\n// is disabled.\nclass BenchmarkCapacity : public Sample\n{\npublic:\n\texplicit BenchmarkCapacity( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 150.0f };\n\t\t\tm_context->camera.zoom = 200.0f;\n\t\t}\n\n\t\tm_context->enableSleep = false;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position.y = -5.0f;\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 800.0f, 5.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tm_square = b2MakeSquare( 0.5f );\n\t\tm_done = false;\n\t\tm_reachCount = 0;\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tfloat millisecondLimit = 20.0f;\n\n\t\tb2Profile profile = b2World_GetProfile( m_worldId );\n\t\tif ( profile.step > millisecondLimit )\n\t\t{\n\t\t\tm_reachCount += 1;\n\t\t\tif ( m_reachCount > 60 )\n\t\t\t{\n\t\t\t\t// Hit the millisecond limit 60 times in a row\n\t\t\t\tm_done = true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_reachCount = 0;\n\t\t}\n\n\t\tif ( m_done == true )\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif ( ( m_stepCount & 0x1F ) != 0x1F )\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position.y = 200.0f;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tint count = 200;\n\t\tfloat x = -1.0f * count;\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tbodyDef.position.x = x;\n\t\t\tbodyDef.position.y += 0.5f;\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &m_square );\n\n\t\t\tx += 2.0f;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BenchmarkCapacity( context );\n\t}\n\n\tb2Polygon m_square;\n\tint m_reachCount;\n\tbool m_done;\n};\n\nstatic int benchmarkCapacity = RegisterSample( \"Benchmark\", \"Capacity\", BenchmarkCapacity::Create );\n"
  },
  {
    "path": "samples/sample_bodies.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"draw.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n\n#include <imgui.h>\n\nclass BodyType : public Sample\n{\npublic:\n\texplicit BodyType( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.8f, 6.4f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t}\n\n\t\tm_type = b2_staticBody;\n\t\tm_isEnabled = true;\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"ground\";\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Define attachment\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -2.0f, 3.0f };\n\t\t\tbodyDef.name = \"attach1\";\n\t\t\tm_attachmentId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 2.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 1.0f;\n\t\t\tb2CreatePolygonShape( m_attachmentId, &shapeDef, &box );\n\t\t}\n\n\t\t// Define second attachment\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = m_type;\n\t\t\tbodyDef.isEnabled = m_isEnabled;\n\t\t\tbodyDef.position = { 3.0f, 3.0f };\n\t\t\tbodyDef.name = \"attach2\";\n\t\t\tm_secondAttachmentId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 2.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 1.0f;\n\t\t\tb2CreatePolygonShape( m_secondAttachmentId, &shapeDef, &box );\n\t\t}\n\n\t\t// Define platform\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = m_type;\n\t\t\tbodyDef.isEnabled = m_isEnabled;\n\t\t\tbodyDef.position = { -4.0f, 5.0f };\n\t\t\tbodyDef.name = \"platform\";\n\t\t\tm_platformId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f, 4.0f, { 4.0f, 0.0f }, b2MakeRot( 0.5f * B2_PI ) );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 2.0f;\n\t\t\tb2CreatePolygonShape( m_platformId, &shapeDef, &box );\n\n\t\t\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\t\t\tb2Vec2 pivot = { -2.0f, 5.0f };\n\t\t\trevoluteDef.base.bodyIdA = m_attachmentId;\n\t\t\trevoluteDef.base.bodyIdB = m_platformId;\n\t\t\trevoluteDef.base.localFrameA.p = b2Body_GetLocalPoint( m_attachmentId, pivot );\n\t\t\trevoluteDef.base.localFrameB.p = b2Body_GetLocalPoint( m_platformId, pivot );\n\t\t\trevoluteDef.maxMotorTorque = 50.0f;\n\t\t\trevoluteDef.enableMotor = true;\n\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\n\t\t\tpivot = { 3.0f, 5.0f };\n\t\t\trevoluteDef.base.bodyIdA = m_secondAttachmentId;\n\t\t\trevoluteDef.base.bodyIdB = m_platformId;\n\t\t\trevoluteDef.base.localFrameA.p = b2Body_GetLocalPoint( m_secondAttachmentId, pivot );\n\t\t\trevoluteDef.base.localFrameB.p = b2Body_GetLocalPoint( m_platformId, pivot );\n\t\t\trevoluteDef.maxMotorTorque = 50.0f;\n\t\t\trevoluteDef.enableMotor = true;\n\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\n\t\t\tb2PrismaticJointDef prismaticDef = b2DefaultPrismaticJointDef();\n\t\t\tb2Vec2 anchor = { 0.0f, 5.0f };\n\t\t\tprismaticDef.base.bodyIdA = groundId;\n\t\t\tprismaticDef.base.bodyIdB = m_platformId;\n\t\t\tprismaticDef.base.localFrameA.p = b2Body_GetLocalPoint( groundId, anchor );\n\t\t\tprismaticDef.base.localFrameB.p = b2Body_GetLocalPoint( m_platformId, anchor );\n\t\t\tprismaticDef.maxMotorForce = 1000.0f;\n\t\t\tprismaticDef.motorSpeed = 0.0f;\n\t\t\tprismaticDef.enableMotor = true;\n\t\t\tprismaticDef.lowerTranslation = -10.0f;\n\t\t\tprismaticDef.upperTranslation = 10.0f;\n\t\t\tprismaticDef.enableLimit = true;\n\n\t\t\tb2CreatePrismaticJoint( m_worldId, &prismaticDef );\n\n\t\t\tm_speed = 3.0f;\n\t\t}\n\n\t\t// Create a payload\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -3.0f, 8.0f };\n\t\t\tbodyDef.name = \"crate1\";\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 0.75f, 0.75f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 2.0f;\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t// Create a second payload\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = m_type;\n\t\t\tbodyDef.isEnabled = m_isEnabled;\n\t\t\tbodyDef.position = { 2.0f, 8.0f };\n\t\t\tbodyDef.name = \"crate2\";\n\t\t\tm_secondPayloadId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 0.75f, 0.75f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 2.0f;\n\n\t\t\tb2CreatePolygonShape( m_secondPayloadId, &shapeDef, &box );\n\t\t}\n\n\t\t// Create a separate body on the ground\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = m_type;\n\t\t\tbodyDef.isEnabled = m_isEnabled;\n\t\t\tbodyDef.position = { 8.0f, 0.2f };\n\t\t\tbodyDef.name = \"debris\";\n\t\t\tm_touchingBodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Capsule capsule = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, 0.25f };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 2.0f;\n\n\t\t\tb2CreateCapsuleShape( m_touchingBodyId, &shapeDef, &capsule );\n\t\t}\n\n\t\t// Create a separate body on the ground\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_staticBody;\n\t\t\tbodyDef.isEnabled = m_isEnabled;\n\t\t\tbodyDef.position = { 8.5f, 0.2f };\n\t\t\tbodyDef.name = \"debris\";\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Capsule capsule = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, 0.5f };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\t\t}\n\n\t\t// Create a separate floating body\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = m_type;\n\t\t\tbodyDef.isEnabled = m_isEnabled;\n\t\t\tbodyDef.position = { -8.0f, 12.0f };\n\t\t\tbodyDef.gravityScale = 0.0f;\n\t\t\tbodyDef.name = \"floater\";\n\t\t\tm_floatingBodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Circle circle = { { 0.0f, 0.5f }, 0.25f };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 2.0f;\n\n\t\t\tb2CreateCircleShape( m_floatingBodyId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 11.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 9.0f * fontSize, height ) );\n\t\tImGui::Begin( \"Body Type\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::RadioButton( \"Static\", m_type == b2_staticBody ) )\n\t\t{\n\t\t\tm_type = b2_staticBody;\n\t\t\tb2Body_SetType( m_platformId, b2_staticBody );\n\t\t\tb2Body_SetType( m_secondAttachmentId, b2_staticBody );\n\t\t\tb2Body_SetType( m_secondPayloadId, b2_staticBody );\n\t\t\tb2Body_SetType( m_touchingBodyId, b2_staticBody );\n\t\t\tb2Body_SetType( m_floatingBodyId, b2_staticBody );\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Kinematic\", m_type == b2_kinematicBody ) )\n\t\t{\n\t\t\tm_type = b2_kinematicBody;\n\t\t\tb2Body_SetType( m_platformId, b2_kinematicBody );\n\t\t\tb2Body_SetLinearVelocity( m_platformId, { -m_speed, 0.0f } );\n\t\t\tb2Body_SetAngularVelocity( m_platformId, 0.0f );\n\t\t\t\n\t\t\tb2Body_SetType( m_secondAttachmentId, b2_kinematicBody );\n\t\t\tb2Body_SetLinearVelocity(m_secondAttachmentId, b2Vec2_zero);\n\t\t\tb2Body_SetAngularVelocity(m_secondAttachmentId, 0.0f);\n\t\t\t\n\t\t\tb2Body_SetType( m_secondPayloadId, b2_kinematicBody );\n\t\t\tb2Body_SetType( m_touchingBodyId, b2_kinematicBody );\n\t\t\tb2Body_SetType( m_floatingBodyId, b2_kinematicBody );\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Dynamic\", m_type == b2_dynamicBody ) )\n\t\t{\n\t\t\tm_type = b2_dynamicBody;\n\t\t\tb2Body_SetType( m_platformId, b2_dynamicBody );\n\t\t\tb2Body_SetType( m_secondAttachmentId, b2_dynamicBody );\n\t\t\tb2Body_SetType( m_secondPayloadId, b2_dynamicBody );\n\t\t\tb2Body_SetType( m_touchingBodyId, b2_dynamicBody );\n\t\t\tb2Body_SetType( m_floatingBodyId, b2_dynamicBody );\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Enable\", &m_isEnabled ) )\n\t\t{\n\t\t\tif ( m_isEnabled )\n\t\t\t{\n\t\t\t\tb2Body_Enable( m_attachmentId );\n\t\t\t\tb2Body_Enable( m_secondPayloadId );\n\t\t\t\tb2Body_Enable( m_floatingBodyId );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2Body_Disable( m_attachmentId );\n\t\t\t\tb2Body_Disable( m_secondPayloadId );\n\t\t\t\tb2Body_Disable( m_floatingBodyId );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\t// Drive the kinematic body.\n\t\tif ( m_type == b2_kinematicBody )\n\t\t{\n\t\t\tb2Vec2 p = b2Body_GetPosition( m_platformId );\n\t\t\tb2Vec2 v = b2Body_GetLinearVelocity( m_platformId );\n\n\t\t\tif ( ( p.x < -14.0f && v.x < 0.0f ) || ( p.x > 6.0f && v.x > 0.0f ) )\n\t\t\t{\n\t\t\t\tv.x = -v.x;\n\t\t\t\tb2Body_SetLinearVelocity( m_platformId, v );\n\t\t\t}\n\t\t}\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BodyType( context );\n\t}\n\n\tb2BodyId m_attachmentId;\n\tb2BodyId m_secondAttachmentId;\n\tb2BodyId m_platformId;\n\tb2BodyId m_secondPayloadId;\n\tb2BodyId m_touchingBodyId;\n\tb2BodyId m_floatingBodyId;\n\tb2BodyType m_type;\n\tfloat m_speed;\n\tbool m_isEnabled;\n};\n\nstatic int sampleBodyType = RegisterSample( \"Bodies\", \"Body Type\", BodyType::Create );\n\nfloat FrictionCallback( float, uint64_t, float, uint64_t )\n{\n\treturn 0.1f;\n}\n\nfloat RestitutionCallback( float, uint64_t, float, uint64_t )\n{\n\treturn 1.0f;\n}\n\nclass Weeble : public Sample\n{\npublic:\n\texplicit Weeble( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 2.3f, 10.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t}\n\n\t\t// Test friction and restitution callbacks\n\t\tb2World_SetFrictionCallback( m_worldId, FrictionCallback );\n\t\tb2World_SetRestitutionCallback( m_worldId, RestitutionCallback );\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Build weeble\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 0.0f, 3.0f };\n\t\t\tbodyDef.rotation = b2MakeRot( 0.25f * B2_PI );\n\t\t\tm_weebleId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 1.0f };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateCapsuleShape( m_weebleId, &shapeDef, &capsule );\n\n\t\t\tfloat mass = b2Body_GetMass( m_weebleId );\n\t\t\tfloat inertiaTensor = b2Body_GetRotationalInertia( m_weebleId );\n\n\t\t\tfloat offset = 1.5f;\n\n\t\t\t// See: https://en.wikipedia.org/wiki/Parallel_axis_theorem\n\t\t\tinertiaTensor += mass * offset * offset;\n\n\t\t\tb2MassData massData = { mass, { 0.0f, -offset }, inertiaTensor };\n\t\t\tb2Body_SetMassData( m_weebleId, massData );\n\t\t}\n\n\t\tm_explosionPosition = { 0.0f, 0.0f };\n\t\tm_explosionRadius = 2.0f;\n\t\tm_explosionMagnitude = 8.0f;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 120.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );\n\t\tImGui::Begin( \"Weeble\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\t\tif ( ImGui::Button( \"Teleport\" ) )\n\t\t{\n\t\t\tb2Body_SetTransform( m_weebleId, { 0.0f, 5.0f }, b2MakeRot( 0.95 * B2_PI ) );\n\t\t}\n\n\t\tif ( ImGui::Button( \"Explode\" ) )\n\t\t{\n\t\t\tb2ExplosionDef def = b2DefaultExplosionDef();\n\t\t\tdef.position = m_explosionPosition;\n\t\t\tdef.radius = m_explosionRadius;\n\t\t\tdef.falloff = 0.1f;\n\t\t\tdef.impulsePerLength = m_explosionMagnitude;\n\t\t\tb2World_Explode( m_worldId, &def );\n\t\t}\n\t\tImGui::PushItemWidth( 100.0f );\n\n\t\tImGui::SliderFloat( \"Magnitude\", &m_explosionMagnitude, -100.0f, 100.0f, \"%.1f\" );\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tDrawCircle( m_context->draw, m_explosionPosition, m_explosionRadius, b2_colorAzure );\n\n\t\t// This shows how to get the velocity of a point on a body\n\t\tb2Vec2 localPoint = { 0.0f, 2.0f };\n\t\tb2Vec2 worldPoint = b2Body_GetWorldPoint( m_weebleId, localPoint );\n\n\t\tb2Vec2 v1 = b2Body_GetLocalPointVelocity( m_weebleId, localPoint );\n\t\tb2Vec2 v2 = b2Body_GetWorldPointVelocity( m_weebleId, worldPoint );\n\n\t\tb2Vec2 offset = { 0.05f, 0.0f };\n\t\tDrawLine(m_context->draw,  worldPoint, worldPoint + v1, b2_colorRed );\n\t\tDrawLine(m_context->draw,  worldPoint + offset, worldPoint + v2 + offset, b2_colorGreen );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Weeble( context );\n\t}\n\n\tb2BodyId m_weebleId;\n\tb2Vec2 m_explosionPosition;\n\tfloat m_explosionRadius;\n\tfloat m_explosionMagnitude;\n};\n\nstatic int sampleWeeble = RegisterSample( \"Bodies\", \"Weeble\", Weeble::Create );\n\nclass Sleep : public Sample\n{\npublic:\n\texplicit Sleep( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 3.0f, 50.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 2.2f;\n\t\t}\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.enableSensorEvents = true;\n\t\t\tm_groundShapeId = b2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Sleeping body with sensors\n\t\tfor ( int i = 0; i < 2; ++i )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -4.0f, 3.0f + 2.0f * i };\n\t\t\tbodyDef.isAwake = false;\n\t\t\tbodyDef.enableSleep = true;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Capsule capsule = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, 0.75f };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\n\t\t\tshapeDef.isSensor = true;\n\t\t\tshapeDef.enableSensorEvents = true;\n\t\t\tcapsule.radius = 1.0f;\n\t\t\tm_sensorIds[i] = b2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\t\t\tm_sensorTouching[i] = false;\n\t\t}\n\n\t\t// Sleeping body but sleep is disabled\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 0.0f, 3.0f };\n\t\t\tbodyDef.isAwake = false;\n\t\t\tbodyDef.enableSleep = false;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Circle circle = { { 1.0f, 1.0f }, 1.0f };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\n\t\t// Awake body and sleep is disabled\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 5.0f, 3.0f };\n\t\t\tbodyDef.isAwake = true;\n\t\t\tbodyDef.enableSleep = false;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 1.0f, 1.0f, { 0.0f, 1.0f }, b2MakeRot( 0.25f * B2_PI ) );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t// A sleeping body to test waking on collision\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 5.0f, 1.0f };\n\t\t\tbodyDef.isAwake = false;\n\t\t\tbodyDef.enableSleep = true;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeSquare( 1.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t// A long pendulum\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 0.0f, 100.0f };\n\t\t\tbodyDef.angularDamping = 0.5f;\n\t\t\tbodyDef.sleepThreshold = 0.05f;\n\t\t\tm_pendulumId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Capsule capsule = { { 0.0f, 0.0f }, { 90.0f, 0.0f }, 0.25f };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateCapsuleShape( m_pendulumId, &shapeDef, &capsule );\n\n\t\t\tb2Vec2 pivot = bodyDef.position;\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_pendulumId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\t// A sleeping body to test waking on contact destroyed\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -10.0f, 1.0f };\n\t\t\tbodyDef.isAwake = false;\n\t\t\tbodyDef.enableSleep = true;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeSquare( 1.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\tm_staticBodyId = b2_nullBodyId;\n\t}\n\n\tvoid ToggleInvoker()\n\t{\n\t\tif ( B2_IS_NULL( m_staticBodyId ) )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { -10.5f, 3.0f };\n\t\t\tm_staticBodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 2.0f, 0.1f, { 0.0f, 0.0f }, b2MakeRot( 0.25f * B2_PI ) );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.invokeContactCreation = true;\n\t\t\tb2CreatePolygonShape( m_staticBodyId, &shapeDef, &box );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb2DestroyBody( m_staticBodyId );\n\t\t\tm_staticBodyId = b2_nullBodyId;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 160.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\t\tImGui::Begin( \"Sleep\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tImGui::PushItemWidth( 120.0f );\n\n\t\tImGui::Text( \"Pendulum Tuning\" );\n\n\t\tfloat sleepVelocity = b2Body_GetSleepThreshold( m_pendulumId );\n\t\tif ( ImGui::SliderFloat( \"sleep velocity\", &sleepVelocity, 0.0f, 1.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tb2Body_SetSleepThreshold( m_pendulumId, sleepVelocity );\n\t\t\tb2Body_SetAwake( m_pendulumId, true );\n\t\t}\n\n\t\tfloat angularDamping = b2Body_GetAngularDamping( m_pendulumId );\n\t\tif ( ImGui::SliderFloat( \"angular damping\", &angularDamping, 0.0f, 2.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tb2Body_SetAngularDamping( m_pendulumId, angularDamping );\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\n\t\tImGui::Separator();\n\n\t\tif ( B2_IS_NULL( m_staticBodyId ) )\n\t\t{\n\t\t\tif ( ImGui::Button( \"Create\" ) )\n\t\t\t{\n\t\t\t\tToggleInvoker();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ( ImGui::Button( \"Destroy\" ) )\n\t\t\t{\n\t\t\t\tToggleInvoker();\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\t// Detect sensors touching the ground\n\t\tb2SensorEvents sensorEvents = b2World_GetSensorEvents( m_worldId );\n\n\t\tfor ( int i = 0; i < sensorEvents.beginCount; ++i )\n\t\t{\n\t\t\tb2SensorBeginTouchEvent* event = sensorEvents.beginEvents + i;\n\t\t\tif ( B2_ID_EQUALS( event->visitorShapeId, m_groundShapeId ) )\n\t\t\t{\n\t\t\t\tif ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[0] ) )\n\t\t\t\t{\n\t\t\t\t\tm_sensorTouching[0] = true;\n\t\t\t\t}\n\t\t\t\telse if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[1] ) )\n\t\t\t\t{\n\t\t\t\t\tm_sensorTouching[1] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor ( int i = 0; i < sensorEvents.endCount; ++i )\n\t\t{\n\t\t\tb2SensorEndTouchEvent* event = sensorEvents.endEvents + i;\n\t\t\tif ( B2_ID_EQUALS( event->visitorShapeId, m_groundShapeId ) )\n\t\t\t{\n\t\t\t\tif ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[0] ) )\n\t\t\t\t{\n\t\t\t\t\tm_sensorTouching[0] = false;\n\t\t\t\t}\n\t\t\t\telse if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[1] ) )\n\t\t\t\t{\n\t\t\t\t\tm_sensorTouching[1] = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor ( int i = 0; i < 2; ++i )\n\t\t{\n\t\t\tDrawTextLine( \"sensor touch %d = %s\", i, m_sensorTouching[i] ? \"true\" : \"false\" );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Sleep( context );\n\t}\n\n\tb2BodyId m_pendulumId;\n\tb2BodyId m_staticBodyId;\n\tb2ShapeId m_groundShapeId;\n\tb2ShapeId m_sensorIds[2];\n\tbool m_sensorTouching[2];\n};\n\nstatic int sampleSleep = RegisterSample( \"Bodies\", \"Sleep\", Sleep::Create );\n\nclass BadBody : public Sample\n{\npublic:\n\texplicit BadBody( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 2.3f, 10.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t}\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Build a bad body\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 0.0f, 3.0f };\n\t\t\tbodyDef.angularVelocity = 0.5f;\n\t\t\tbodyDef.rotation = b2MakeRot( 0.25f * B2_PI );\n\n\t\t\tm_badBodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 1.0f };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\t// density set to zero intentionally to create a bad body\n\t\t\tshapeDef.density = 0.0f;\n\t\t\tb2CreateCapsuleShape( m_badBodyId, &shapeDef, &capsule );\n\t\t}\n\n\t\t// Build a normal body\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 2.0f, 3.0f };\n\t\t\tbodyDef.rotation = b2MakeRot( 0.25f * B2_PI );\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 1.0f };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tDrawTextLine(\"A bad body is a dynamic body with no mass and behaves like a kinematic body.\" );\n\t\tDrawTextLine( \"Bad bodies are considered invalid and a user bug. Behavior is not guaranteed.\" );\n\n\t\t// For science\n\t\tb2Body_ApplyForceToCenter( m_badBodyId, { 0.0f, 10.0f }, true );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BadBody( context );\n\t}\n\n\tb2BodyId m_badBodyId;\n};\n\nstatic int sampleBadBody = RegisterSample( \"Bodies\", \"Bad\", BadBody::Create );\n\n// This shows how to set the initial angular velocity to get a specific movement.\nclass Pivot : public Sample\n{\npublic:\n\texplicit Pivot( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.8f, 6.4f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t}\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Create a separate body on the ground\n\t\t{\n\t\t\tb2Vec2 v = { 5.0f, 0.0f };\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 0.0f, 3.0f };\n\t\t\tbodyDef.gravityScale = 1.0f;\n\t\t\tbodyDef.linearVelocity = v;\n\n\t\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tm_lever = 3.0f;\n\t\t\tb2Vec2 r = { 0.0f, -m_lever };\n\n\t\t\tfloat omega = b2Cross( v, r ) / b2Dot( r, r );\n\t\t\tb2Body_SetAngularVelocity( m_bodyId, omega );\n\n\t\t\tb2Polygon box = b2MakeBox( 0.1f, m_lever );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2Vec2 v = b2Body_GetLinearVelocity( m_bodyId );\n\t\tfloat omega = b2Body_GetAngularVelocity( m_bodyId );\n\t\tb2Vec2 r = b2Body_GetWorldVector( m_bodyId, { 0.0f, -m_lever } );\n\n\t\tb2Vec2 vp = v + b2CrossSV( omega, r );\n\t\tDrawTextLine( \"pivot velocity = (%g, %g)\", vp.x, vp.y );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Pivot( context );\n\t}\n\n\tb2BodyId m_bodyId;\n\tfloat m_lever;\n};\n\nstatic int samplePivot = RegisterSample( \"Bodies\", \"Pivot\", Pivot::Create );\n\n// This shows how to drive a kinematic body to reach a target\nclass Kinematic : public Sample\n{\npublic:\n\texplicit Kinematic( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 4.0f;\n\t\t}\n\n\t\tm_amplitude = 2.0f;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.position.x = 2.0f * m_amplitude;\n\n\t\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 0.1f, 1.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\t\t}\n\n\t\tm_time = 0.0f;\n\t}\n\n\tvoid Step() override\n\t{\n\t\tfloat timeStep = m_context->hertz > 0.0f ? 1.0f / m_context->hertz : 0.0f;\n\t\tif ( m_context->pause && m_context->singleStep == false )\n\t\t{\n\t\t\ttimeStep = 0.0f;\n\t\t}\n\n\t\tif ( timeStep > 0.0f )\n\t\t{\n\t\t\tb2Vec2 point = {\n\t\t\t\t.x = 2.0f * m_amplitude * cosf( m_time ),\n\t\t\t\t.y = m_amplitude * sinf( 2.0f * m_time ),\n\t\t\t};\n\t\t\tb2Rot rotation = b2MakeRot( 2.0f * m_time );\n\n\t\t\tb2Vec2 axis = b2RotateVector( rotation, { 0.0f, 1.0f } );\n\t\t\tDrawLine( m_context->draw, point - 0.5f * axis, point + 0.5f * axis, b2_colorPlum );\n\t\t\tDrawPoint( m_context->draw, point, 10.0f, b2_colorPlum );\n\n\t\t\tbool wake = true;\n\t\t\tb2Body_SetTargetTransform( m_bodyId, { point, rotation }, timeStep, wake );\n\t\t}\n\n\t\tSample::Step();\n\n\t\tm_time += timeStep;\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Kinematic( context );\n\t}\n\n\tb2BodyId m_bodyId;\n\tfloat m_amplitude;\n\tfloat m_time;\n};\n\nstatic int sampleKinematic = RegisterSample( \"Bodies\", \"Kinematic\", Kinematic::Create );\n\n// Motion locking can be a bit squishy\nclass MixedLocks : public Sample\n{\npublic:\n\texplicit MixedLocks( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 2.5f };\n\t\t\tm_context->camera.zoom = 3.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -40.0, 0.0f }, { 40.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2Polygon box = b2MakeSquare( 0.5f );\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 2.0f, 1.0f };\n\t\t\tbodyDef.name = \"static\";\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 1.0f, 1.0f };\n\t\t\tbodyDef.name = \"free\";\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 1.0f, 3.0f };\n\t\t\tbodyDef.name = \"free\";\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -1.0f, 1.0f };\n\t\t\tbodyDef.motionLocks.angularZ = true;\n\t\t\tbodyDef.name = \"angular z\";\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -2.0f, 2.0f };\n\t\t\tbodyDef.motionLocks.linearX = true;\n\t\t\tbodyDef.name = \"linear x\";\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -1.0f, 2.5f };\n\t\t\tbodyDef.motionLocks.linearY = true;\n\t\t\tbodyDef.motionLocks.angularZ = true;\n\t\t\tbodyDef.name = \"lin y ang z\";\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 0.0f, 1.0f };\n\t\t\tbodyDef.motionLocks.linearX = true;\n\t\t\tbodyDef.motionLocks.linearY = true;\n\t\t\tbodyDef.motionLocks.angularZ = true;\n\t\t\tbodyDef.name = \"full\";\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new MixedLocks( context );\n\t}\n};\n\nstatic int sampleMixedLocks = RegisterSample( \"Bodies\", \"Mixed Locks\", MixedLocks::Create );\n\nclass SetVelocity : public Sample\n{\npublic:\n\texplicit SetVelocity( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 2.5f };\n\t\t\tm_context->camera.zoom = 3.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, -0.25f };\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeBox( 20.0f, 0.25f );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeSquare( 0.5f );\n\t\t\tbodyDef.position = { 0.0f, 0.5f };\n\t\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2Body_SetLinearVelocity( m_bodyId, { 0.0f, -20.0f } );\n\n\t\tb2Vec2 position = b2Body_GetPosition( m_bodyId );\n\t\tDrawTextLine( \"(x, y) = (%.2g, %.2g)\", position.x, position.y );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SetVelocity( context );\n\t}\n\n\tb2BodyId m_bodyId;\n};\n\nstatic int sampleSetVelocity = RegisterSample( \"Bodies\", \"Set Velocity\", SetVelocity::Create );\n\nclass WakeTouching : public Sample\n{\npublic:\n\texplicit WakeTouching( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 4.0f };\n\t\t\tm_context->camera.zoom = 8.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tm_groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateSegmentShape( m_groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2Polygon box = b2MakeBox( 0.5f, 0.5f );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tfloat x = -1.0f * ( m_count - 1 );\n\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tbodyDef.position = { x, 4.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\tx += 2.0f;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 5.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 10.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Wake Touching\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Button( \"Wake Touching\" ) )\n\t\t{\n\t\t\tb2Body_WakeTouching( m_groundId );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new WakeTouching( context );\n\t}\n\n\tstatic constexpr int m_count = 10;\n\n\tb2BodyId m_groundId;\n};\n\nstatic int sampleWakeTouching = RegisterSample( \"Bodies\", \"Wake Touching\", WakeTouching::Create );\n"
  },
  {
    "path": "samples/sample_character.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"draw.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n#include <imgui.h>\n\nstruct ShapeUserData\n{\n\tfloat maxPush;\n\tbool clipVelocity;\n};\n\nenum CollisionBits : uint64_t\n{\n\tStaticBit = 0x0001,\n\tMoverBit = 0x0002,\n\tDynamicBit = 0x0004,\n\tDebrisBit = 0x0008,\n\n\tAllBits = ~0u,\n};\n\nenum PogoShape\n{\n\tPogoPoint,\n\tPogoCircle,\n\tPogoSegment\n};\n\nstruct CastResult\n{\n\tb2Vec2 point;\n\tb2Vec2 normal;\n\tb2BodyId bodyId;\n\tfloat fraction;\n\tbool hit;\n};\n\nstatic float CastCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )\n{\n\tCastResult* result = (CastResult*)context;\n\tresult->point = point;\n\tresult->normal = normal;\n\tresult->bodyId = b2Shape_GetBody( shapeId );\n\tresult->fraction = fraction;\n\tresult->hit = true;\n\treturn fraction;\n}\n\nclass Mover : public Sample\n{\npublic:\n\texplicit Mover( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( context->restart == false )\n\t\t{\n\t\t\tm_camera->center = { 20.0f, 9.0f };\n\t\t\tm_camera->zoom = 10.0f;\n\t\t}\n\n\t\tcontext->debugDraw.drawJoints = false;\n\t\tm_transform = { { 2.0f, 8.0f }, b2Rot_identity };\n\t\tm_velocity = { 0.0f, 0.0f };\n\t\tm_capsule = { { 0.0f, -0.5f }, { 0.0f, 0.5f }, 0.3f };\n\n\t\tb2BodyId groundId1;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, 0.0f };\n\t\t\tgroundId1 = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tconst char* path =\n\t\t\t\t\"M 2.6458333,201.08333 H 293.68751 v -47.625 h -2.64584 l -10.58333,7.9375 -13.22916,7.9375 -13.24648,5.29167 \"\n\t\t\t\t\"-31.73269,7.9375 -21.16667,2.64583 -23.8125,10.58333 H 142.875 v -5.29167 h -5.29166 v 5.29167 H 119.0625 v \"\n\t\t\t\t\"-2.64583 h -2.64583 v -2.64584 h -2.64584 v -2.64583 H 111.125 v -2.64583 H 84.666668 v -2.64583 h -5.291666 v \"\n\t\t\t\t\"-2.64584 h -5.291667 v -2.64583 H 68.791668 V 174.625 h -5.291666 v -2.64584 H 52.916669 L 39.6875,177.27083 H \"\n\t\t\t\t\"34.395833 L 23.8125,185.20833 H 15.875 L 5.2916669,187.85416 V 153.45833 H 2.6458333 v 47.625\";\n\n\t\t\tb2Vec2 points[64];\n\n\t\t\tb2Vec2 offset = { -50.0f, -200.0f };\n\t\t\tfloat scale = 0.2f;\n\n\t\t\tint count = ParsePath( path, offset, points, 64, scale, false );\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = count;\n\t\t\tchainDef.isLoop = true;\n\n\t\t\tb2CreateChain( groundId1, &chainDef );\n\t\t}\n\n\t\tb2BodyId groundId2;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 98.0f, 0.0f };\n\t\t\tgroundId2 = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tconst char* path =\n\t\t\t\t\"M 2.6458333,201.08333 H 293.68751 l 0,-23.8125 h -23.8125 l 21.16667,21.16667 h -23.8125 l -39.68751,-13.22917 \"\n\t\t\t\t\"-26.45833,7.9375 -23.8125,2.64583 h -13.22917 l -0.0575,2.64584 h -5.29166 v -2.64583 l -7.86855,-1e-5 \"\n\t\t\t\t\"-0.0114,-2.64583 h -2.64583 l -2.64583,2.64584 h -7.9375 l -2.64584,2.64583 -2.58891,-2.64584 h -13.28609 v \"\n\t\t\t\t\"-2.64583 h -2.64583 v -2.64584 l -5.29167,1e-5 v -2.64583 h -2.64583 v -2.64583 l -5.29167,-1e-5 v -2.64583 h \"\n\t\t\t\t\"-2.64583 v -2.64584 h -5.291667 v -2.64583 H 92.60417 V 174.625 h -5.291667 v -2.64584 l -34.395835,1e-5 \"\n\t\t\t\t\"-7.9375,-2.64584 -7.9375,-2.64583 -5.291667,-5.29167 H 21.166667 L 13.229167,158.75 5.2916668,153.45833 H \"\n\t\t\t\t\"2.6458334 l -10e-8,47.625\";\n\n\t\t\tb2Vec2 points[64];\n\n\t\t\tb2Vec2 offset = { 0.0f, -200.0f };\n\t\t\tfloat scale = 0.2f;\n\n\t\t\tint count = ParsePath( path, offset, points, 64, scale, false );\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = count;\n\t\t\tchainDef.isLoop = true;\n\n\t\t\tb2CreateChain( groundId2, &chainDef );\n\t\t}\n\n\t\t{\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 0.125f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.maxMotorTorque = 10.0f;\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.hertz = 3.0f;\n\t\t\tjointDef.dampingRatio = 0.8f;\n\t\t\tjointDef.enableSpring = true;\n\n\t\t\tfloat xBase = 48.7f;\n\t\t\tfloat yBase = 9.2f;\n\t\t\tint count = 50;\n\t\t\tb2BodyId prevBodyId = groundId1;\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t\tbodyDef.position = { xBase + 0.5f + 1.0f * i, yBase };\n\t\t\t\tbodyDef.angularDamping = 0.2f;\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\t\tb2Vec2 pivot = { xBase + 1.0f * i, yBase };\n\t\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\t\tprevBodyId = bodyId;\n\t\t\t}\n\n\t\t\tb2Vec2 pivot = { xBase + 1.0f * count, yBase };\n\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\tjointDef.base.bodyIdB = groundId2;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 32.0f, 4.5f };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tm_friendlyShape.maxPush = 0.025f;\n\t\t\tm_friendlyShape.clipVelocity = false;\n\n\t\t\tshapeDef.filter = { MoverBit, AllBits, 0 };\n\t\t\tshapeDef.userData = &m_friendlyShape;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &m_capsule );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 7.0f, 7.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter = { DebrisBit, AllBits, 0 };\n\t\t\tshapeDef.material.restitution = 0.7f;\n\t\t\tshapeDef.material.rollingResistance = 0.2f;\n\n\t\t\tb2Circle circle = { b2Vec2_zero, 0.3f };\n\t\t\tm_ballId = b2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.position = { m_elevatorBase.x, m_elevatorBase.y - m_elevatorAmplitude };\n\t\t\tm_elevatorId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tm_elevatorShape = {\n\t\t\t\t.maxPush = 0.1f,\n\t\t\t\t.clipVelocity = true,\n\t\t\t};\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter = { DynamicBit, AllBits, 0 };\n\t\t\tshapeDef.userData = &m_elevatorShape;\n\n\t\t\tb2Polygon box = b2MakeBox( 2.0f, 0.1f );\n\t\t\tb2CreatePolygonShape( m_elevatorId, &shapeDef, &box );\n\t\t}\n\n\t\tm_totalIterations = 0;\n\t\tm_pogoVelocity = 0.0f;\n\t\tm_onGround = false;\n\t\tm_jumpReleased = true;\n\t\tm_lockCamera = true;\n\t\tm_planeCount = 0;\n\t\tm_time = 0.0f;\n\t}\n\n\t// https://github.com/id-Software/Quake/blob/master/QW/client/pmove.c#L390\n\tvoid SolveMove( float timeStep, float throttle )\n\t{\n\t\t// Friction\n\t\tfloat speed = b2Length( m_velocity );\n\t\tif ( speed < m_minSpeed )\n\t\t{\n\t\t\tm_velocity.x = 0.0f;\n\t\t\tm_velocity.y = 0.0f;\n\t\t}\n\t\telse if ( m_onGround )\n\t\t{\n\t\t\t// Linear damping above stopSpeed and fixed reduction below stopSpeed\n\t\t\tfloat control = speed < m_stopSpeed ? m_stopSpeed : speed;\n\n\t\t\t// friction has units of 1/time\n\t\t\tfloat drop = control * m_friction * timeStep;\n\t\t\tfloat newSpeed = b2MaxFloat( 0.0f, speed - drop );\n\t\t\tm_velocity *= newSpeed / speed;\n\t\t}\n\n\t\tb2Vec2 desiredVelocity = { m_maxSpeed * throttle, 0.0f };\n\t\tfloat desiredSpeed;\n\t\tb2Vec2 desiredDirection = b2GetLengthAndNormalize( &desiredSpeed, desiredVelocity );\n\n\t\tif ( desiredSpeed > m_maxSpeed )\n\t\t{\n\t\t\tdesiredSpeed = m_maxSpeed;\n\t\t}\n\n\t\tif ( m_onGround )\n\t\t{\n\t\t\tm_velocity.y = 0.0f;\n\t\t}\n\n\t\t// Accelerate\n\t\tfloat currentSpeed = b2Dot( m_velocity, desiredDirection );\n\t\tfloat addSpeed = desiredSpeed - currentSpeed;\n\t\tif ( addSpeed > 0.0f )\n\t\t{\n\t\t\tfloat steer = m_onGround ? 1.0f : m_airSteer;\n\t\t\tfloat accelSpeed = steer * m_accelerate * m_maxSpeed * timeStep;\n\t\t\tif ( accelSpeed > addSpeed )\n\t\t\t{\n\t\t\t\taccelSpeed = addSpeed;\n\t\t\t}\n\n\t\t\tm_velocity += accelSpeed * desiredDirection;\n\t\t}\n\n\t\tm_velocity.y -= m_gravity * timeStep;\n\n\t\tfloat pogoRestLength = 3.0f * m_capsule.radius;\n\t\tfloat rayLength = pogoRestLength + m_capsule.radius;\n\t\tb2Vec2 origin = b2TransformPoint( m_transform, m_capsule.center1 );\n\t\tb2Circle circle = { origin, 0.5f * m_capsule.radius };\n\t\tb2Vec2 segmentOffset = { 0.75f * m_capsule.radius, 0.0f };\n\t\tb2Segment segment = {\n\t\t\t.point1 = origin - segmentOffset,\n\t\t\t.point2 = origin + segmentOffset,\n\t\t};\n\n\t\tb2ShapeProxy proxy = {};\n\t\tb2Vec2 translation;\n\t\tb2QueryFilter pogoFilter = { MoverBit, StaticBit | DynamicBit };\n\t\tCastResult castResult = {};\n\n\t\tif ( m_pogoShape == PogoPoint )\n\t\t{\n\t\t\tproxy = b2MakeProxy( &origin, 1, 0.0f );\n\t\t\ttranslation = { 0.0f, -rayLength };\n\t\t}\n\t\telse if ( m_pogoShape == PogoCircle )\n\t\t{\n\t\t\tproxy = b2MakeProxy( &origin, 1, circle.radius );\n\t\t\ttranslation = { 0.0f, -rayLength + circle.radius };\n\t\t}\n\t\telse\n\t\t{\n\t\t\tproxy = b2MakeProxy( &segment.point1, 2, 0.0f );\n\t\t\ttranslation = { 0.0f, -rayLength };\n\t\t}\n\n\t\tb2World_CastShape( m_worldId, &proxy, translation, pogoFilter, CastCallback, &castResult );\n\n\t\t// Avoid snapping to ground if still going up\n\t\tif ( m_onGround == false )\n\t\t{\n\t\t\tm_onGround = castResult.hit && m_velocity.y <= 0.01f;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_onGround = castResult.hit;\n\t\t}\n\n\t\tif ( castResult.hit == false )\n\t\t{\n\t\t\tm_pogoVelocity = 0.0f;\n\n\t\t\tb2Vec2 delta = translation;\n\t\t\tDrawLine( m_draw, origin, origin + delta, b2_colorGray );\n\n\t\t\tif ( m_pogoShape == PogoPoint )\n\t\t\t{\n\t\t\t\tDrawPoint( m_draw, origin + delta, 10.0f, b2_colorGray );\n\t\t\t}\n\t\t\telse if ( m_pogoShape == PogoCircle )\n\t\t\t{\n\t\t\t\tDrawCircle( m_draw, origin + delta, circle.radius, b2_colorGray );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDrawLine( m_draw, segment.point1 + delta, segment.point2 + delta, b2_colorGray );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfloat pogoCurrentLength = castResult.fraction * rayLength;\n\n\t\t\tfloat offset = pogoCurrentLength - pogoRestLength;\n\t\t\tm_pogoVelocity = b2SpringDamper( m_pogoHertz, m_pogoDampingRatio, offset, m_pogoVelocity, timeStep );\n\n\t\t\tb2Vec2 delta = castResult.fraction * translation;\n\t\t\tDrawLine( m_draw, origin, origin + delta, b2_colorGray );\n\n\t\t\tif ( m_pogoShape == PogoPoint )\n\t\t\t{\n\t\t\t\tDrawPoint( m_draw, origin + delta, 10.0f, b2_colorPlum );\n\t\t\t}\n\t\t\telse if ( m_pogoShape == PogoCircle )\n\t\t\t{\n\t\t\t\tDrawCircle( m_draw, origin + delta, circle.radius, b2_colorPlum );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDrawLine( m_draw, segment.point1 + delta, segment.point2 + delta, b2_colorPlum );\n\t\t\t}\n\n\t\t\tb2Body_ApplyForce( castResult.bodyId, { 0.0f, -50.0f }, castResult.point, true );\n\t\t}\n\n\t\tb2Vec2 target = m_transform.p + timeStep * m_velocity + timeStep * m_pogoVelocity * b2Vec2{ 0.0f, 1.0f };\n\n\t\t// Mover overlap filter\n\t\tb2QueryFilter collideFilter = { MoverBit, StaticBit | DynamicBit | MoverBit };\n\n\t\t// Movers don't sweep against other movers, allows for soft collision\n\t\tb2QueryFilter castFilter = { MoverBit, StaticBit | DynamicBit };\n\n\t\tm_totalIterations = 0;\n\t\tfloat tolerance = 0.01f;\n\n\t\tfor ( int iteration = 0; iteration < 5; ++iteration )\n\t\t{\n\t\t\tm_planeCount = 0;\n\n\t\t\tb2Capsule mover;\n\t\t\tmover.center1 = b2TransformPoint( m_transform, m_capsule.center1 );\n\t\t\tmover.center2 = b2TransformPoint( m_transform, m_capsule.center2 );\n\t\t\tmover.radius = m_capsule.radius;\n\n\t\t\tb2World_CollideMover( m_worldId, &mover, collideFilter, PlaneResultFcn, this );\n\t\t\tb2PlaneSolverResult result = b2SolvePlanes( target - m_transform.p, m_planes, m_planeCount );\n\n\t\t\tm_totalIterations += result.iterationCount;\n\n\t\t\tfloat fraction = b2World_CastMover( m_worldId, &mover, result.translation, castFilter );\n\n\t\t\tb2Vec2 delta = fraction * result.translation;\n\t\t\tm_transform.p += delta;\n\n\t\t\tif ( b2LengthSquared( delta ) < tolerance * tolerance )\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tm_velocity = b2ClipVector( m_velocity, m_planes, m_planeCount );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 350.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 25.0f ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 340.0f, height ) );\n\n\t\tImGui::Begin( \"Mover\", nullptr, 0 );\n\n\t\tImGui::PushItemWidth( 240.0f );\n\n\t\tImGui::SliderFloat( \"Jump Speed\", &m_jumpSpeed, 0.0f, 40.0f, \"%.0f\" );\n\t\tImGui::SliderFloat( \"Min Speed\", &m_minSpeed, 0.0f, 1.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"Max Speed\", &m_maxSpeed, 0.0f, 20.0f, \"%.0f\" );\n\t\tImGui::SliderFloat( \"Stop Speed\", &m_stopSpeed, 0.0f, 10.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"Accelerate\", &m_accelerate, 0.0f, 100.0f, \"%.0f\" );\n\t\tImGui::SliderFloat( \"Friction\", &m_friction, 0.0f, 10.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"Gravity\", &m_gravity, 0.0f, 100.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"Air Steer\", &m_airSteer, 0.0f, 1.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"Pogo Hertz\", &m_pogoHertz, 0.0f, 30.0f, \"%.0f\" );\n\t\tImGui::SliderFloat( \"Pogo Damping\", &m_pogoDampingRatio, 0.0f, 4.0f, \"%.1f\" );\n\n\t\tImGui::PopItemWidth();\n\n\t\tImGui::Separator();\n\n\t\tImGui::Text( \"Pogo Shape\" );\n\t\tImGui::RadioButton( \"Point\", &m_pogoShape, PogoPoint );\n\t\tImGui::SameLine();\n\t\tImGui::RadioButton( \"Circle\", &m_pogoShape, PogoCircle );\n\t\tImGui::SameLine();\n\t\tImGui::RadioButton( \"Segment\", &m_pogoShape, PogoSegment );\n\n\t\tImGui::Checkbox( \"Lock Camera\", &m_lockCamera );\n\n\t\tImGui::End();\n\t}\n\n\tstatic bool PlaneResultFcn( b2ShapeId shapeId, const b2PlaneResult* planeResult, void* context )\n\t{\n\t\tassert( planeResult->hit == true );\n\n\t\tMover* self = static_cast<Mover*>( context );\n\t\tfloat maxPush = FLT_MAX;\n\t\tbool clipVelocity = true;\n\t\tShapeUserData* userData = static_cast<ShapeUserData*>( (void*)b2Shape_GetUserData( shapeId ) );\n\t\tif ( userData != nullptr )\n\t\t{\n\t\t\tmaxPush = userData->maxPush;\n\t\t\tclipVelocity = userData->clipVelocity;\n\t\t}\n\n\t\tif ( self->m_planeCount < m_planeCapacity )\n\t\t{\n\t\t\tassert( b2IsValidPlane( planeResult->plane ) );\n\t\t\tself->m_planes[self->m_planeCount] = { planeResult->plane, maxPush, 0.0f, clipVelocity };\n\t\t\tself->m_planeCount += 1;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tstatic bool Kick( b2ShapeId shapeId, void* context )\n\t{\n\t\tMover* self = (Mover*)context;\n\t\tb2BodyId bodyId = b2Shape_GetBody( shapeId );\n\t\tb2BodyType type = b2Body_GetType( bodyId );\n\n\t\tif ( type != b2_dynamicBody )\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tb2Vec2 center = b2Body_GetWorldCenterOfMass( bodyId );\n\t\tb2Vec2 direction = b2Normalize( center - self->m_transform.p );\n\t\tb2Vec2 impulse = b2Vec2{ 2.0f * direction.x, 2.0f };\n\t\tb2Body_ApplyLinearImpulseToCenter( bodyId, impulse, true );\n\n\t\treturn true;\n\t}\n\n\tvoid Keyboard( int key ) override\n\t{\n\t\tif ( key == 'K' )\n\t\t{\n\t\t\tb2Vec2 point = b2TransformPoint( m_transform, { 0.0f, m_capsule.center1.y - 3.0f * m_capsule.radius } );\n\t\t\tb2Circle circle = { point, 0.5f };\n\t\t\tb2ShapeProxy proxy = b2MakeProxy( &circle.center, 1, circle.radius );\n\t\t\tb2QueryFilter filter = { MoverBit, DebrisBit };\n\t\t\tb2World_OverlapShape( m_worldId, &proxy, filter, Kick, this );\n\t\t\tDrawCircle( m_draw, circle.center, circle.radius, b2_colorGoldenRod );\n\t\t}\n\n\t\tSample::Keyboard( key );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tbool pause = false;\n\t\tif ( m_context->pause )\n\t\t{\n\t\t\tpause = m_context->singleStep != true;\n\t\t}\n\n\t\tfloat timeStep = m_context->hertz > 0.0f ? 1.0f / m_context->hertz : 0.0f;\n\t\tif ( pause )\n\t\t{\n\t\t\ttimeStep = 0.0f;\n\t\t}\n\n\t\tif ( timeStep > 0.0f )\n\t\t{\n\t\t\tb2Vec2 point = {\n\t\t\t\t.x = m_elevatorBase.x,\n\t\t\t\t.y = m_elevatorAmplitude * cosf( 1.0f * m_time + B2_PI ) + m_elevatorBase.y,\n\t\t\t};\n\n\t\t\tbool wake = true;\n\t\t\tb2Body_SetTargetTransform( m_elevatorId, { point, b2Rot_identity }, timeStep, wake );\n\t\t}\n\n\t\tm_time += timeStep;\n\n\t\tSample::Step();\n\n\t\tif ( pause == false )\n\t\t{\n\t\t\tfloat throttle = 0.0f;\n\n\t\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_A ) )\n\t\t\t{\n\t\t\t\tthrottle -= 1.0f;\n\t\t\t}\n\n\t\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_D ) )\n\t\t\t{\n\t\t\t\tthrottle += 1.0f;\n\t\t\t}\n\n\t\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_SPACE ) )\n\t\t\t{\n\t\t\t\tif ( m_onGround == true && m_jumpReleased )\n\t\t\t\t{\n\t\t\t\t\tm_velocity.y = m_jumpSpeed;\n\t\t\t\t\tm_onGround = false;\n\t\t\t\t\tm_jumpReleased = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_jumpReleased = true;\n\t\t\t}\n\n\t\t\tSolveMove( timeStep, throttle );\n\t\t}\n\n\t\tint count = m_planeCount;\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tb2Plane plane = m_planes[i].plane;\n\t\t\tb2Vec2 p1 = m_transform.p + ( plane.offset - m_capsule.radius ) * plane.normal;\n\t\t\tb2Vec2 p2 = p1 + 0.1f * plane.normal;\n\t\t\tDrawPoint( m_draw, p1, 5.0f, b2_colorYellow );\n\t\t\tDrawLine( m_draw, p1, p2, b2_colorYellow );\n\t\t}\n\n\t\tb2Vec2 p1 = b2TransformPoint( m_transform, m_capsule.center1 );\n\t\tb2Vec2 p2 = b2TransformPoint( m_transform, m_capsule.center2 );\n\n\t\tb2HexColor color = m_onGround ? b2_colorOrange : b2_colorAquamarine;\n\t\tDrawSolidCapsule( m_draw, p1, p2, m_capsule.radius, color );\n\t\tDrawLine( m_draw, m_transform.p, m_transform.p + m_velocity, b2_colorPurple );\n\n\t\tb2Vec2 p = m_transform.p;\n\t\tDrawTextLine( \"position %.2f %.2f\", p.x, p.y );\n\t\tDrawTextLine( \"velocity %.2f %.2f\", m_velocity.x, m_velocity.y );\n\t\tDrawTextLine( \"iterations %d\", m_totalIterations );\n\n\t\tif ( m_lockCamera )\n\t\t{\n\t\t\tm_camera->center.x = m_transform.p.x;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Mover( context );\n\t}\n\n\tstatic constexpr int m_planeCapacity = 8;\n\tstatic constexpr b2Vec2 m_elevatorBase = { 112.0f, 10.0f };\n\tstatic constexpr float m_elevatorAmplitude = 4.0f;\n\n\tfloat m_jumpSpeed = 10.0f;\n\tfloat m_maxSpeed = 6.0f;\n\tfloat m_minSpeed = 0.1f;\n\tfloat m_stopSpeed = 3.0f;\n\tfloat m_accelerate = 20.0f;\n\tfloat m_airSteer = 0.2f;\n\tfloat m_friction = 8.0f;\n\tfloat m_gravity = 30.0f;\n\tfloat m_pogoHertz = 5.0f;\n\tfloat m_pogoDampingRatio = 0.8f;\n\n\tint m_pogoShape = PogoSegment;\n\tb2Transform m_transform;\n\tb2Vec2 m_velocity;\n\tb2Capsule m_capsule;\n\tb2BodyId m_elevatorId;\n\tb2ShapeId m_ballId;\n\tShapeUserData m_friendlyShape;\n\tShapeUserData m_elevatorShape;\n\tb2CollisionPlane m_planes[m_planeCapacity] = {};\n\tint m_planeCount;\n\tint m_totalIterations;\n\tfloat m_pogoVelocity;\n\tfloat m_time;\n\tbool m_onGround;\n\tbool m_jumpReleased;\n\tbool m_lockCamera;\n};\n\nstatic int sampleMover = RegisterSample( \"Character\", \"Mover\", Mover::Create );\n\n// todo mover experiment wip\n#if 0\nenum MoverMode\n{\n\te_walkingMode,\n\te_fallingMode,\n\te_modeCount,\n};\n\nclass Mover2 : public Sample\n{\npublic:\n\texplicit Mover( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 20.0f, 9.0f };\n\t\t\tm_context->camera.zoom = 10.0f;\n\t\t}\n\n\t\tsettings.drawJoints = false;\n\t\tm_transform = { { 2.0f, 8.0f }, b2Rot_identity };\n\t\tm_velocity = { 0.0f, 0.0f };\n\t\tm_capsule = { { 0.0f, -0.5f * m_capsuleInteriorLength }, { 0.0f, 0.5f * m_capsuleInteriorLength }, m_capsuleRadius };\n\n\t\tb2BodyId groundId1;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, 0.0f };\n\t\t\tgroundId1 = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tconst char* path =\n\t\t\t\t\"M -34.395834,201.08333 H 293.68751 v -47.625 h -2.64584 l -10.58333,7.9375 -13.22916,7.9375 -13.24648,5.29167 \"\n\t\t\t\t\"-31.73269,7.9375 -21.16667,2.64583 -23.8125,10.58333 H 142.875 v -5.29167 h -5.29166 v 5.29167 H 119.0625 v \"\n\t\t\t\t\"-2.64583 h -2.64583 v -2.64584 h -2.64584 v -2.64583 H 111.125 v -2.64583 H 84.666668 v -2.64583 h -5.291666 v \"\n\t\t\t\t\"-2.64584 h -5.291667 v -2.64583 H 68.791668 V 174.625 h -5.291666 v -2.64584 H 52.916669 L 39.6875,177.27083 h \"\n\t\t\t\t\"-5.291667 l -7.937499,5.29167 H 15.875001 l -47.625002,-50.27083 v -26.45834 h -2.645834 l 10e-7,95.25\";\n\n\t\t\tb2Vec2 points[64];\n\n\t\t\tb2Vec2 offset = { -50.0f, -200.0f };\n\t\t\tfloat scale = 0.2f;\n\n\t\t\tint count = ParsePath( path, offset, points, 64, scale, false );\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = count;\n\t\t\tchainDef.isLoop = true;\n\n\t\t\tb2CreateChain( groundId1, &chainDef );\n\t\t}\n\n\t\tb2BodyId groundId2;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 98.0f, 0.0f };\n\t\t\tgroundId2 = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tconst char* path =\n\t\t\t\t\"M 2.6458333,201.08333 H 293.68751 l 0,-23.8125 h -23.8125 l 21.16667,21.16667 h -23.8125 l -39.68751,-13.22917 \"\n\t\t\t\t\"-26.45833,7.9375 -23.8125,2.64583 h -13.22917 l -0.0575,2.64584 h -5.29166 v -2.64583 l -7.86855,-1e-5 \"\n\t\t\t\t\"-0.0114,-2.64583 h -2.64583 l -2.64583,2.64584 h -7.9375 l -2.64584,2.64583 -2.58891,-2.64584 h -13.28609 v \"\n\t\t\t\t\"-2.64583 h -2.64583 v -2.64584 l -5.29167,1e-5 v -2.64583 h -2.64583 v -2.64583 l -5.29167,-1e-5 v -2.64583 h \"\n\t\t\t\t\"-2.64583 v -2.64584 h -5.291667 v -2.64583 H 92.60417 V 174.625 h -5.291667 v -2.64584 l -34.395835,1e-5 \"\n\t\t\t\t\"-7.9375,-2.64584 -7.9375,-2.64583 -5.291667,-5.29167 H 21.166667 L 13.229167,158.75 5.2916668,153.45833 H \"\n\t\t\t\t\"2.6458334 l -10e-8,47.625\";\n\n\t\t\tb2Vec2 points[64];\n\n\t\t\tb2Vec2 offset = { 0.0f, -200.0f };\n\t\t\tfloat scale = 0.2f;\n\n\t\t\tint count = ParsePath( path, offset, points, 64, scale, false );\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = count;\n\t\t\tchainDef.isLoop = true;\n\n\t\t\tb2CreateChain( groundId2, &chainDef );\n\t\t}\n\n\t\t{\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 0.125f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.maxMotorTorque = 10.0f;\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.hertz = 3.0f;\n\t\t\tjointDef.dampingRatio = 0.8f;\n\t\t\tjointDef.enableSpring = true;\n\n\t\t\tfloat xBase = 48.7f;\n\t\t\tfloat yBase = 9.2f;\n\t\t\tint count = 50;\n\t\t\tb2BodyId prevBodyId = groundId1;\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t\tbodyDef.position = { xBase + 0.5f + 1.0f * i, yBase };\n\t\t\t\tbodyDef.angularDamping = 0.2f;\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\t\tb2Vec2 pivot = { xBase + 1.0f * i, yBase };\n\t\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\t\tprevBodyId = bodyId;\n\t\t\t}\n\n\t\t\tb2Vec2 pivot = { xBase + 1.0f * count, yBase };\n\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\tjointDef.base.bodyIdB = groundId2;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 32.0f, 4.5f };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tm_friendlyShape.maxPush = 0.025f;\n\t\t\tm_friendlyShape.clipVelocity = false;\n\n\t\t\tshapeDef.filter = { MoverBit, AllBits, 0 };\n\t\t\tshapeDef.userData = &m_friendlyShape;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &m_capsule );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 7.0f, 7.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter = { DebrisBit, AllBits, 0 };\n\t\t\tshapeDef.material.restitution = 0.7f;\n\t\t\tshapeDef.material.rollingResistance = 0.2f;\n\n\t\t\tb2Circle circle = { b2Vec2_zero, 0.3f };\n\t\t\tm_ballId = b2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.position = { m_elevatorBase.x, m_elevatorBase.y - m_elevatorAmplitude };\n\t\t\tm_elevatorId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tm_elevatorShape = {\n\t\t\t\t.maxPush = 0.1f,\n\t\t\t\t.clipVelocity = true,\n\t\t\t};\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter = { DynamicBit, AllBits, 0 };\n\t\t\tshapeDef.userData = &m_elevatorShape;\n\n\t\t\tb2Polygon box = b2MakeBox( 2.0f, 0.1f );\n\t\t\tb2CreatePolygonShape( m_elevatorId, &shapeDef, &box );\n\t\t}\n\n\t\tm_floorResult = {};\n\t\tm_floorSeparation = 0.0f;\n\t\tm_mode = MoverMode::e_fallingMode;\n\t\tm_nonWalkablePosition = m_transform.p;\n\t\tm_noWalkTime = 0.0f;\n\t\tm_groundNormal = b2Vec2_zero;\n\t\tm_totalIterations = 0;\n\t\tm_pogoVelocity = 0.0f;\n\t\tm_canWalk = false;\n\t\tm_jumpReleased = true;\n\t\tm_lockCamera = true;\n\t\tm_planeCount = 0;\n\t\tm_time = 0.0f;\n\t}\n\n\tvoid UpdateFloorData(float timeStep)\n\t{\n\t\t// This cast extension keeps the character glued to the ground when walking down slopes and steps.\n\t\tfloat castExtension = m_stepDownHeight;\n\n\t\t// The extension increases with velocity while on ground.\n\t\tif (m_mode == MoverMode::e_walkingMode)\n\t\t{\n\t\t\tcastExtension += b2Length( m_velocity ) * timeStep;\n\t\t}\n\n\t\tfloat castLength = m_pogoRestLength + m_capsule.radius + castExtension;\n\n\t\tDrawTextLine( \"extension = %.3f\", castExtension );\n\n\t\t// Start cast from the center of the bottom capsule sphere.\n\t\tb2Vec2 origin = b2TransformPoint( m_transform, m_capsule.center1 );\n\t\tb2Vec2 translation = { 0.0, -castLength };\n\n\t\tb2QueryFilter pogoFilter = { MoverBit, StaticBit | DynamicBit };\n\t\tm_floorResult = {};\n\n\t\tb2World_CastRay( m_worldId, origin, translation, pogoFilter, CastCallback, &m_floorResult );\n\n\t\tif ( m_floorResult.hit == false || m_floorResult.normal.y < m_walkableGroundY )\n\t\t{\n\t\t\tm_floorResult = {};\n\n\t\t\t// Try again with a circle cast\n\t\t\tb2ShapeProxy proxy = b2MakeProxy( &origin, 1, 0.9f * m_capsuleRadius );\n\t\t\tb2World_CastShape( m_worldId, &proxy, translation, pogoFilter, CastCallback, &m_floorResult );\n\t\t}\n\n\t\tif ( m_floorResult.hit == true)\n\t\t{\n\t\t\tfloat contactHeight = m_floorResult.point.y;\n\t\t\tfloat distanceToGround = m_transform.p.y - contactHeight;\n\n\t\t\tif (m_velocity.y > 0.0f)\n\t\t\t{\n\t\t\t\t// todo why bother trying to find the surface if we just reset it here?\n\t\t\t\tm_floorResult = {};\n\t\t\t\tm_floorSeparation = 1.0f;\n\t\t\t\tm_canWalk = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_floorSeparation = distanceToGround;\n\t\t\t\tm_canWalk = false;\n\n\t\t\t\tif (m_floorResult.hit && m_floorResult.normal.y >= m_walkableGroundY)\n\t\t\t\t{\n\t\t\t\t\tif (distanceToGround <= castExtension)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (m_mode == MoverMode::e_walkingMode)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfloat currentPogoLength = m_pogoRestLength + m_pogoOffset;\n\t\t\t\t\t\t\tfloat bottomOfCapsule = m_transform.p.y + currentPogoLength;\n\t\t\t\t\t\t\tfloat bottomOfCapsuleAtRest = contactHeight + m_pogoRestLength;\n\t\t\t\t\t\t\tfloat prevOffset = m_pogoOffset;\n\t\t\t\t\t\t\tm_pogoOffset = bottomOfCapsule - bottomOfCapsuleAtRest;\n\t\t\t\t\t\t\tm_visualOffset += m_pogoOffset - prevOffset;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tm_canWalk = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tm_floorResult = {};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid SetMode(MoverMode mode)\n\t{\n\t\tif (mode == m_mode)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tm_canJump = false;\n\n\t\tif (m_modePicked[mode] == true)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif (mode == e_fallingMode)\n\t\t{\n\t\t\tm_timeInAir = 0.0f;\n\t\t}\n\t\telse if (mode == e_walkingMode)\n\t\t{\n\t\t\tif (m_mode == e_fallingMode)\n\t\t\t{\n\t\t\t\tm_landed = true;\n\t\t\t\tm_landingSpeed = -m_velocity.y;\n\t\t\t}\n\n\t\t\tm_velocity.y = 0.0f;\n\t\t}\n\n\t\tm_mode = mode;\n\t}\n\n\tvoid ComputeVelocity( float timeStep, bool keepNormalVelocity )\n\t{\n#if 0\n\t\tbool walkable = m_groundNormal.y > 0.71f;\n\n\t\t// Friction\n\t\tfloat speed = b2Length( m_velocity );\n\n\t\t{\n\t\t\t// Linear damping above stopSpeed and fixed reduction below stopSpeed\n\t\t\tfloat control = speed < m_stopSpeed ? m_stopSpeed : speed;\n\n\t\t\t// friction has units of 1/time\n\t\t\tfloat drop = control * m_friction * timeStep;\n\t\t\tfloat newSpeed = b2MaxFloat( 0.0f, speed - drop );\n\t\t\tm_velocity *= newSpeed / speed;\n\t\t}\n\n\t\tb2Vec2 desiredVelocity = { m_maxSpeed * throttle, 0.0f };\n\t\tfloat desiredSpeed;\n\t\tb2Vec2 desiredDirection = b2GetLengthAndNormalize( &desiredSpeed, desiredVelocity );\n\n\t\tif ( desiredSpeed > m_maxSpeed )\n\t\t{\n\t\t\tdesiredSpeed = m_maxSpeed;\n\t\t}\n\n\t\tfloat noWalkSteer = 0.0f;\n\t\tif ( m_onGround )\n\t\t{\n\t\t\tif ( walkable )\n\t\t\t{\n\t\t\t\tm_velocity.y = 0.0f;\n\t\t\t\tnoWalkSteer = m_airSteer;\n\t\t\t\tm_noWalkTime = 0.0f;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ( m_noWalkTime == 0.0f )\n\t\t\t\t{\n\t\t\t\t\tm_nonWalkablePosition = m_transform.p;\n\t\t\t\t}\n\n\t\t\t\tm_velocity = m_velocity - b2Dot( m_velocity, m_groundNormal ) * m_groundNormal;\n\t\t\t\t// m_noWalkSpeed = m_airSteer * b2MaxFloat( 0.0f, 1.0f - 0.25f * speed / b2MaxFloat(m_stopSpeed, 1.0f) );\n\n\t\t\t\tfloat noWalkDistance = b2Distance( m_transform.p, m_nonWalkablePosition );\n\t\t\t\tnoWalkSteer = m_airSteer;\n\t\t\t\tm_noWalkTime += timeStep;\n\t\t\t\t// if ( noWalkDistance / m_noWalkTime < 0.5f * m_airSteer * m_maxSpeed )\n\t\t\t\t//{\n\t\t\t\t//\tnoWalkSteer = m_airSteer + 0.5f * m_noWalkTime * ( 1.0f - m_airSteer );\n\t\t\t\t//\tnoWalkSteer = b2ClampFloat( noWalkSteer, m_airSteer, 1.0f );\n\t\t\t\t// }\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_noWalkTime = 0.0f;\n\t\t}\n\n\t\t// Accelerate\n\t\tfloat currentSpeed = b2Dot( m_velocity, desiredDirection );\n\t\tfloat addSpeed = desiredSpeed - currentSpeed;\n\t\tif ( addSpeed > 0.0f )\n\t\t{\n\t\t\tfloat steer;\n\t\t\tif ( m_onGround )\n\t\t\t{\n\t\t\t\tsteer = walkable ? 1.0f : noWalkSteer;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsteer = m_airSteer;\n\t\t\t}\n\n\t\t\tDrawTextLine( \"steer = %.2f\", steer );\n\n\t\t\tfloat accelSpeed = steer * m_accelerate * m_maxSpeed * timeStep;\n\t\t\tif ( accelSpeed > addSpeed )\n\t\t\t{\n\t\t\t\taccelSpeed = addSpeed;\n\t\t\t}\n\n\t\t\tm_velocity += accelSpeed * desiredDirection;\n\t\t}\n#else\n\t\t// todo keepNormalVelocity always true\n\t\tb2Vec2 v = m_velocity;\n\t\tfloat vy = v.y;\n\t\tv.y = 0.0f;\n\n\t\t// ConfigAccel = m_accelerate\n\t\t// ConfigDecelBase =\n\t\t// ConfigDecelPerSpeed\n\t\t// ConfigDecelStartAtSpeed\n\n\t\tbool inAir = m_mode == e_fallingMode;\n\t\tfloat acceleration = inAir ? m_airSteer * m_accelerate : m_accelerate;\n\n\t\t// m/s^2\n\t\tfloat decelBase = inAir ? 0.0f : 2.0f; // todo\n\n\t\t// m/s^2 / m/s = 1/s\n\t\t// same as friction in quake?\n\t\tfloat decelPerSpeed = inAir ? 1.0f : 5.0f; // todo\n\n\t\t// m/s\n\t\tfloat decelStartAtSpeed = inAir ? 4.25f : 0.0f; // todo\n\n\t\tfloat speed = b2Length( v );\n\n\t\t// m/s^2\n\t\tfloat perSpeedDecel = decelPerSpeed * b2MaxFloat( speed - decelStartAtSpeed, 0.0f );\n\n\t\t// m/s^2\n\t\tfloat decelTotal = decelBase + perSpeedDecel;\n\n\t\t// m/s\n\t\tfloat decel = decelTotal * timeStep;\n\n\t\t// m/s\n\t\tfloat decelCapped = b2MinFloat( decel, speed );\n\n\t\tfloat accel = acceleration * m_maxSpeed * timeStep;\n\n\t\tv -= decelCapped * b2Normalize( v );\n\n\t\tb2Vec2 throttle = { m_throttle, 0.0f };\n\t\tb2Vec2 control = m_maxSpeed * throttle;\n\t\tb2Vec2 toTarget = control - v;\n\t\tfloat toTargetDist = b2Length( toTarget );\n\n\t\tif ( b2Length( control ) > b2Length(v))\n\t\t{\n\t\t\tfloat accelFraction = b2ClampFloat( accel / toTargetDist, 0.0f, 1.0f );\n\t\t\tv += accelFraction * toTarget;\n\t\t}\n#endif\n\t}\n\n\tvoid UpdateFalling(float timeStep)\n\t{\n\t\tfloat vy = m_velocity.y;\n\t\tComputeVelocity(timeStep, true );\n\t\tm_velocity.y = vy;\n\n\t\tm_velocity.y -= m_gravity * timeStep;\n\n\t\tm_timeInAir += timeStep;\n\n\t\tm_floorSeparation = 0.0f;\n\n\t\tif ( b2Dot(m_velocity, m_floorResult.normal) < 0.0f && m_floorResult.normal.y >= m_walkableGroundY)\n\t\t{\n\t\t\tfloat currentPogoLength = m_pogoRestLength + m_pogoOffset;\n\t\t\tfloat bottomOfCapsule = m_transform.p.y + currentPogoLength;\n\t\t\tfloat bottomOfCapsuleAtRest = m_floorResult.point.y + m_pogoRestLength;\n\t\t\tfloat prevOffset = m_pogoOffset;\n\t\t\tm_pogoOffset = bottomOfCapsule - bottomOfCapsuleAtRest;\n\t\t\tm_visualOffset += m_pogoOffset - prevOffset;\n\n\t\t\tSetMode( e_walkingMode );\n\t\t}\n\t}\n\n\tvoid UpdateWalking( float timeStep )\n\t{\n\t\tif (m_floorResult.hit == false || m_floorResult.normal.y < m_walkableGroundY)\n\t\t{\n\t\t\tSetMode( e_fallingMode );\n\t\t\treturn;\n\t\t}\n\n\t\tif (m_floorResult.normal.y >= m_walkableGroundY)\n\t\t{\n\t\t\t// todo unsafe teleport\n\t\t\tm_transform.p.y = m_floorResult.point.y;\n\t\t}\n\n\t\tComputeVelocity(timeStep, true);\n\t\tm_velocity.y = 0.0f;\n\n\t\tif ( m_canJump && glfwGetKey( m_context->window, GLFW_KEY_SPACE ) )\n\t\t{\n\t\t\tm_velocity.y = m_jumpSpeed;\n\t\t\tSetMode( e_fallingMode );\n\t\t}\n\t}\n\n\t// https://github.com/id-Software/Quake/blob/master/QW/client/pmove.c#L390\n\tvoid SolveMove( float timeStep, float throttle )\n\t{\n\t\tbool walkable = m_groundNormal.y > 0.71f;\n\n\t\t// Friction\n\t\tfloat speed = b2Length( m_velocity );\n\t\tif ( speed < m_minSpeed )\n\t\t{\n\t\t\tm_velocity.x = 0.0f;\n\t\t\tm_velocity.y = 0.0f;\n\t\t}\n\t\telse if ( m_onGround && walkable )\n\t\t{\n\t\t\t// Linear damping above stopSpeed and fixed reduction below stopSpeed\n\t\t\tfloat control = speed < m_stopSpeed ? m_stopSpeed : speed;\n\n\t\t\t// friction has units of 1/time\n\t\t\tfloat drop = control * m_friction * timeStep;\n\t\t\tfloat newSpeed = b2MaxFloat( 0.0f, speed - drop );\n\t\t\tm_velocity *= newSpeed / speed;\n\t\t}\n\n\t\tb2Vec2 desiredVelocity = { m_maxSpeed * throttle, 0.0f };\n\t\tfloat desiredSpeed;\n\t\tb2Vec2 desiredDirection = b2GetLengthAndNormalize( &desiredSpeed, desiredVelocity );\n\n\t\tif ( desiredSpeed > m_maxSpeed )\n\t\t{\n\t\t\tdesiredSpeed = m_maxSpeed;\n\t\t}\n\n\t\tfloat noWalkSteer = 0.0f;\n\t\tif ( m_onGround )\n\t\t{\n\t\t\tif ( walkable )\n\t\t\t{\n\t\t\t\tm_velocity.y = 0.0f;\n\t\t\t\tnoWalkSteer = m_airSteer;\n\t\t\t\tm_noWalkTime = 0.0f;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (m_noWalkTime == 0.0f)\n\t\t\t\t{\n\t\t\t\t\tm_nonWalkablePosition = m_transform.p;\n\t\t\t\t}\n\n\t\t\t\tm_velocity = m_velocity - b2Dot( m_velocity, m_groundNormal ) * m_groundNormal;\n\t\t\t\t//m_noWalkSpeed = m_airSteer * b2MaxFloat( 0.0f, 1.0f - 0.25f * speed / b2MaxFloat(m_stopSpeed, 1.0f) );\n\n\t\t\t\tfloat noWalkDistance = b2Distance( m_transform.p, m_nonWalkablePosition );\n\t\t\t\tnoWalkSteer = m_airSteer;\n\t\t\t\tm_noWalkTime += timeStep;\n\t\t\t\t//if ( noWalkDistance / m_noWalkTime < 0.5f * m_airSteer * m_maxSpeed )\n\t\t\t\t//{\n\t\t\t\t//\tnoWalkSteer = m_airSteer + 0.5f * m_noWalkTime * ( 1.0f - m_airSteer );\n\t\t\t\t//\tnoWalkSteer = b2ClampFloat( noWalkSteer, m_airSteer, 1.0f );\n\t\t\t\t//}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_noWalkTime = 0.0f;\n\t\t}\n\n\t\t// Accelerate\n\t\tfloat currentSpeed = b2Dot( m_velocity, desiredDirection );\n\t\tfloat addSpeed = desiredSpeed - currentSpeed;\n\t\tif ( addSpeed > 0.0f )\n\t\t{\n\t\t\tfloat steer;\n\t\t\tif ( m_onGround )\n\t\t\t{\n\t\t\t\tsteer = walkable ? 1.0f : noWalkSteer;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsteer = m_airSteer;\n\t\t\t}\n\n\t\t\tDrawTextLine( \"steer = %.2f\", steer );\n\n\t\t\tfloat accelSpeed = steer * m_accelerate * m_maxSpeed * timeStep;\n\t\t\tif ( accelSpeed > addSpeed )\n\t\t\t{\n\t\t\t\taccelSpeed = addSpeed;\n\t\t\t}\n\n\t\t\tm_velocity += accelSpeed * desiredDirection;\n\t\t}\n\n\t\tm_velocity.y -= m_gravity * timeStep;\n\n\t\t// This ray extension keeps you glued to the ground when walking down slopes.\n\t\t// The extension increases with velocity.\n\t\tfloat rayExtension = m_stepDownHeight;\n\t\trayExtension += m_onGround ? b2Length( m_velocity ) * timeStep : 0.0f;\n\n\t\tfloat rayLength = m_pogoRestLength + m_capsule.radius + rayExtension;\n\n\t\tDrawTextLine( \"extension = %.3f\", rayExtension );\n\n\t\tb2Vec2 origin = b2TransformPoint( m_transform, m_capsule.center1 );\n\t\tb2Circle circle = { origin, 0.9f * m_capsule.radius };\n\t\tb2Vec2 segmentOffset = { 0.9f * m_capsule.radius, 0.0f };\n\t\tb2Segment segment = {\n\t\t\t.point1 = origin - segmentOffset,\n\t\t\t.point2 = origin + segmentOffset,\n\t\t};\n\n\t\tb2ShapeProxy proxy = {};\n\t\tb2Vec2 translation;\n\t\tb2QueryFilter pogoFilter = { MoverBit, StaticBit | DynamicBit };\n\t\tCastResult castResult = {};\n\n\t\tif ( m_pogoShape == e_pogoPoint )\n\t\t{\n\t\t\tproxy = b2MakeProxy( &origin, 1, 0.0f );\n\t\t\ttranslation = { 0.0f, -rayLength };\n\t\t}\n\t\telse if ( m_pogoShape == PogoCircle )\n\t\t{\n\t\t\tproxy = b2MakeProxy( &origin, 1, circle.radius );\n\t\t\ttranslation = { 0.0f, -rayLength + circle.radius };\n\t\t}\n\t\telse\n\t\t{\n\t\t\tproxy = b2MakeProxy( &segment.point1, 2, 0.0f );\n\t\t\ttranslation = { 0.0f, -rayLength };\n\t\t}\n\n\t\tb2World_CastShape( m_worldId, &proxy, translation, pogoFilter, CastCallback, &castResult );\n\n\t\t// Avoid snapping to ground if still going up\n\t\tif ( m_onGround == false )\n\t\t{\n\t\t\tm_onGround = castResult.hit && m_velocity.y <= 0.01f;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_onGround = castResult.hit;\n\t\t}\n\n\t\tif ( castResult.hit == false )\n\t\t{\n\t\t\tm_pogoVelocity = 0.0f;\n\t\t\tm_groundNormal = b2Vec2_zero;\n\n\t\t\tb2Vec2 delta = translation;\n\t\t\tm_context->draw.DrawSegment( origin, origin + delta, b2_colorGray );\n\n\t\t\tif ( m_pogoShape == e_pogoPoint )\n\t\t\t{\n\t\t\t\tm_context->draw.DrawPoint( origin + delta, 10.0f, b2_colorGray );\n\t\t\t}\n\t\t\telse if ( m_pogoShape == PogoCircle )\n\t\t\t{\n\t\t\t\tm_context->draw.DrawCircle( origin + delta, circle.radius, b2_colorGray );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_context->draw.DrawSegment( segment.point1 + delta, segment.point2 + delta, b2_colorGray );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_groundNormal = castResult.normal;\n\n\t\t\tfloat pogoCurrentLength = castResult.fraction * rayLength - m_capsuleRadius;\n\n\t\t\tfloat zeta = m_pogoDampingRatio;\n\t\t\tfloat hertz = m_pogoHertz;\n\t\t\tfloat omega = 2.0f * B2_PI * hertz;\n\t\t\tfloat omegaH = omega * timeStep;\n\n\t\t\tm_pogoVelocity = ( m_pogoVelocity - omega * omegaH * ( pogoCurrentLength - m_pogoRestLength ) ) /\n\t\t\t\t\t\t\t ( 1.0f + 2.0f * zeta * omegaH + omegaH * omegaH );\n\n\t\t\tb2Vec2 delta = castResult.fraction * translation;\n\t\t\tm_context->draw.DrawSegment( origin, origin + delta, b2_colorGray );\n\n\t\t\tif ( m_pogoShape == e_pogoPoint )\n\t\t\t{\n\t\t\t\tm_context->draw.DrawPoint( origin + delta, 10.0f, b2_colorPlum );\n\t\t\t}\n\t\t\telse if ( m_pogoShape == PogoCircle )\n\t\t\t{\n\t\t\t\tm_context->draw.DrawCircle( origin + delta, circle.radius, b2_colorPlum );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_context->draw.DrawSegment( segment.point1 + delta, segment.point2 + delta, b2_colorPlum );\n\t\t\t}\n\n\t\t\tb2Body_ApplyForce( castResult.bodyId, { 0.0f, -50.0f }, castResult.point, true );\n\t\t}\n\n\t\tb2Vec2 target = m_transform.p + timeStep * m_velocity + timeStep * m_pogoVelocity * b2Vec2{ 0.0f, 1.0f };\n\n\t\t// Mover overlap filter\n\t\tb2QueryFilter collideFilter = { MoverBit, StaticBit | DynamicBit | MoverBit };\n\n\t\t// Movers don't sweep against other movers, allows for soft collision\n\t\tb2QueryFilter castFilter = { MoverBit, StaticBit | DynamicBit };\n\n\t\tm_totalIterations = 0;\n\t\tfloat tolerance = 0.01f;\n\n\t\tfor ( int iteration = 0; iteration < 5; ++iteration )\n\t\t{\n\t\t\tm_planeCount = 0;\n\n\t\t\tb2Capsule mover;\n\t\t\tmover.center1 = b2TransformPoint( m_transform, m_capsule.center1 );\n\t\t\tmover.center2 = b2TransformPoint( m_transform, m_capsule.center2 );\n\t\t\tmover.radius = m_capsule.radius;\n\n\t\t\tb2World_CollideMover( m_worldId, &mover, collideFilter, PlaneResultFcn, this );\n\t\t\tb2PlaneSolverResult result = b2SolvePlanes( target, m_planes, m_planeCount );\n\n\t\t\tm_totalIterations += result.iterationCount;\n\n\t\t\tb2Vec2 moverTranslation = result.position - m_transform.p;\n\n\t\t\tfloat fraction = b2World_CastMover( m_worldId, &mover, moverTranslation, castFilter );\n\n\t\t\tb2Vec2 delta = fraction * moverTranslation;\n\t\t\tm_transform.p += delta;\n\n\t\t\tif ( b2LengthSquared( delta ) < tolerance * tolerance )\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tm_velocity = b2ClipVector( m_velocity, m_planes, m_planeCount );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 350.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->m_height - height - 25.0f ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 340.0f, height ) );\n\n\t\tImGui::Begin( \"Mover\", nullptr, 0 );\n\n\t\tImGui::PushItemWidth( 240.0f );\n\n\t\tImGui::SliderFloat( \"Jump Speed\", &m_jumpSpeed, 0.0f, 20.0f, \"%.0f\" );\n\t\tImGui::SliderFloat( \"Min Speed\", &m_minSpeed, 0.0f, 1.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"Max Speed\", &m_maxSpeed, 0.0f, 20.0f, \"%.0f\" );\n\t\tImGui::SliderFloat( \"Stop Speed\", &m_stopSpeed, 0.0f, 10.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"Accelerate\", &m_accelerate, 0.0f, 100.0f, \"%.0f\" );\n\t\tImGui::SliderFloat( \"Friction\", &m_friction, 0.0f, 10.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"Gravity\", &m_gravity, 0.0f, 100.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"Air Steer\", &m_airSteer, 0.0f, 1.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"Pogo Hertz\", &m_pogoHertz, 0.0f, 30.0f, \"%.0f\" );\n\t\tImGui::SliderFloat( \"Pogo Damping\", &m_pogoDampingRatio, 0.0f, 4.0f, \"%.1f\" );\n\n\t\tImGui::PopItemWidth();\n\n\t\tImGui::Separator();\n\n\t\tImGui::Checkbox( \"Lock Camera\", &m_lockCamera );\n\n\t\tImGui::End();\n\t}\n\n\tstatic bool PlaneResultFcn( b2ShapeId shapeId, const b2PlaneResult* planeResult, void* context )\n\t{\n\t\tassert( planeResult->hit == true );\n\n\t\tMover* self = static_cast<Mover*>( context );\n\t\tfloat maxPush = FLT_MAX;\n\t\tbool clipVelocity = true;\n\t\tShapeUserData* userData = static_cast<ShapeUserData*>( (void*)b2Shape_GetUserData( shapeId ) );\n\t\tif ( userData != nullptr )\n\t\t{\n\t\t\tmaxPush = userData->maxPush;\n\t\t\tclipVelocity = userData->clipVelocity;\n\t\t}\n\n\t\tif ( self->m_planeCount < m_planeCapacity )\n\t\t{\n\t\t\tassert( b2IsValidPlane( planeResult->plane ) );\n\t\t\tself->m_planes[self->m_planeCount] = { planeResult->plane, maxPush, 0.0f, clipVelocity };\n\t\t\tself->m_planeCount += 1;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tstatic bool Kick( b2ShapeId shapeId, void* context )\n\t{\n\t\tMover* self = (Mover*)context;\n\t\tb2BodyId bodyId = b2Shape_GetBody( shapeId );\n\t\tb2BodyType type = b2Body_GetType( bodyId );\n\n\t\tif ( type != b2_dynamicBody )\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tb2Vec2 center = b2Body_GetWorldCenterOfMass( bodyId );\n\t\tb2Vec2 direction = b2Normalize( center - self->m_transform.p );\n\t\tb2Vec2 impulse = b2Vec2{ 2.0f * direction.x, 2.0f };\n\t\tb2Body_ApplyLinearImpulseToCenter( bodyId, impulse, true );\n\n\t\treturn true;\n\t}\n\n\tvoid Keyboard( int key ) override\n\t{\n\t\tif ( key == 'K' )\n\t\t{\n\t\t\tb2Vec2 point = b2TransformPoint( m_transform, { 0.0f, m_capsule.center1.y - m_capsuleRadius } );\n\t\t\tb2Circle circle = { point, 0.5f };\n\t\t\tb2ShapeProxy proxy = b2MakeProxy( &circle.center, 1, circle.radius );\n\t\t\tb2QueryFilter filter = { MoverBit, DebrisBit };\n\t\t\tb2World_OverlapShape( m_worldId, &proxy, filter, Kick, this );\n\t\t\tm_context->draw.DrawCircle( circle.center, circle.radius, b2_colorGoldenRod );\n\t\t}\n\n\t\tSample::Keyboard( key );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tbool pause = false;\n\t\tif ( settings.pause )\n\t\t{\n\t\t\tpause = settings.singleStep != true;\n\t\t}\n\n\t\tfloat timeStep = settings.hertz > 0.0f ? 1.0f / settings.hertz : 0.0f;\n\t\tif ( pause )\n\t\t{\n\t\t\ttimeStep = 0.0f;\n\t\t}\n\n\t\tif ( timeStep > 0.0f )\n\t\t{\n\t\t\tb2Vec2 point = {\n\t\t\t\t.x = m_elevatorBase.x,\n\t\t\t\t.y = m_elevatorAmplitude * cosf( 1.0f * m_time + B2_PI ) + m_elevatorBase.y,\n\t\t\t};\n\n\t\t\tb2Body_SetTargetTransform( m_elevatorId, { point, b2Rot_identity }, timeStep );\n\t\t}\n\n\t\tm_time += timeStep;\n\n\t\tSample::Step();\n\n\t\tif ( pause == false )\n\t\t{\n\t\t\tfloat throttle = 0.0f;\n\n\t\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_A ) )\n\t\t\t{\n\t\t\t\tthrottle -= 1.0f;\n\t\t\t}\n\n\t\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_D ) )\n\t\t\t{\n\t\t\t\tthrottle += 1.0f;\n\t\t\t}\n\n\t\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_SPACE ) )\n\t\t\t{\n\t\t\t\tbool walkable = m_groundNormal.y > 0.71f;\n\t\t\t\tif ( m_onGround == true && walkable && m_jumpReleased )\n\t\t\t\t{\n\t\t\t\t\tm_velocity.y = m_jumpSpeed;\n\t\t\t\t\tm_onGround = false;\n\t\t\t\t\tm_jumpReleased = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_jumpReleased = true;\n\t\t\t}\n\n\t\t\tSolveMove( timeStep, throttle );\n\t\t}\n\n\t\tint count = m_planeCount;\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tb2Plane plane = m_planes[i].plane;\n\t\t\tb2Vec2 p1 = m_transform.p + ( plane.offset - m_capsule.radius ) * plane.normal;\n\t\t\tb2Vec2 p2 = p1 + 0.1f * plane.normal;\n\t\t\tm_context->draw.DrawPoint( p1, 5.0f, b2_colorYellow );\n\t\t\tm_context->draw.DrawSegment( p1, p2, b2_colorYellow );\n\t\t}\n\n\t\tb2Vec2 p1 = b2TransformPoint( m_transform, m_capsule.center1 );\n\t\tb2Vec2 p2 = b2TransformPoint( m_transform, m_capsule.center2 );\n\n\t\tb2HexColor color = m_onGround ? b2_colorOrange : b2_colorAquamarine;\n\t\tm_context->draw.DrawSolidCapsule( p1, p2, m_capsule.radius, color );\n\t\tm_context->draw.DrawSegment( m_transform.p, m_transform.p + m_velocity, b2_colorPurple );\n\n\t\tb2Vec2 p = m_transform.p;\n\t\tDrawTextLine( \"position %.2f %.2f\", p.x, p.y );\n\t\tDrawTextLine( \"velocity %.2f %.2f\", m_velocity.x, m_velocity.y );\n\t\tDrawTextLine( \"iterations %d\", m_totalIterations );\n\n\t\tif ( m_lockCamera )\n\t\t{\n\t\t\tm_context->camera.m_center.x = m_transform.p.x;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Mover( context );\n\t}\n\n\tstatic constexpr int m_planeCapacity = 8;\n\tstatic constexpr b2Vec2 m_elevatorBase = { 112.0f, 10.0f };\n\tstatic constexpr float m_elevatorAmplitude = 4.0f;\n\tstatic constexpr float m_walkableGroundY = 0.706f;\n\n\tfloat m_pogoRestLength = 0.5f;\n\tfloat m_capsuleRadius = 0.4f;\n\tfloat m_capsuleInteriorLength = 0.5f;\n\tfloat m_jumpSpeed = 5.0f;\n\tfloat m_maxSpeed = 6.0f;\n\tfloat m_stopSpeed = 2.0f;\n\tfloat m_accelerate = 11.0f;\n\tfloat m_airSteer = 0.2f;\n\tfloat m_friction = 8.0f;\n\tfloat m_gravity = 15.0f;\n\tfloat m_pogoHertz = 5.0f;\n\tfloat m_pogoDampingRatio = 0.8f;\n\tfloat m_stepDownHeight = 0.5f;\n\n\tCastResult m_floorResult;\n\tfloat m_floorSeparation;\n\tfloat m_pogoSpeed = 0.0f;\n\tfloat m_pogoOffset = 0.0f;\n\tfloat m_visualOffset = 0.0f;\n\n\t// The mover transform origin is at the bottom of the pogo stick. On the floor if walking.\n\tb2Transform m_transform;\n\n\tMoverMode m_mode;\n\tb2Vec2 m_velocity;\n\tb2Vec2 m_groundNormal;\n\tb2Vec2 m_nonWalkablePosition;\n\tb2Capsule m_capsule;\n\tb2BodyId m_elevatorId;\n\tb2ShapeId m_ballId;\n\tShapeUserData m_friendlyShape;\n\tShapeUserData m_elevatorShape;\n\tb2CollisionPlane m_planes[m_planeCapacity] = {};\n\tbool m_modePicked[MoverMode::e_modeCount];\n\tint m_planeCount;\n\tint m_totalIterations;\n\tfloat m_timeInAir;\n\tfloat m_throttle;\n\tbool m_canJump;\n\tbool m_landed;\n\tfloat m_landingSpeed;\n\tfloat m_pogoVelocity;\n\tfloat m_time;\n\tfloat m_noWalkTime;\n\tbool m_canWalk;\n\tbool m_jumpReleased;\n\tbool m_lockCamera;\n};\n\nstatic int sampleMover = RegisterSample( \"Character\", \"Mover\", Mover2::Create );\n#endif\n"
  },
  {
    "path": "samples/sample_collision.cpp",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"draw.h\"\n#include \"random.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n#include <imgui.h>\n#include <imgui_internal.h>\n//#include <stdlib.h>\n\nclass ShapeDistance : public Sample\n{\npublic:\n\tenum ShapeType\n\t{\n\t\te_point,\n\t\te_segment,\n\t\te_triangle,\n\t\te_box\n\t};\n\n\texplicit ShapeDistance( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 3.0f;\n\t\t}\n\n\t\tm_point = b2Vec2_zero;\n\t\tm_segment = { { -0.5f, 0.0f }, { 0.5f, 0.0f } };\n\n\t\t{\n\t\t\tb2Vec2 points[3] = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 1.0f } };\n\t\t\tb2Hull hull = b2ComputeHull( points, 3 );\n\t\t\tm_triangle = b2MakePolygon( &hull, 0.0f );\n\n\t\t\t// m_triangle = b2MakeSquare( 0.4f );\n\t\t}\n\n\t\tm_box = b2MakeSquare( 0.5f );\n\n\t\t// m_transform = { { 1.5f, -1.5f }, b2Rot_identity };\n\t\tm_transform = { { 0.0f, 0.0f }, b2Rot_identity };\n\t\tm_angle = 0.0f;\n\n\t\tm_cache = b2_emptySimplexCache;\n\t\tm_simplexCount = 0;\n\t\tm_simplexIndex = 0;\n\t\tm_startPoint = { 0.0f, 0.0f };\n\t\tm_basePosition = { 0.0f, 0.0f };\n\t\tm_baseAngle = 0.0f;\n\n\t\tm_dragging = false;\n\t\tm_rotating = false;\n\t\tm_showIndices = false;\n\t\tm_useCache = false;\n\t\tm_drawSimplex = false;\n\n\t\tm_typeA = e_box;\n\t\tm_typeB = e_box;\n\t\tm_radiusA = 0.0f;\n\t\tm_radiusB = 0.0f;\n\n\t\tm_proxyA = MakeProxy( m_typeA, m_radiusA );\n\t\tm_proxyB = MakeProxy( m_typeB, m_radiusB );\n\t}\n\n\tb2ShapeProxy MakeProxy( ShapeType type, float radius )\n\t{\n\t\tb2ShapeProxy proxy = {};\n\t\tproxy.radius = radius;\n\n\t\tswitch ( type )\n\t\t{\n\t\t\tcase e_point:\n\t\t\t\tproxy.points[0] = b2Vec2_zero;\n\t\t\t\tproxy.count = 1;\n\t\t\t\tbreak;\n\n\t\t\tcase e_segment:\n\t\t\t\tproxy.points[0] = m_segment.point1;\n\t\t\t\tproxy.points[1] = m_segment.point2;\n\t\t\t\tproxy.count = 2;\n\t\t\t\tbreak;\n\n\t\t\tcase e_triangle:\n\t\t\t\tfor ( int i = 0; i < m_triangle.count; ++i )\n\t\t\t\t{\n\t\t\t\t\tproxy.points[i] = m_triangle.vertices[i];\n\t\t\t\t}\n\t\t\t\tproxy.count = m_triangle.count;\n\t\t\t\tbreak;\n\n\t\t\tcase e_box:\n\t\t\t\tproxy.points[0] = m_box.vertices[0];\n\t\t\t\tproxy.points[1] = m_box.vertices[1];\n\t\t\t\tproxy.points[2] = m_box.vertices[2];\n\t\t\t\tproxy.points[3] = m_box.vertices[3];\n\t\t\t\tproxy.count = 4;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tassert( false );\n\t\t}\n\n\t\treturn proxy;\n\t}\n\n\tvoid DrawShape( ShapeType type, b2Transform transform, float radius, b2HexColor color )\n\t{\n\t\tswitch ( type )\n\t\t{\n\t\t\tcase e_point:\n\t\t\t{\n\t\t\t\tb2Vec2 p = b2TransformPoint( transform, m_point );\n\t\t\t\tif ( radius > 0.0f )\n\t\t\t\t{\n\t\t\t\t\tDrawSolidCircle( m_draw, { p, transform.q }, radius, color );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDrawPoint( m_draw, p, 5.0f, color );\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\t\tcase e_segment:\n\t\t\t{\n\t\t\t\tb2Vec2 p1 = b2TransformPoint( transform, m_segment.point1 );\n\t\t\t\tb2Vec2 p2 = b2TransformPoint( transform, m_segment.point2 );\n\n\t\t\t\tif ( radius > 0.0f )\n\t\t\t\t{\n\t\t\t\t\tDrawSolidCapsule( m_draw, p1, p2, radius, color );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDrawLine( m_draw, p1, p2, color );\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\t\tcase e_triangle:\n\t\t\t\tDrawSolidPolygon( m_draw, transform, m_triangle.vertices, m_triangle.count, radius, color );\n\t\t\t\tbreak;\n\n\t\t\tcase e_box:\n\t\t\t\tDrawSolidPolygon( m_draw, transform, m_box.vertices, m_box.count, radius, color );\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tassert( false );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 21.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 19.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Shape Distance\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tconst char* shapeTypes[] = { \"point\", \"segment\", \"triangle\", \"box\" };\n\t\tint shapeType = int( m_typeA );\n\t\tif ( ImGui::Combo( \"shape A\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) )\n\t\t{\n\t\t\tm_typeA = ShapeType( shapeType );\n\t\t\tm_proxyA = MakeProxy( m_typeA, m_radiusA );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"radius A\", &m_radiusA, 0.0f, 0.5f, \"%.2f\" ) )\n\t\t{\n\t\t\tm_proxyA.radius = m_radiusA;\n\t\t}\n\n\t\tshapeType = int( m_typeB );\n\t\tif ( ImGui::Combo( \"shape B\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) )\n\t\t{\n\t\t\tm_typeB = ShapeType( shapeType );\n\t\t\tm_proxyB = MakeProxy( m_typeB, m_radiusB );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"radius B\", &m_radiusB, 0.0f, 0.5f, \"%.2f\" ) )\n\t\t{\n\t\t\tm_proxyB.radius = m_radiusB;\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::SliderFloat( \"x offset\", &m_transform.p.x, -2.0f, 2.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"y offset\", &m_transform.p.y, -2.0f, 2.0f, \"%.2f\" );\n\n\t\tif ( ImGui::SliderFloat( \"angle\", &m_angle, -B2_PI, B2_PI, \"%.2f\" ) )\n\t\t{\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::Checkbox( \"show indices\", &m_showIndices );\n\t\tImGui::Checkbox( \"use cache\", &m_useCache );\n\n\t\tImGui::Separator();\n\n\t\tif ( ImGui::Checkbox( \"draw simplex\", &m_drawSimplex ) )\n\t\t{\n\t\t\tm_simplexIndex = 0;\n\t\t}\n\n\t\tif ( m_drawSimplex && m_simplexCount > 0 )\n\t\t{\n\t\t\tImGui::SliderInt( \"index\", &m_simplexIndex, 0, m_simplexCount - 1 );\n\t\t\tm_simplexIndex = b2ClampInt( m_simplexIndex, 0, m_simplexCount - 1 );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid MouseDown( b2Vec2 p, int button, int mods ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tif ( mods == 0 && m_rotating == false )\n\t\t\t{\n\t\t\t\tm_dragging = true;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_basePosition = m_transform.p;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_SHIFT && m_dragging == false )\n\t\t\t{\n\t\t\t\tm_rotating = true;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_baseAngle = m_angle;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseUp( b2Vec2, int button ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tm_dragging = false;\n\t\t\tm_rotating = false;\n\t\t}\n\t}\n\n\tvoid MouseMove( b2Vec2 p ) override\n\t{\n\t\tif ( m_dragging )\n\t\t{\n\t\t\tm_transform.p = m_basePosition + 0.5f * ( p - m_startPoint );\n\t\t}\n\t\telse if ( m_rotating )\n\t\t{\n\t\t\tfloat dx = p.x - m_startPoint.x;\n\t\t\tm_angle = b2ClampFloat( m_baseAngle + 1.0f * dx, -B2_PI, B2_PI );\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\t}\n\n\tstatic b2Vec2 Weight2( float a1, b2Vec2 w1, float a2, b2Vec2 w2 )\n\t{\n\t\treturn { a1 * w1.x + a2 * w2.x, a1 * w1.y + a2 * w2.y };\n\t}\n\n\tstatic b2Vec2 Weight3( float a1, b2Vec2 w1, float a2, b2Vec2 w2, float a3, b2Vec2 w3 )\n\t{\n\t\treturn { a1 * w1.x + a2 * w2.x + a3 * w3.x, a1 * w1.y + a2 * w2.y + a3 * w3.y };\n\t}\n\n\tvoid ComputeSimplexWitnessPoints( b2Vec2* a, b2Vec2* b, const b2Simplex* s )\n\t{\n\t\tswitch ( s->count )\n\t\t{\n\t\t\tcase 0:\n\t\t\t\tassert( false );\n\t\t\t\tbreak;\n\n\t\t\tcase 1:\n\t\t\t\t*a = s->v1.wA;\n\t\t\t\t*b = s->v1.wB;\n\t\t\t\tbreak;\n\n\t\t\tcase 2:\n\t\t\t\t*a = Weight2( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA );\n\t\t\t\t*b = Weight2( s->v1.a, s->v1.wB, s->v2.a, s->v2.wB );\n\t\t\t\tbreak;\n\n\t\t\tcase 3:\n\t\t\t\t*a = Weight3( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA, s->v3.a, s->v3.wA );\n\t\t\t\t*b = *a;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tassert( false );\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tb2DistanceInput input;\n\t\tinput.proxyA = m_proxyA;\n\t\tinput.proxyB = m_proxyB;\n\t\tinput.transformA = b2Transform_identity;\n\t\tinput.transformB = m_transform;\n\t\tinput.useRadii = m_radiusA > 0.0f || m_radiusB > 0.0f;\n\n\t\tif ( m_useCache == false )\n\t\t{\n\t\t\tm_cache.count = 0;\n\t\t}\n\n\t\tb2DistanceOutput output = b2ShapeDistance( &input, &m_cache, m_simplexes, m_simplexCapacity );\n\n\t\tm_simplexCount = output.simplexCount;\n\n\t\tDrawShape( m_typeA, b2Transform_identity, m_radiusA, b2_colorCyan );\n\t\tDrawShape( m_typeB, m_transform, m_radiusB, b2_colorBisque );\n\n\t\tif ( m_drawSimplex )\n\t\t{\n\t\t\tb2Simplex* simplex = m_simplexes + m_simplexIndex;\n\t\t\tb2SimplexVertex* vertices[3] = { &simplex->v1, &simplex->v2, &simplex->v3 };\n\n\t\t\tif ( m_simplexIndex > 0 )\n\t\t\t{\n\t\t\t\t// The first recorded simplex does not have valid barycentric coordinates\n\t\t\t\tb2Vec2 pointA, pointB;\n\t\t\t\tComputeSimplexWitnessPoints( &pointA, &pointB, simplex );\n\n\t\t\t\tDrawLine( m_draw, pointA, pointB, b2_colorWhite );\n\t\t\t\tDrawPoint( m_draw, pointA, 10.0f, b2_colorWhite );\n\t\t\t\tDrawPoint( m_draw, pointB, 10.0f, b2_colorWhite );\n\t\t\t}\n\n\t\t\tb2HexColor colors[3] = { b2_colorRed, b2_colorGreen, b2_colorBlue };\n\n\t\t\tfor ( int i = 0; i < simplex->count; ++i )\n\t\t\t{\n\t\t\t\tb2SimplexVertex* vertex = vertices[i];\n\t\t\t\tDrawPoint( m_draw, vertex->wA, 10.0f, colors[i] );\n\t\t\t\tDrawPoint( m_draw, vertex->wB, 10.0f, colors[i] );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDrawLine( m_draw, output.pointA, output.pointB, b2_colorDimGray );\n\t\t\tDrawPoint( m_draw, output.pointA, 10.0f, b2_colorWhite );\n\t\t\tDrawPoint( m_draw, output.pointB, 10.0f, b2_colorWhite );\n\n\t\t\tDrawLine( m_draw, output.pointA, output.pointA + 0.5f * output.normal, b2_colorYellow );\n\t\t}\n\n\t\tif ( m_showIndices )\n\t\t{\n\t\t\tfor ( int i = 0; i < m_proxyA.count; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 p = m_proxyA.points[i];\n\t\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \" %d\", i );\n\t\t\t}\n\n\t\t\tfor ( int i = 0; i < m_proxyB.count; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 p = b2TransformPoint( m_transform, m_proxyB.points[i] );\n\t\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \" %d\", i );\n\t\t\t}\n\t\t}\n\n\t\tDrawTextLine( \"mouse button 1: drag\" );\n\t\tDrawTextLine( \"mouse button 1 + shift: rotate\" );\n\t\tDrawTextLine( \"distance = %.2f, iterations = %d\", output.distance, output.iterations );\n\n\t\tif ( m_cache.count == 1 )\n\t\t{\n\t\t\tDrawTextLine( \"cache = {%d}, {%d}\", m_cache.indexA[0], m_cache.indexB[0] );\n\t\t}\n\t\telse if ( m_cache.count == 2 )\n\t\t{\n\t\t\tDrawTextLine( \"cache = {%d, %d}, {%d, %d}\", m_cache.indexA[0], m_cache.indexA[1], m_cache.indexB[0],\n\t\t\t\t\t\t  m_cache.indexB[1] );\n\t\t}\n\t\telse if ( m_cache.count == 3 )\n\t\t{\n\t\t\tDrawTextLine( \"cache = {%d, %d, %d}, {%d, %d, %d}\", m_cache.indexA[0], m_cache.indexA[1], m_cache.indexA[2],\n\t\t\t\t\t\t  m_cache.indexB[0], m_cache.indexB[1], m_cache.indexB[2] );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ShapeDistance( context );\n\t}\n\n\tstatic constexpr int m_simplexCapacity = 20;\n\n\tb2Polygon m_box;\n\tb2Polygon m_triangle;\n\tb2Vec2 m_point;\n\tb2Segment m_segment;\n\n\tShapeType m_typeA;\n\tShapeType m_typeB;\n\tfloat m_radiusA;\n\tfloat m_radiusB;\n\tb2ShapeProxy m_proxyA;\n\tb2ShapeProxy m_proxyB;\n\n\tb2SimplexCache m_cache;\n\tb2Simplex m_simplexes[m_simplexCapacity];\n\tint m_simplexCount;\n\tint m_simplexIndex;\n\n\tb2Transform m_transform;\n\tfloat m_angle;\n\n\tb2Vec2 m_basePosition;\n\tb2Vec2 m_startPoint;\n\tfloat m_baseAngle;\n\n\tbool m_dragging;\n\tbool m_rotating;\n\tbool m_showIndices;\n\tbool m_useCache;\n\tbool m_drawSimplex;\n};\n\nstatic int sampleShapeDistance = RegisterSample( \"Collision\", \"Shape Distance\", ShapeDistance::Create );\n\nenum UpdateType\n{\n\tUpdate_Incremental = 0,\n\tUpdate_FullRebuild = 1,\n\tUpdate_PartialRebuild = 2,\n};\n\nstruct Proxy\n{\n\tb2AABB box;\n\tb2AABB fatBox;\n\tb2Vec2 position;\n\tb2Vec2 width;\n\tint proxyId;\n\tint rayStamp;\n\tint queryStamp;\n\tbool moved;\n};\n\nstatic bool QueryCallback( int32_t proxyId, uint64_t userData, void* context );\nstatic float RayCallback( const b2RayCastInput* input, int32_t proxyId, uint64_t userData, void* context );\n\n// Tests the Box2D bounding volume hierarchy (BVH). The dynamic tree\n// can be used independently as a spatial data structure.\nclass DynamicTree : public Sample\n{\npublic:\n\texplicit DynamicTree( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 500.0f, 500.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 21.0f;\n\t\t}\n\n\t\tm_fill = 0.25f;\n\t\tm_moveFraction = 0.05f;\n\t\tm_moveDelta = 0.1f;\n\t\tm_proxies = nullptr;\n\t\tm_proxyCount = 0;\n\t\tm_proxyCapacity = 0;\n\t\tm_ratio = 5.0f;\n\t\tm_grid = 1.0f;\n\n\t\tm_moveBuffer = nullptr;\n\t\tm_moveCount = 0;\n\n\t\tm_rowCount = m_isDebug ? 100 : 1000;\n\t\tm_columnCount = m_isDebug ? 100 : 1000;\n\t\tmemset( &m_tree, 0, sizeof( m_tree ) );\n\t\tBuildTree();\n\t\tm_timeStamp = 0;\n\t\tm_updateType = Update_Incremental;\n\n\t\tm_startPoint = { 0.0f, 0.0f };\n\t\tm_endPoint = { 0.0f, 0.0f };\n\t\tm_queryDrag = false;\n\t\tm_rayDrag = false;\n\t\tm_validate = true;\n\t}\n\n\t~DynamicTree() override\n\t{\n\t\tfree( m_proxies );\n\t\tfree( m_moveBuffer );\n\t\tb2DynamicTree_Destroy( &m_tree );\n\t}\n\n\tvoid BuildTree()\n\t{\n\t\tb2DynamicTree_Destroy( &m_tree );\n\t\tfree( m_proxies );\n\t\tfree( m_moveBuffer );\n\n\t\tm_proxyCapacity = m_rowCount * m_columnCount;\n\t\tm_proxies = static_cast<Proxy*>( malloc( m_proxyCapacity * sizeof( Proxy ) ) );\n\t\tm_proxyCount = 0;\n\n\t\tm_moveBuffer = static_cast<int*>( malloc( m_proxyCapacity * sizeof( int ) ) );\n\t\tm_moveCount = 0;\n\n\t\tfloat y = -4.0f;\n\n\t\tm_tree = b2DynamicTree_Create();\n\n\t\tconst b2Vec2 aabbMargin = { 0.1f, 0.1f };\n\n\t\tfor ( int i = 0; i < m_rowCount; ++i )\n\t\t{\n\t\t\tfloat x = -40.0f;\n\n\t\t\tfor ( int j = 0; j < m_columnCount; ++j )\n\t\t\t{\n\t\t\t\tfloat fillTest = RandomFloatRange( 0.0f, 1.0f );\n\t\t\t\tif ( fillTest <= m_fill )\n\t\t\t\t{\n\t\t\t\t\tassert( m_proxyCount <= m_proxyCapacity );\n\t\t\t\t\tProxy* p = m_proxies + m_proxyCount;\n\t\t\t\t\tp->position = { x, y };\n\n\t\t\t\t\tfloat ratio = RandomFloatRange( 1.0f, m_ratio );\n\t\t\t\t\tfloat width = RandomFloatRange( 0.1f, 0.5f );\n\t\t\t\t\tif ( RandomFloat() > 0.0f )\n\t\t\t\t\t{\n\t\t\t\t\t\tp->width.x = ratio * width;\n\t\t\t\t\t\tp->width.y = width;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tp->width.x = width;\n\t\t\t\t\t\tp->width.y = ratio * width;\n\t\t\t\t\t}\n\n\t\t\t\t\tp->box.lowerBound = { x, y };\n\t\t\t\t\tp->box.upperBound = { x + p->width.x, y + p->width.y };\n\t\t\t\t\tp->fatBox.lowerBound = b2Sub( p->box.lowerBound, aabbMargin );\n\t\t\t\t\tp->fatBox.upperBound = b2Add( p->box.upperBound, aabbMargin );\n\n\t\t\t\t\tp->proxyId = b2DynamicTree_CreateProxy( &m_tree, p->fatBox, B2_DEFAULT_CATEGORY_BITS, m_proxyCount );\n\t\t\t\t\tp->rayStamp = -1;\n\t\t\t\t\tp->queryStamp = -1;\n\t\t\t\t\tp->moved = false;\n\t\t\t\t\t++m_proxyCount;\n\t\t\t\t}\n\n\t\t\t\tx += m_grid;\n\t\t\t}\n\n\t\t\ty += m_grid;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 320.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );\n\n\t\tImGui::Begin( \"Dynamic Tree\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tImGui::PushItemWidth( 100.0f );\n\n\t\tbool changed = false;\n\t\tif ( ImGui::SliderInt( \"rows\", &m_rowCount, 0, 1000, \"%d\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderInt( \"columns\", &m_columnCount, 0, 1000, \"%d\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"fill\", &m_fill, 0.0f, 1.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"grid\", &m_grid, 0.5f, 2.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"ratio\", &m_ratio, 1.0f, 10.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"move\", &m_moveFraction, 0.0f, 1.0f, \"%.2f\" ) )\n\t\t{\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"delta\", &m_moveDelta, 0.0f, 1.0f, \"%.2f\" ) )\n\t\t{\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Incremental\", m_updateType == Update_Incremental ) )\n\t\t{\n\t\t\tm_updateType = Update_Incremental;\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Full Rebuild\", m_updateType == Update_FullRebuild ) )\n\t\t{\n\t\t\tm_updateType = Update_FullRebuild;\n\t\t\tchanged = true;\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Partial Rebuild\", m_updateType == Update_PartialRebuild ) )\n\t\t{\n\t\t\tm_updateType = Update_PartialRebuild;\n\t\t\tchanged = true;\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::Text( \"mouse button 1: ray cast\" );\n\t\tImGui::Text( \"mouse button 1 + shift: query\" );\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\n\t\tif ( changed )\n\t\t{\n\t\t\tBuildTree();\n\t\t}\n\t}\n\n\tvoid MouseDown( b2Vec2 p, int button, int mods ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tif ( mods == 0 && m_queryDrag == false )\n\t\t\t{\n\t\t\t\tm_rayDrag = true;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_endPoint = p;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_SHIFT && m_rayDrag == false )\n\t\t\t{\n\t\t\t\tm_queryDrag = true;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_endPoint = p;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseUp( b2Vec2, int button ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tm_queryDrag = false;\n\t\t\tm_rayDrag = false;\n\t\t}\n\t}\n\n\tvoid MouseMove( b2Vec2 p ) override\n\t{\n\t\tm_endPoint = p;\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( m_queryDrag )\n\t\t{\n\t\t\tb2AABB box = { b2Min( m_startPoint, m_endPoint ), b2Max( m_startPoint, m_endPoint ) };\n\t\t\tb2DynamicTree_Query( &m_tree, box, B2_DEFAULT_MASK_BITS, QueryCallback, this );\n\n\t\t\tDrawBounds( m_draw, box, b2_colorWhite );\n\t\t}\n\n\t\t// m_startPoint = {-1.0f, 0.5f};\n\t\t// m_endPoint = {7.0f, 0.5f};\n\n\t\tif ( m_rayDrag )\n\t\t{\n\t\t\tb2RayCastInput input = { m_startPoint, b2Sub( m_endPoint, m_startPoint ), 1.0f };\n\t\t\tb2TreeStats result = b2DynamicTree_RayCast( &m_tree, &input, B2_DEFAULT_MASK_BITS, RayCallback, this );\n\n\t\t\tDrawLine( m_draw, m_startPoint, m_endPoint, b2_colorWhite );\n\t\t\tDrawPoint( m_draw, m_startPoint, 5.0f, b2_colorGreen );\n\t\t\tDrawPoint( m_draw, m_endPoint, 5.0f, b2_colorRed );\n\n\t\t\tDrawTextLine( \"node visits = %d, leaf visits = %d\", result.nodeVisits, result.leafVisits );\n\t\t}\n\n\t\tb2HexColor c = b2_colorBlue;\n\t\tb2HexColor qc = b2_colorGreen;\n\n\t\tconst b2Vec2 aabbMargin = { 0.1f, 0.1f };\n\n\t\tfor ( int i = 0; i < m_proxyCount; ++i )\n\t\t{\n\t\t\tProxy* p = m_proxies + i;\n\n\t\t\tif ( p->queryStamp == m_timeStamp || p->rayStamp == m_timeStamp )\n\t\t\t{\n\t\t\t\tDrawBounds( m_draw, p->box, qc );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDrawBounds( m_draw, p->box, c );\n\t\t\t}\n\n\t\t\tfloat moveTest = RandomFloatRange( 0.0f, 1.0f );\n\t\t\tif ( m_moveFraction > moveTest )\n\t\t\t{\n\t\t\t\tfloat dx = m_moveDelta * RandomFloat();\n\t\t\t\tfloat dy = m_moveDelta * RandomFloat();\n\n\t\t\t\tp->position.x += dx;\n\t\t\t\tp->position.y += dy;\n\n\t\t\t\tp->box.lowerBound.x = p->position.x + dx;\n\t\t\t\tp->box.lowerBound.y = p->position.y + dy;\n\t\t\t\tp->box.upperBound.x = p->position.x + dx + p->width.x;\n\t\t\t\tp->box.upperBound.y = p->position.y + dy + p->width.y;\n\n\t\t\t\tif ( b2AABB_Contains( p->fatBox, p->box ) == false )\n\t\t\t\t{\n\t\t\t\t\tp->fatBox.lowerBound = b2Sub( p->box.lowerBound, aabbMargin );\n\t\t\t\t\tp->fatBox.upperBound = b2Add( p->box.upperBound, aabbMargin );\n\t\t\t\t\tp->moved = true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tp->moved = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tp->moved = false;\n\t\t\t}\n\t\t}\n\n\t\tswitch ( m_updateType )\n\t\t{\n\t\t\tcase Update_Incremental:\n\t\t\t{\n\t\t\t\tuint64_t ticks = b2GetTicks();\n\t\t\t\tfor ( int i = 0; i < m_proxyCount; ++i )\n\t\t\t\t{\n\t\t\t\t\tProxy* p = m_proxies + i;\n\t\t\t\t\tif ( p->moved )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2DynamicTree_MoveProxy( &m_tree, p->proxyId, p->fatBox );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfloat ms = b2GetMilliseconds( ticks );\n\t\t\t\tDrawTextLine( \"incremental : %.3f ms\", ms );\n\t\t\t}\n\t\t\tbreak;\n\n\t\t\tcase Update_FullRebuild:\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i < m_proxyCount; ++i )\n\t\t\t\t{\n\t\t\t\t\tProxy* p = m_proxies + i;\n\t\t\t\t\tif ( p->moved )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2DynamicTree_EnlargeProxy( &m_tree, p->proxyId, p->fatBox );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tuint64_t ticks = b2GetTicks();\n\t\t\t\tint boxCount = b2DynamicTree_Rebuild( &m_tree, true );\n\t\t\t\tfloat ms = b2GetMilliseconds( ticks );\n\t\t\t\tDrawTextLine( \"full build %d : %.3f ms\", boxCount, ms );\n\t\t\t}\n\t\t\tbreak;\n\n\t\t\tcase Update_PartialRebuild:\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i < m_proxyCount; ++i )\n\t\t\t\t{\n\t\t\t\t\tProxy* p = m_proxies + i;\n\t\t\t\t\tif ( p->moved )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2DynamicTree_EnlargeProxy( &m_tree, p->proxyId, p->fatBox );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tuint64_t ticks = b2GetTicks();\n\t\t\t\tint boxCount = b2DynamicTree_Rebuild( &m_tree, false );\n\t\t\t\tfloat ms = b2GetMilliseconds( ticks );\n\t\t\t\tDrawTextLine( \"partial rebuild %d : %.3f ms\", boxCount, ms );\n\t\t\t}\n\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\tint height = b2DynamicTree_GetHeight( &m_tree );\n\t\tfloat areaRatio = b2DynamicTree_GetAreaRatio( &m_tree );\n\n\t\tint hmin = (int)( ceilf( logf( (float)m_proxyCount ) / logf( 2.0f ) - 1.0f ) );\n\t\tDrawTextLine( \"proxies = %d, height = %d, hmin = %d, area ratio = %.1f\", m_proxyCount, height, hmin, areaRatio );\n\n\t\tb2DynamicTree_Validate( &m_tree );\n\n\t\tm_timeStamp += 1;\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new DynamicTree( context );\n\t}\n\n\tb2DynamicTree m_tree;\n\tint m_rowCount, m_columnCount;\n\tProxy* m_proxies;\n\tint* m_moveBuffer;\n\tint m_moveCount;\n\tint m_proxyCapacity;\n\tint m_proxyCount;\n\tint m_timeStamp;\n\tint m_updateType;\n\tfloat m_fill;\n\tfloat m_moveFraction;\n\tfloat m_moveDelta;\n\tfloat m_ratio;\n\tfloat m_grid;\n\n\tb2Vec2 m_startPoint;\n\tb2Vec2 m_endPoint;\n\n\tbool m_rayDrag;\n\tbool m_queryDrag;\n\tbool m_validate;\n};\n\nstatic bool QueryCallback( int proxyId, uint64_t userData, void* context )\n{\n\tDynamicTree* sample = static_cast<DynamicTree*>( context );\n\tProxy* proxy = sample->m_proxies + userData;\n\tassert( proxy->proxyId == proxyId );\n\tproxy->queryStamp = sample->m_timeStamp;\n\treturn true;\n}\n\nstatic float RayCallback( const b2RayCastInput* input, int proxyId, uint64_t userData, void* context )\n{\n\tDynamicTree* sample = static_cast<DynamicTree*>( context );\n\tProxy* proxy = sample->m_proxies + userData;\n\tassert( proxy->proxyId == proxyId );\n\tproxy->rayStamp = sample->m_timeStamp;\n\treturn input->maxFraction;\n}\n\nstatic int sampleDynamicTree = RegisterSample( \"Collision\", \"Dynamic Tree\", DynamicTree::Create );\n\nclass RayCast : public Sample\n{\npublic:\n\texplicit RayCast( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 20.0f };\n\t\t\tm_context->camera.zoom = 17.5f;\n\t\t}\n\n\t\tm_circle = { { 0.0f, 0.0f }, 2.0f };\n\t\tm_capsule = { { -1.0f, 1.0f }, { 1.0f, -1.0f }, 1.5f };\n\t\tm_box = b2MakeBox( 2.0f, 2.0f );\n\n\t\tb2Vec2 vertices[3] = { { -2.0f, 0.0f }, { 2.0f, 0.0f }, { 2.0f, 3.0f } };\n\t\tb2Hull hull = b2ComputeHull( vertices, 3 );\n\t\tm_triangle = b2MakePolygon( &hull, 0.0f );\n\n\t\tm_segment = { { -3.0f, 0.0f }, { 3.0f, 0.0f } };\n\n\t\tm_transform = b2Transform_identity;\n\t\tm_angle = 0.0f;\n\n\t\tm_basePosition = { 0.0f, 0.0f };\n\t\tm_baseAngle = 0.0f;\n\t\tm_startPosition = { 0.0f, 0.0f };\n\n\t\tm_rayStart = { 0.0f, 30.0f };\n\t\tm_rayEnd = { 0.0f, 0.0f };\n\n\t\tm_rayDrag = false;\n\t\tm_translating = false;\n\t\tm_rotating = false;\n\n\t\tm_showFraction = false;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 230.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );\n\n\t\tImGui::Begin( \"Ray-cast\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tImGui::PushItemWidth( 100.0f );\n\n\t\tImGui::SliderFloat( \"x offset\", &m_transform.p.x, -2.0f, 2.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"y offset\", &m_transform.p.y, -2.0f, 2.0f, \"%.2f\" );\n\n\t\tif ( ImGui::SliderFloat( \"angle\", &m_angle, -B2_PI, B2_PI, \"%.2f\" ) )\n\t\t{\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\n\t\t// if (ImGui::SliderFloat(\"ray radius\", &m_rayRadius, 0.0f, 1.0f, \"%.1f\"))\n\t\t//{\n\t\t// }\n\n\t\tImGui::Checkbox( \"show fraction\", &m_showFraction );\n\n\t\tif ( ImGui::Button( \"Reset\" ) )\n\t\t{\n\t\t\tm_transform = b2Transform_identity;\n\t\t\tm_angle = 0.0f;\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::Text( \"mouse btn 1: ray cast\" );\n\t\tImGui::Text( \"mouse btn 1 + shft: translate\" );\n\t\tImGui::Text( \"mouse btn 1 + ctrl: rotate\" );\n\n\t\tImGui::PopItemWidth();\n\n\t\tImGui::End();\n\t}\n\n\tvoid MouseDown( b2Vec2 p, int button, int mods ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tm_startPosition = p;\n\n\t\t\tif ( mods == 0 )\n\t\t\t{\n\t\t\t\tm_rayStart = p;\n\t\t\t\tm_rayDrag = true;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_SHIFT )\n\t\t\t{\n\t\t\t\tm_translating = true;\n\t\t\t\tm_basePosition = m_transform.p;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_CONTROL )\n\t\t\t{\n\t\t\t\tm_rotating = true;\n\t\t\t\tm_baseAngle = m_angle;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseUp( b2Vec2, int button ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tm_rayDrag = false;\n\t\t\tm_rotating = false;\n\t\t\tm_translating = false;\n\t\t}\n\t}\n\n\tvoid MouseMove( b2Vec2 p ) override\n\t{\n\t\tif ( m_rayDrag )\n\t\t{\n\t\t\tm_rayEnd = p;\n\t\t}\n\t\telse if ( m_translating )\n\t\t{\n\t\t\tm_transform.p.x = m_basePosition.x + 0.5f * ( p.x - m_startPosition.x );\n\t\t\tm_transform.p.y = m_basePosition.y + 0.5f * ( p.y - m_startPosition.y );\n\t\t}\n\t\telse if ( m_rotating )\n\t\t{\n\t\t\tfloat dx = p.x - m_startPosition.x;\n\t\t\tm_angle = b2ClampFloat( m_baseAngle + 0.5f * dx, -B2_PI, B2_PI );\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\t}\n\n\tvoid DrawRay( const b2CastOutput* output )\n\t{\n\t\tb2Vec2 p1 = m_rayStart;\n\t\tb2Vec2 p2 = m_rayEnd;\n\t\tb2Vec2 d = b2Sub( p2, p1 );\n\n\t\tif ( output->hit )\n\t\t{\n\t\t\tb2Vec2 p;\n\n\t\t\tif ( output->fraction == 0.0f )\n\t\t\t{\n\t\t\t\tassert( output->normal.x == 0.0f && output->normal.y == 0.0f );\n\t\t\t\tp = output->point;\n\t\t\t\tDrawPoint( m_draw, output->point, 5.0, b2_colorPeru );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tp = b2MulAdd( p1, output->fraction, d );\n\t\t\t\tDrawLine( m_draw, p1, p, b2_colorWhite );\n\t\t\t\tDrawPoint( m_draw, p1, 5.0f, b2_colorGreen );\n\t\t\t\tDrawPoint( m_draw, output->point, 5.0f, b2_colorWhite );\n\n\t\t\t\tb2Vec2 n = b2MulAdd( p, 1.0f, output->normal );\n\t\t\t\tDrawLine( m_draw, p, n, b2_colorViolet );\n\t\t\t}\n\n\t\t\tif ( m_showFraction )\n\t\t\t{\n\t\t\t\tb2Vec2 ps = { p.x + 0.05f, p.y - 0.02f };\n\t\t\t\tDrawWorldString( m_draw, m_camera, ps, b2_colorWhite, \"%.2f\", output->fraction );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDrawLine( m_draw, p1, p2, b2_colorWhite );\n\t\t\tDrawPoint( m_draw, p1, 5.0f, b2_colorGreen );\n\t\t\tDrawPoint( m_draw, p2, 5.0f, b2_colorRed );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tb2Vec2 offset = { -20.0f, 20.0f };\n\t\tb2Vec2 increment = { 10.0f, 0.0f };\n\n\t\tb2HexColor color1 = b2_colorYellow;\n\n\t\tb2CastOutput output = {};\n\t\tfloat maxFraction = 1.0f;\n\n\t\t// circle\n\t\t{\n\t\t\tb2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\tb2Vec2 center = b2TransformPoint( transform, m_circle.center );\n\t\t\tDrawSolidCircle( m_draw, { center, transform.q }, m_circle.radius, color1 );\n\n\t\t\tb2Vec2 start = b2InvTransformPoint( transform, m_rayStart );\n\t\t\tb2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) );\n\t\t\tb2RayCastInput input = { start, translation, maxFraction };\n\n\t\t\tb2CastOutput localOutput = b2RayCastCircle( &m_circle, &input );\n\t\t\tif ( localOutput.hit )\n\t\t\t{\n\t\t\t\toutput = localOutput;\n\t\t\t\toutput.point = b2TransformPoint( transform, localOutput.point );\n\t\t\t\toutput.normal = b2RotateVector( transform.q, localOutput.normal );\n\t\t\t\tmaxFraction = localOutput.fraction;\n\t\t\t}\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// capsule\n\t\t{\n\t\t\tb2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\tb2Vec2 v1 = b2TransformPoint( transform, m_capsule.center1 );\n\t\t\tb2Vec2 v2 = b2TransformPoint( transform, m_capsule.center2 );\n\t\t\tDrawSolidCapsule( m_draw, v1, v2, m_capsule.radius, color1 );\n\n\t\t\tb2Vec2 start = b2InvTransformPoint( transform, m_rayStart );\n\t\t\tb2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) );\n\t\t\tb2RayCastInput input = { start, translation, maxFraction };\n\n\t\t\tb2CastOutput localOutput = b2RayCastCapsule( &m_capsule, &input );\n\t\t\tif ( localOutput.hit )\n\t\t\t{\n\t\t\t\toutput = localOutput;\n\t\t\t\toutput.point = b2TransformPoint( transform, localOutput.point );\n\t\t\t\toutput.normal = b2RotateVector( transform.q, localOutput.normal );\n\t\t\t\tmaxFraction = localOutput.fraction;\n\t\t\t}\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// box\n\t\t{\n\t\t\tb2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\tDrawSolidPolygon( m_draw, transform, m_box.vertices, m_box.count, 0.0f, color1 );\n\n\t\t\tb2Vec2 start = b2InvTransformPoint( transform, m_rayStart );\n\t\t\tb2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) );\n\t\t\tb2RayCastInput input = { start, translation, maxFraction };\n\n\t\t\tb2CastOutput localOutput = b2RayCastPolygon( &m_box, &input );\n\t\t\tif ( localOutput.hit )\n\t\t\t{\n\t\t\t\toutput = localOutput;\n\t\t\t\toutput.point = b2TransformPoint( transform, localOutput.point );\n\t\t\t\toutput.normal = b2RotateVector( transform.q, localOutput.normal );\n\t\t\t\tmaxFraction = localOutput.fraction;\n\t\t\t}\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// triangle\n\t\t{\n\t\t\tb2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\tDrawSolidPolygon( m_draw, transform, m_triangle.vertices, m_triangle.count, 0.0f, color1 );\n\n\t\t\tb2Vec2 start = b2InvTransformPoint( transform, m_rayStart );\n\t\t\tb2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) );\n\t\t\tb2RayCastInput input = { start, translation, maxFraction };\n\n\t\t\tb2CastOutput localOutput = b2RayCastPolygon( &m_triangle, &input );\n\t\t\tif ( localOutput.hit )\n\t\t\t{\n\t\t\t\toutput = localOutput;\n\t\t\t\toutput.point = b2TransformPoint( transform, localOutput.point );\n\t\t\t\toutput.normal = b2RotateVector( transform.q, localOutput.normal );\n\t\t\t\tmaxFraction = localOutput.fraction;\n\t\t\t}\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// segment\n\t\t{\n\t\t\tb2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Vec2 p1 = b2TransformPoint( transform, m_segment.point1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( transform, m_segment.point2 );\n\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\n\t\t\tb2Vec2 start = b2InvTransformPoint( transform, m_rayStart );\n\t\t\tb2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) );\n\t\t\tb2RayCastInput input = { start, translation, maxFraction };\n\n\t\t\tb2CastOutput localOutput = b2RayCastSegment( &m_segment, &input, false );\n\t\t\tif ( localOutput.hit )\n\t\t\t{\n\t\t\t\toutput = localOutput;\n\t\t\t\toutput.point = b2TransformPoint( transform, localOutput.point );\n\t\t\t\toutput.normal = b2RotateVector( transform.q, localOutput.normal );\n\t\t\t\tmaxFraction = localOutput.fraction;\n\t\t\t}\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\tDrawRay( &output );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new RayCast( context );\n\t}\n\n\tb2Polygon m_box;\n\tb2Polygon m_triangle;\n\tb2Circle m_circle;\n\tb2Capsule m_capsule;\n\tb2Segment m_segment;\n\n\tb2Transform m_transform;\n\tfloat m_angle;\n\n\tb2Vec2 m_rayStart;\n\tb2Vec2 m_rayEnd;\n\n\tb2Vec2 m_basePosition;\n\tfloat m_baseAngle;\n\n\tb2Vec2 m_startPosition;\n\n\tbool m_rayDrag;\n\tbool m_translating;\n\tbool m_rotating;\n\tbool m_showFraction;\n};\n\nstatic int sampleIndex = RegisterSample( \"Collision\", \"Ray Cast\", RayCast::Create );\n\n// This shows how to filter a specific shape using using data.\nstruct ShapeUserData\n{\n\tint index;\n\tbool ignore;\n};\n\n// Context for ray cast callbacks. Do what you want with this.\nstruct CastContext\n{\n\tb2Vec2 points[3];\n\tb2Vec2 normals[3];\n\tfloat fractions[3];\n\tint count;\n};\n\n// This callback finds the closest hit. This is the most common callback used in games.\nstatic float RayCastClosestCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )\n{\n\tCastContext* rayContext = (CastContext*)context;\n\n\tShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );\n\n\t// Ignore a specific shape. Also ignore initial overlap.\n\tif ( ( userData != nullptr && userData->ignore ) || fraction == 0.0f )\n\t{\n\t\t// By returning -1, we instruct the calling code to ignore this shape and\n\t\t// continue the ray-cast to the next shape.\n\t\treturn -1.0f;\n\t}\n\n\trayContext->points[0] = point;\n\trayContext->normals[0] = normal;\n\trayContext->fractions[0] = fraction;\n\trayContext->count = 1;\n\n\t// By returning the current fraction, we instruct the calling code to clip the ray and\n\t// continue the ray-cast to the next shape. WARNING: do not assume that shapes\n\t// are reported in order. However, by clipping, we can always get the closest shape.\n\treturn fraction;\n}\n\n// This callback finds any hit. For this type of query we are usually just checking for obstruction,\n// so the hit data is not relevant.\n// NOTE: shape hits are not ordered, so this may not return the closest hit\nstatic float RayCastAnyCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )\n{\n\tCastContext* rayContext = (CastContext*)context;\n\n\tShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );\n\n\t// Ignore a specific shape. Also ignore initial overlap.\n\tif ( ( userData != nullptr && userData->ignore ) || fraction == 0.0f )\n\t{\n\t\t// By returning -1, we instruct the calling code to ignore this shape and\n\t\t// continue the ray-cast to the next shape.\n\t\treturn -1.0f;\n\t}\n\n\trayContext->points[0] = point;\n\trayContext->normals[0] = normal;\n\trayContext->fractions[0] = fraction;\n\trayContext->count = 1;\n\n\t// At this point we have a hit, so we know the ray is obstructed.\n\t// By returning 0, we instruct the calling code to terminate the ray-cast.\n\treturn 0.0f;\n}\n\n// This ray cast collects multiple hits along the ray.\n// The shapes are not necessary reported in order, so we might not capture\n// the closest shape.\n// NOTE: shape hits are not ordered, so this may return hits in any order. This means that\n// if you limit the number of results, you may discard the closest hit. You can see this\n// behavior in the sample.\nstatic float RayCastMultipleCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )\n{\n\tCastContext* rayContext = (CastContext*)context;\n\n\tShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );\n\n\t// Ignore a specific shape. Also ignore initial overlap.\n\tif ( ( userData != nullptr && userData->ignore ) || fraction == 0.0f )\n\t{\n\t\t// By returning -1, we instruct the calling code to ignore this shape and\n\t\t// continue the ray-cast to the next shape.\n\t\treturn -1.0f;\n\t}\n\n\tint count = rayContext->count;\n\tassert( count < 3 );\n\n\trayContext->points[count] = point;\n\trayContext->normals[count] = normal;\n\trayContext->fractions[count] = fraction;\n\trayContext->count = count + 1;\n\n\tif ( rayContext->count == 3 )\n\t{\n\t\t// At this point the buffer is full.\n\t\t// By returning 0, we instruct the calling code to terminate the ray-cast.\n\t\treturn 0.0f;\n\t}\n\n\t// By returning 1, we instruct the caller to continue without clipping the ray.\n\treturn 1.0f;\n}\n\n// This ray cast collects multiple hits along the ray and sorts them.\nstatic float RayCastSortedCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )\n{\n\tCastContext* rayContext = (CastContext*)context;\n\n\tShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );\n\n\t// Ignore a specific shape. Also ignore initial overlap.\n\tif ( ( userData != nullptr && userData->ignore ) || fraction == 0.0f )\n\t{\n\t\t// By returning -1, we instruct the calling code to ignore this shape and\n\t\t// continue the ray-cast to the next shape.\n\t\treturn -1.0f;\n\t}\n\n\tint count = rayContext->count;\n\tassert( count <= 3 );\n\n\tint index = 3;\n\twhile ( fraction < rayContext->fractions[index - 1] )\n\t{\n\t\tindex -= 1;\n\n\t\tif ( index == 0 )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif ( index == 3 )\n\t{\n\t\t// not closer, continue but tell the caller not to consider fractions further than the largest fraction acquired\n\t\t// this only happens once the buffer is full\n\t\tassert( rayContext->count == 3 );\n\t\tassert( rayContext->fractions[2] <= 1.0f );\n\t\treturn rayContext->fractions[2];\n\t}\n\n\tfor ( int j = 2; j > index; --j )\n\t{\n\t\trayContext->points[j] = rayContext->points[j - 1];\n\t\trayContext->normals[j] = rayContext->normals[j - 1];\n\t\trayContext->fractions[j] = rayContext->fractions[j - 1];\n\t}\n\n\trayContext->points[index] = point;\n\trayContext->normals[index] = normal;\n\trayContext->fractions[index] = fraction;\n\trayContext->count = count < 3 ? count + 1 : 3;\n\n\tif ( rayContext->count == 3 )\n\t{\n\t\treturn rayContext->fractions[2];\n\t}\n\n\t// By returning 1, we instruct the caller to continue without clipping the ray.\n\treturn 1.0f;\n}\n\n// This sample shows how to use the ray and shape cast functions on a b2World. This\n// sample is configured to ignore initial overlap.\nclass CastWorld : public Sample\n{\npublic:\n\tenum Mode\n\t{\n\t\te_any = 0,\n\t\te_closest = 1,\n\t\te_multiple = 2,\n\t\te_sorted = 3\n\t};\n\n\tenum CastType\n\t{\n\t\te_rayCast = 0,\n\t\te_circleCast = 1,\n\t\te_capsuleCast = 2,\n\t\te_polygonCast = 3\n\t};\n\n\tenum\n\t{\n\t\te_maxCount = 64\n\t};\n\n\texplicit CastWorld( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 2.0f, 14.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.75f;\n\t\t}\n\n\t\t// Ground body\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2Vec2 vertices[3] = { { -0.1f, 0.0f }, { 0.1f, 0.0f }, { 0.0f, 1.5f } };\n\t\t\tb2Hull hull = b2ComputeHull( vertices, 3 );\n\t\t\tm_polygons[0] = b2MakePolygon( &hull, 0.0f );\n\t\t\tm_polygons[0].radius = 0.5f;\n\t\t}\n\n\t\t{\n\t\t\tfloat w = 1.0f;\n\t\t\tfloat b = w / ( 2.0f + sqrtf( 2.0f ) );\n\t\t\tfloat s = sqrtf( 2.0f ) * b;\n\n\t\t\tb2Vec2 vertices[8] = { { 0.5f * s, 0.0f }, { 0.5f * w, b },\t\t { 0.5f * w, b + s }, { 0.5f * s, w },\n\t\t\t\t\t\t\t\t   { -0.5f * s, w },   { -0.5f * w, b + s }, { -0.5f * w, b },\t  { -0.5f * s, 0.0f } };\n\n\t\t\tb2Hull hull = b2ComputeHull( vertices, 8 );\n\t\t\tm_polygons[1] = b2MakePolygon( &hull, 0.0f );\n\t\t}\n\n\t\tm_box = b2MakeBox( 0.5f, 0.5f );\n\t\tm_capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\t\tm_circle = { { 0.0f, 0.0f }, 0.5f };\n\t\tm_segment = { { -1.0f, 0.0f }, { 1.0f, 0.0f } };\n\n\t\tm_bodyIndex = 0;\n\n\t\tfor ( int i = 0; i < e_maxCount; ++i )\n\t\t{\n\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t}\n\n\t\tm_mode = e_closest;\n\t\tm_ignoreIndex = 7;\n\n\t\tm_castType = e_rayCast;\n\t\tm_castRadius = 0.5f;\n\n\t\tm_rayStart = { -20.0f, 10.0f };\n\t\tm_rayEnd = { 20.0f, 10.0f };\n\t\tm_dragging = false;\n\n\t\tm_angle = 0.0f;\n\t\tm_baseAngle = 0.0f;\n\t\tm_angleAnchor = { 0.0f, 0.0f };\n\t\tm_rotating = false;\n\n\t\tm_simple = false;\n\t}\n\n\tvoid Create( int index )\n\t{\n\t\tif ( B2_IS_NON_NULL( m_bodyIds[m_bodyIndex] ) )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyIds[m_bodyIndex] );\n\t\t\tm_bodyIds[m_bodyIndex] = b2_nullBodyId;\n\t\t}\n\n\t\tfloat x = RandomFloatRange( -20.0f, 20.0f );\n\t\tfloat y = RandomFloatRange( 0.0f, 20.0f );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.position = { x, y };\n\t\tbodyDef.rotation = b2MakeRot( RandomFloatRange( -B2_PI, B2_PI ) );\n\n\t\tint mod = m_bodyIndex % 3;\n\t\tif ( mod == 0 )\n\t\t{\n\t\t\tbodyDef.type = b2_staticBody;\n\t\t}\n\t\telse if ( mod == 1 )\n\t\t{\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t}\n\t\telse if ( mod == 2 )\n\t\t{\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.gravityScale = 0.0f;\n\t\t}\n\n\t\tm_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.userData = m_userData + m_bodyIndex;\n\t\tm_userData[m_bodyIndex].ignore = false;\n\t\tif ( m_bodyIndex == m_ignoreIndex )\n\t\t{\n\t\t\tm_userData[m_bodyIndex].ignore = true;\n\t\t}\n\n\t\tif ( index == 0 )\n\t\t{\n\t\t\tint polygonIndex = ( m_bodyIndex & 1 );\n\t\t\tb2CreatePolygonShape( m_bodyIds[m_bodyIndex], &shapeDef, m_polygons + polygonIndex );\n\t\t}\n\t\telse if ( index == 1 )\n\t\t{\n\t\t\tb2CreatePolygonShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_box );\n\t\t}\n\t\telse if ( index == 2 )\n\t\t{\n\t\t\tb2CreateCircleShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_circle );\n\t\t}\n\t\telse if ( index == 3 )\n\t\t{\n\t\t\tb2CreateCapsuleShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_capsule );\n\t\t}\n\t\telse if ( index == 4 )\n\t\t{\n\t\t\tb2CreateSegmentShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_segment );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb2Vec2 points[4] = { { 1.0f, 0.0f }, { -1.0f, 0.0f }, { -1.0f, -1.0f }, { 1.0f, -1.0f } };\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = 4;\n\t\t\tchainDef.isLoop = true;\n\t\t\tb2CreateChain( m_bodyIds[m_bodyIndex], &chainDef );\n\t\t}\n\n\t\tm_bodyIndex = ( m_bodyIndex + 1 ) % e_maxCount;\n\t}\n\n\tvoid CreateN( int index, int count )\n\t{\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tCreate( index );\n\t\t}\n\t}\n\n\tvoid DestroyBody()\n\t{\n\t\tfor ( int i = 0; i < e_maxCount; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_bodyIds[i] ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseDown( b2Vec2 p, int button, int mods ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tif ( mods == 0 && m_rotating == false )\n\t\t\t{\n\t\t\t\tm_rayStart = p;\n\t\t\t\tm_rayEnd = p;\n\t\t\t\tm_dragging = true;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_SHIFT && m_dragging == false )\n\t\t\t{\n\t\t\t\tm_rotating = true;\n\t\t\t\tm_angleAnchor = p;\n\t\t\t\tm_baseAngle = m_angle;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseUp( b2Vec2, int button ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tm_dragging = false;\n\t\t\tm_rotating = false;\n\t\t}\n\t}\n\n\tvoid MouseMove( b2Vec2 p ) override\n\t{\n\t\tif ( m_dragging )\n\t\t{\n\t\t\tm_rayEnd = p;\n\t\t}\n\t\telse if ( m_rotating )\n\t\t{\n\t\t\tfloat dx = p.x - m_angleAnchor.x;\n\t\t\tm_angle = m_baseAngle + 1.0f * dx;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 320.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );\n\n\t\tImGui::Begin( \"Ray-cast World\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tImGui::Checkbox( \"Simple\", &m_simple );\n\n\t\tif ( m_simple == false )\n\t\t{\n\t\t\tconst char* castTypes[] = { \"Ray\", \"Circle\", \"Capsule\", \"Polygon\" };\n\t\t\tint castType = int( m_castType );\n\t\t\tif ( ImGui::Combo( \"Type\", &castType, castTypes, IM_ARRAYSIZE( castTypes ) ) )\n\t\t\t{\n\t\t\t\tm_castType = CastType( castType );\n\t\t\t}\n\n\t\t\tif ( m_castType != e_rayCast )\n\t\t\t{\n\t\t\t\tImGui::SliderFloat( \"Radius\", &m_castRadius, 0.0f, 2.0f, \"%.1f\" );\n\t\t\t}\n\n\t\t\tconst char* modes[] = { \"Any\", \"Closest\", \"Multiple\", \"Sorted\" };\n\t\t\tint mode = int( m_mode );\n\t\t\tif ( ImGui::Combo( \"Mode\", &mode, modes, IM_ARRAYSIZE( modes ) ) )\n\t\t\t{\n\t\t\t\tm_mode = Mode( mode );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::Button( \"Polygon\" ) )\n\t\t\tCreate( 0 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Poly\" ) )\n\t\t\tCreateN( 0, 10 );\n\n\t\tif ( ImGui::Button( \"Box\" ) )\n\t\t\tCreate( 1 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Box\" ) )\n\t\t\tCreateN( 1, 10 );\n\n\t\tif ( ImGui::Button( \"Circle\" ) )\n\t\t\tCreate( 2 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Circle\" ) )\n\t\t\tCreateN( 2, 10 );\n\n\t\tif ( ImGui::Button( \"Capsule\" ) )\n\t\t\tCreate( 3 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Capsule\" ) )\n\t\t\tCreateN( 3, 10 );\n\n\t\tif ( ImGui::Button( \"Segment\" ) )\n\t\t\tCreate( 4 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Segment\" ) )\n\t\t\tCreateN( 4, 10 );\n\n\t\tif ( ImGui::Button( \"Chain\" ) )\n\t\t\tCreate( 5 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Chain\" ) )\n\t\t\tCreateN( 5, 10 );\n\n\t\tif ( ImGui::Button( \"Destroy Shape\" ) )\n\t\t{\n\t\t\tDestroyBody();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tDrawTextLine( \"Click left mouse button and drag to modify ray cast\" );\n\t\tDrawTextLine( \"Shape 7 is intentionally ignored by the ray\" );\n\n\t\tb2HexColor color1 = b2_colorGreen;\n\t\tb2HexColor color2 = b2_colorLightGray;\n\t\tb2HexColor color3 = b2_colorMagenta;\n\n\t\tb2Vec2 rayTranslation = b2Sub( m_rayEnd, m_rayStart );\n\n\t\tif ( m_simple )\n\t\t{\n\t\t\tDrawTextLine( \"Simple closest point ray cast\" );\n\n\t\t\t// This version doesn't have a callback, but it doesn't skip the ignored shape\n\t\t\tb2RayResult result = b2World_CastRayClosest( m_worldId, m_rayStart, rayTranslation, b2DefaultQueryFilter() );\n\n\t\t\tif ( result.hit == true && result.fraction > 0.0f )\n\t\t\t{\n\t\t\t\tb2Vec2 c = b2MulAdd( m_rayStart, result.fraction, rayTranslation );\n\t\t\t\tDrawPoint( m_draw, result.point, 5.0f, color1 );\n\t\t\t\tDrawLine( m_draw, m_rayStart, c, color2 );\n\t\t\t\tb2Vec2 head = b2MulAdd( result.point, 0.5f, result.normal );\n\t\t\t\tDrawLine( m_draw, result.point, head, color3 );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDrawLine( m_draw, m_rayStart, m_rayEnd, color2 );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswitch ( m_mode )\n\t\t\t{\n\t\t\t\tcase e_any:\n\t\t\t\t\tDrawTextLine( \"Cast mode: any - check for obstruction - unsorted\" );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase e_closest:\n\t\t\t\t\tDrawTextLine( \"Cast mode: closest - find closest shape along the cast\" );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase e_multiple:\n\t\t\t\t\tDrawTextLine( \"Cast mode: multiple - gather up to 3 shapes - unsorted\" );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase e_sorted:\n\t\t\t\t\tDrawTextLine( \"Cast mode: sorted - gather up to 3 shapes sorted by closeness\" );\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tassert( false );\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tb2CastResultFcn* functions[] = {\n\t\t\t\tRayCastAnyCallback,\n\t\t\t\tRayCastClosestCallback,\n\t\t\t\tRayCastMultipleCallback,\n\t\t\t\tRayCastSortedCallback,\n\t\t\t};\n\t\t\tb2CastResultFcn* modeFcn = functions[m_mode];\n\n\t\t\tCastContext context = {};\n\n\t\t\t// Must initialize fractions for sorting\n\t\t\tcontext.fractions[0] = FLT_MAX;\n\t\t\tcontext.fractions[1] = FLT_MAX;\n\t\t\tcontext.fractions[2] = FLT_MAX;\n\n\t\t\tb2Transform transform = { m_rayStart, b2MakeRot( m_angle ) };\n\t\t\tb2Circle circle = { .center = m_rayStart, .radius = m_castRadius };\n\t\t\tb2Capsule capsule = { b2TransformPoint( transform, { -0.25f, 0.0f } ), b2TransformPoint( transform, { 0.25f, 0.0f } ),\n\t\t\t\t\t\t\t\t  m_castRadius };\n\t\t\tb2Polygon box = b2MakeOffsetRoundedBox( 0.125f, 0.25f, transform.p, transform.q, m_castRadius );\n\t\t\tb2ShapeProxy proxy = {};\n\n\t\t\tif ( m_castType == e_rayCast )\n\t\t\t{\n\t\t\t\tb2World_CastRay( m_worldId, m_rayStart, rayTranslation, b2DefaultQueryFilter(), modeFcn, &context );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ( m_castType == e_circleCast )\n\t\t\t\t{\n\t\t\t\t\tproxy = b2MakeProxy( &circle.center, 1, circle.radius );\n\t\t\t\t}\n\t\t\t\telse if ( m_castType == e_capsuleCast )\n\t\t\t\t{\n\t\t\t\t\tproxy = b2MakeProxy( &capsule.center1, 2, capsule.radius );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tproxy = b2MakeProxy( box.vertices, box.count, box.radius );\n\t\t\t\t}\n\n\t\t\t\tb2World_CastShape( m_worldId, &proxy, rayTranslation, b2DefaultQueryFilter(), modeFcn, &context );\n\t\t\t}\n\n\t\t\tif ( context.count > 0 )\n\t\t\t{\n\t\t\t\tassert( context.count <= 3 );\n\t\t\t\tb2HexColor colors[3] = { b2_colorRed, b2_colorGreen, b2_colorBlue };\n\t\t\t\tfor ( int i = 0; i < context.count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2Vec2 c = b2MulAdd( m_rayStart, context.fractions[i], rayTranslation );\n\t\t\t\t\tb2Vec2 p = context.points[i];\n\t\t\t\t\tb2Vec2 n = context.normals[i];\n\t\t\t\t\tDrawPoint( m_draw, p, 5.0f, colors[i] );\n\t\t\t\t\tDrawLine( m_draw, m_rayStart, c, color2 );\n\t\t\t\t\tb2Vec2 head = b2MulAdd( p, 1.0f, n );\n\t\t\t\t\tDrawLine( m_draw, p, head, color3 );\n\n\t\t\t\t\tb2Vec2 t = b2MulSV( context.fractions[i], rayTranslation );\n\t\t\t\t\tb2Transform shiftedTransform = { t, b2Rot_identity };\n\n\t\t\t\t\tif ( m_castType == e_circleCast )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Vec2 center = b2TransformPoint( shiftedTransform, circle.center );\n\t\t\t\t\t\tDrawSolidCircle( m_draw, { center, shiftedTransform.q }, m_castRadius, b2_colorYellow );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( m_castType == e_capsuleCast )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Vec2 p1 = capsule.center1 + t;\n\t\t\t\t\t\tb2Vec2 p2 = capsule.center2 + t;\n\t\t\t\t\t\tDrawSolidCapsule( m_draw, p1, p2, m_castRadius, b2_colorYellow );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( m_castType == e_polygonCast )\n\t\t\t\t\t{\n\t\t\t\t\t\tDrawSolidPolygon( m_draw, shiftedTransform, box.vertices, box.count, box.radius, b2_colorYellow );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDrawLine( m_draw, m_rayStart, m_rayEnd, color2 );\n\t\t\t\tb2Transform shiftedTransform = { rayTranslation, b2Rot_identity };\n\n\t\t\t\tif ( m_castType == e_circleCast )\n\t\t\t\t{\n\t\t\t\t\tb2Vec2 center = b2TransformPoint( shiftedTransform, circle.center );\n\t\t\t\t\tDrawSolidCircle( m_draw, { center, shiftedTransform.q }, m_castRadius, b2_colorGray );\n\t\t\t\t}\n\t\t\t\telse if ( m_castType == e_capsuleCast )\n\t\t\t\t{\n\t\t\t\t\tb2Vec2 p1 = capsule.center1 + rayTranslation;\n\t\t\t\t\tb2Vec2 p2 = capsule.center2 + rayTranslation;\n\t\t\t\t\tDrawSolidCapsule( m_draw, p1, p2, m_castRadius, b2_colorYellow );\n\t\t\t\t}\n\t\t\t\telse if ( m_castType == e_polygonCast )\n\t\t\t\t{\n\t\t\t\t\tDrawSolidPolygon( m_draw, shiftedTransform, box.vertices, box.count, box.radius, b2_colorYellow );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tDrawPoint( m_draw, m_rayStart, 5.0f, b2_colorGreen );\n\n\t\tif ( B2_IS_NON_NULL( m_bodyIds[m_ignoreIndex] ) )\n\t\t{\n\t\t\tb2Vec2 p = b2Body_GetPosition( m_bodyIds[m_ignoreIndex] );\n\t\t\tp.x -= 0.2f;\n\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \"ign\" );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new CastWorld( context );\n\t}\n\n\tint m_bodyIndex;\n\tb2BodyId m_bodyIds[e_maxCount] = {};\n\tShapeUserData m_userData[e_maxCount] = {};\n\tb2Polygon m_polygons[2] = {};\n\tb2Polygon m_box;\n\tb2Capsule m_capsule;\n\tb2Circle m_circle;\n\tb2Segment m_segment;\n\n\tbool m_simple;\n\n\tint m_mode;\n\tint m_ignoreIndex;\n\n\tCastType m_castType;\n\tfloat m_castRadius;\n\n\tb2Vec2 m_angleAnchor;\n\tfloat m_baseAngle;\n\tfloat m_angle;\n\tbool m_rotating;\n\n\tb2Vec2 m_rayStart;\n\tb2Vec2 m_rayEnd;\n\tbool m_dragging;\n};\n\nstatic int sampleRayCastWorld = RegisterSample( \"Collision\", \"Cast World\", CastWorld::Create );\n\nclass OverlapWorld : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_circleShape = 0,\n\t\te_capsuleShape = 1,\n\t\te_boxShape = 2\n\t};\n\n\tenum\n\t{\n\t\te_maxCount = 64,\n\t\te_maxDoomed = 16,\n\t};\n\n\tstatic bool OverlapResultFcn( b2ShapeId shapeId, void* context )\n\t{\n\t\tShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );\n\t\tif ( userData != nullptr && userData->ignore )\n\t\t{\n\t\t\t// continue the query\n\t\t\treturn true;\n\t\t}\n\n\t\tOverlapWorld* sample = (OverlapWorld*)context;\n\n\t\tif ( sample->m_doomCount < e_maxDoomed )\n\t\t{\n\t\t\tint index = sample->m_doomCount;\n\t\t\tsample->m_doomIds[index] = shapeId;\n\t\t\tsample->m_doomCount += 1;\n\t\t}\n\n\t\t// continue the query\n\t\treturn true;\n\t}\n\n\texplicit OverlapWorld( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 10.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.7f;\n\t\t}\n\n\t\t{\n\t\t\tb2Vec2 vertices[3] = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 1.5f } };\n\t\t\tb2Hull hull = b2ComputeHull( vertices, 3 );\n\t\t\tm_polygons[0] = b2MakePolygon( &hull, 0.0f );\n\t\t}\n\n\t\t{\n\t\t\tb2Vec2 vertices[3] = { { -0.1f, 0.0f }, { 0.1f, 0.0f }, { 0.0f, 1.5f } };\n\t\t\tb2Hull hull = b2ComputeHull( vertices, 3 );\n\t\t\tm_polygons[1] = b2MakePolygon( &hull, 0.0f );\n\t\t}\n\n\t\t{\n\t\t\tfloat w = 1.0f;\n\t\t\tfloat b = w / ( 2.0f + sqrtf( 2.0f ) );\n\t\t\tfloat s = sqrtf( 2.0f ) * b;\n\n\t\t\tb2Vec2 vertices[8] = { { 0.5f * s, 0.0f }, { 0.5f * w, b },\t\t { 0.5f * w, b + s }, { 0.5f * s, w },\n\t\t\t\t\t\t\t\t   { -0.5f * s, w },   { -0.5f * w, b + s }, { -0.5f * w, b },\t  { -0.5f * s, 0.0f } };\n\n\t\t\tb2Hull hull = b2ComputeHull( vertices, 8 );\n\t\t\tm_polygons[2] = b2MakePolygon( &hull, 0.0f );\n\t\t}\n\n\t\tm_polygons[3] = b2MakeBox( 0.5f, 0.5f );\n\t\tm_capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\t\tm_circle = { { 0.0f, 0.0f }, 0.5f };\n\t\tm_segment = { { -1.0f, 0.0f }, { 1.0f, 0.0f } };\n\n\t\tm_bodyIndex = 0;\n\n\t\tfor ( int i = 0; i < e_maxCount; ++i )\n\t\t{\n\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t}\n\n\t\tm_ignoreIndex = 7;\n\n\t\tm_shapeType = e_circleShape;\n\n\t\tm_position = { 0.0f, 10.0f };\n\t\tm_angle = 0.0f;\n\t\tm_dragging = false;\n\t\tm_rotating = false;\n\n\t\tm_doomCount = 0;\n\n\t\tCreateN( 0, 10 );\n\t}\n\n\tvoid Create( int index )\n\t{\n\t\tif ( B2_IS_NON_NULL( m_bodyIds[m_bodyIndex] ) )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyIds[m_bodyIndex] );\n\t\t\tm_bodyIds[m_bodyIndex] = b2_nullBodyId;\n\t\t}\n\n\t\tfloat x = RandomFloatRange( -20.0f, 20.0f );\n\t\tfloat y = RandomFloatRange( 0.0f, 20.0f );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.position = { x, y };\n\t\tbodyDef.rotation = b2MakeRot( RandomFloatRange( -B2_PI, B2_PI ) );\n\n\t\tm_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.userData = m_userData + m_bodyIndex;\n\t\tm_userData[m_bodyIndex].index = m_bodyIndex;\n\t\tm_userData[m_bodyIndex].ignore = false;\n\t\tif ( m_bodyIndex == m_ignoreIndex )\n\t\t{\n\t\t\tm_userData[m_bodyIndex].ignore = true;\n\t\t}\n\n\t\tif ( index < 4 )\n\t\t{\n\t\t\tb2CreatePolygonShape( m_bodyIds[m_bodyIndex], &shapeDef, m_polygons + index );\n\t\t}\n\t\telse if ( index == 4 )\n\t\t{\n\t\t\tb2CreateCircleShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_circle );\n\t\t}\n\t\telse if ( index == 5 )\n\t\t{\n\t\t\tb2CreateCapsuleShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_capsule );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb2CreateSegmentShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_segment );\n\t\t}\n\n\t\tm_bodyIndex = ( m_bodyIndex + 1 ) % e_maxCount;\n\t}\n\n\tvoid CreateN( int index, int count )\n\t{\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tCreate( index );\n\t\t}\n\t}\n\n\tvoid DestroyBody()\n\t{\n\t\tfor ( int i = 0; i < e_maxCount; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_bodyIds[i] ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseDown( b2Vec2 p, int button, int mods ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tif ( mods == 0 && m_rotating == false )\n\t\t\t{\n\t\t\t\tm_dragging = true;\n\t\t\t\tm_position = p;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_SHIFT && m_dragging == false )\n\t\t\t{\n\t\t\t\tm_rotating = true;\n\t\t\t\tm_startPosition = p;\n\t\t\t\tm_baseAngle = m_angle;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseUp( b2Vec2, int button ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tm_dragging = false;\n\t\t\tm_rotating = false;\n\t\t}\n\t}\n\n\tvoid MouseMove( b2Vec2 p ) override\n\t{\n\t\tif ( m_dragging )\n\t\t{\n\t\t\tm_position = p;\n\t\t}\n\t\telse if ( m_rotating )\n\t\t{\n\t\t\tfloat dx = p.x - m_startPosition.x;\n\t\t\tm_angle = m_baseAngle + 1.0f * dx;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 330.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 140.0f, height ) );\n\n\t\tImGui::Begin( \"Overlap World\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Button( \"Polygon 1\" ) )\n\t\t\tCreate( 0 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Poly1\" ) )\n\t\t\tCreateN( 0, 10 );\n\n\t\tif ( ImGui::Button( \"Polygon 2\" ) )\n\t\t\tCreate( 1 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Poly2\" ) )\n\t\t\tCreateN( 1, 10 );\n\n\t\tif ( ImGui::Button( \"Polygon 3\" ) )\n\t\t\tCreate( 2 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Poly3\" ) )\n\t\t\tCreateN( 2, 10 );\n\n\t\tif ( ImGui::Button( \"Box\" ) )\n\t\t\tCreate( 3 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Box\" ) )\n\t\t\tCreateN( 3, 10 );\n\n\t\tif ( ImGui::Button( \"Circle\" ) )\n\t\t\tCreate( 4 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Circle\" ) )\n\t\t\tCreateN( 4, 10 );\n\n\t\tif ( ImGui::Button( \"Capsule\" ) )\n\t\t\tCreate( 5 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Capsule\" ) )\n\t\t\tCreateN( 5, 10 );\n\n\t\tif ( ImGui::Button( \"Segment\" ) )\n\t\t\tCreate( 6 );\n\t\tImGui::SameLine();\n\t\tif ( ImGui::Button( \"10x##Segment\" ) )\n\t\t\tCreateN( 6, 10 );\n\n\t\tif ( ImGui::Button( \"Destroy Shape\" ) )\n\t\t{\n\t\t\tDestroyBody();\n\t\t}\n\n\t\tImGui::Separator();\n\t\tImGui::Text( \"Overlap Shape\" );\n\t\tImGui::RadioButton( \"Circle##Overlap\", &m_shapeType, e_circleShape );\n\t\tImGui::RadioButton( \"Capsule##Overlap\", &m_shapeType, e_capsuleShape );\n\t\tImGui::RadioButton( \"Box##Overlap\", &m_shapeType, e_boxShape );\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tDrawTextLine( \"left mouse button: drag query shape\" );\n\t\tDrawTextLine( \"left mouse button + shift: rotate query shape\" );\n\n\t\tm_doomCount = 0;\n\n\t\tb2Transform transform = { m_position, b2MakeRot( m_angle ) };\n\t\tb2ShapeProxy proxy = {};\n\n\t\tif ( m_shapeType == e_circleShape )\n\t\t{\n\t\t\tb2Circle circle = {\n\t\t\t\t.center = transform.p,\n\t\t\t\t.radius = 1.0f,\n\t\t\t};\n\t\t\tproxy = b2MakeProxy( &circle.center, 1, circle.radius );\n\t\t\tDrawSolidCircle( m_draw, { circle.center, b2Rot_identity }, circle.radius, b2_colorWhite );\n\t\t}\n\t\telse if ( m_shapeType == e_capsuleShape )\n\t\t{\n\t\t\tb2Capsule capsule = {\n\t\t\t\t.center1 = b2TransformPoint( transform, { -1.0f, 0.0f } ),\n\t\t\t\t.center2 = b2TransformPoint( transform, { 1.0f, 0.0f } ),\n\t\t\t\t.radius = 0.5f,\n\t\t\t};\n\t\t\tproxy = b2MakeProxy( &capsule.center1, 2, capsule.radius );\n\t\t\tDrawSolidCapsule( m_draw, capsule.center1, capsule.center2, capsule.radius, b2_colorWhite );\n\t\t}\n\t\telse if ( m_shapeType == e_boxShape )\n\t\t{\n\t\t\tb2Polygon box = b2MakeOffsetBox( 2.0f, 0.5f, transform.p, transform.q );\n\t\t\tproxy = b2MakeProxy( box.vertices, box.count, box.radius );\n\t\t\tDrawPolygon( m_draw, box.vertices, box.count, b2_colorWhite );\n\t\t}\n\n\t\tb2World_OverlapShape( m_worldId, &proxy, b2DefaultQueryFilter(), OverlapResultFcn, this );\n\n\t\tif ( B2_IS_NON_NULL( m_bodyIds[m_ignoreIndex] ) )\n\t\t{\n\t\t\tb2Vec2 p = b2Body_GetPosition( m_bodyIds[m_ignoreIndex] );\n\t\t\tp.x -= 0.2f;\n\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \"skip\" );\n\t\t}\n\n\t\tfor ( int i = 0; i < m_doomCount; ++i )\n\t\t{\n\t\t\tb2ShapeId shapeId = m_doomIds[i];\n\t\t\tShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId );\n\t\t\tif ( userData == nullptr )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tint index = userData->index;\n\t\t\tassert( 0 <= index && index < e_maxCount );\n\t\t\tassert( B2_IS_NON_NULL( m_bodyIds[index] ) );\n\n\t\t\tb2DestroyBody( m_bodyIds[index] );\n\t\t\tm_bodyIds[index] = b2_nullBodyId;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new OverlapWorld( context );\n\t}\n\n\tint m_bodyIndex;\n\tb2BodyId m_bodyIds[e_maxCount];\n\tShapeUserData m_userData[e_maxCount];\n\tb2Polygon m_polygons[4];\n\tb2Capsule m_capsule;\n\tb2Circle m_circle;\n\tb2Segment m_segment;\n\tint m_ignoreIndex;\n\n\tb2ShapeId m_doomIds[e_maxDoomed];\n\tint m_doomCount;\n\n\tint m_shapeType;\n\tb2Transform m_transform;\n\n\tb2Vec2 m_startPosition;\n\n\tb2Vec2 m_position;\n\tb2Vec2 m_basePosition;\n\tfloat m_angle;\n\tfloat m_baseAngle;\n\n\tbool m_dragging;\n\tbool m_rotating;\n};\n\nstatic int sampleOverlapWorld = RegisterSample( \"Collision\", \"Overlap World\", OverlapWorld::Create );\n\n// Tests manifolds and contact points\nclass Manifold : public Sample\n{\npublic:\n\texplicit Manifold( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\t// m_context->camera.m_center = {1.8f, 15.0f};\n\t\t\tm_context->camera.center = { 1.8f, 0.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.45f;\n\t\t}\n\n\t\tm_smgroxCache1 = b2_emptySimplexCache;\n\t\tm_smgroxCache2 = b2_emptySimplexCache;\n\t\tm_smgcapCache1 = b2_emptySimplexCache;\n\t\tm_smgcapCache2 = b2_emptySimplexCache;\n\n\t\tm_transform = b2Transform_identity;\n\t\tm_transform.p.x = 0.17f;\n\t\tm_transform.p.y = 1.12f;\n\t\t// m_transform.q = b2MakeRot( 0.5f * b2_pi );\n\t\tm_angle = 0.0f;\n\t\tm_round = 0.1f;\n\n\t\tm_startPoint = { 0.0f, 0.0f };\n\t\tm_basePosition = { 0.0f, 0.0f };\n\t\tm_baseAngle = 0.0f;\n\n\t\tm_dragging = false;\n\t\tm_rotating = false;\n\t\tm_showCount = false;\n\t\tm_showIds = false;\n\t\tm_showSeparation = false;\n\t\tm_showAnchors = false;\n\t\tm_enableCaching = true;\n\n\t\tb2Vec2 points[3] = { { -0.1f, -0.5f }, { 0.1f, -0.5f }, { 0.0f, 0.5f } };\n\t\tm_wedge = b2ComputeHull( points, 3 );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 24.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 20.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Manifold\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tImGui::PushItemWidth( 14.0f * fontSize );\n\n\t\tImGui::SliderFloat( \"x offset\", &m_transform.p.x, -2.0f, 2.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"y offset\", &m_transform.p.y, -2.0f, 2.0f, \"%.2f\" );\n\n\t\tif ( ImGui::SliderFloat( \"angle\", &m_angle, -B2_PI, B2_PI, \"%.2f\" ) )\n\t\t{\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\n\t\tImGui::SliderFloat( \"round\", &m_round, 0.0f, 0.4f, \"%.1f\" );\n\n\t\tImGui::PopItemWidth();\n\n\t\tImGui::Separator();\n\n\t\tImGui::Checkbox( \"show count\", &m_showCount );\n\t\tImGui::Checkbox( \"show ids\", &m_showIds );\n\t\tImGui::Checkbox( \"show separation\", &m_showSeparation );\n\t\tImGui::Checkbox( \"show anchors\", &m_showAnchors );\n\t\tImGui::Checkbox( \"enable caching\", &m_enableCaching );\n\n\t\tif ( ImGui::Button( \"Reset\" ) )\n\t\t{\n\t\t\tm_transform = b2Transform_identity;\n\t\t\tm_angle = 0.0f;\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::Text( \"mouse button 1: drag\" );\n\t\tImGui::Text( \"mouse button 1 + shift: rotate\" );\n\n\t\tImGui::End();\n\t}\n\n\tvoid MouseDown( b2Vec2 p, int button, int mods ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tif ( mods == 0 && m_rotating == false )\n\t\t\t{\n\t\t\t\tm_dragging = true;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_basePosition = m_transform.p;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_SHIFT && m_dragging == false )\n\t\t\t{\n\t\t\t\tm_rotating = true;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_baseAngle = m_angle;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseUp( b2Vec2, int button ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tm_dragging = false;\n\t\t\tm_rotating = false;\n\t\t}\n\t}\n\n\tvoid MouseMove( b2Vec2 p ) override\n\t{\n\t\tif ( m_dragging )\n\t\t{\n\t\t\tm_transform.p.x = m_basePosition.x + 0.5f * ( p.x - m_startPoint.x );\n\t\t\tm_transform.p.y = m_basePosition.y + 0.5f * ( p.y - m_startPoint.y );\n\t\t}\n\t\telse if ( m_rotating )\n\t\t{\n\t\t\tfloat dx = p.x - m_startPoint.x;\n\t\t\tm_angle = b2ClampFloat( m_baseAngle + 1.0f * dx, -B2_PI, B2_PI );\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\t}\n\n\tvoid DrawManifold( const b2Manifold* manifold, b2Vec2 origin1, b2Vec2 origin2 )\n\t{\n\t\tif ( m_showCount )\n\t\t{\n\t\t\tb2Vec2 p = 0.5f * ( origin1 + origin2 );\n\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \"%d\", manifold->pointCount );\n\t\t}\n\n\t\tfor ( int i = 0; i < manifold->pointCount; ++i )\n\t\t{\n\t\t\tconst b2ManifoldPoint* mp = manifold->points + i;\n\n\t\t\tb2Vec2 p1 = mp->point;\n\t\t\tb2Vec2 p2 = b2MulAdd( p1, 0.5f, manifold->normal );\n\t\t\tDrawLine( m_draw, p1, p2, b2_colorViolet );\n\n\t\t\tif ( m_showAnchors )\n\t\t\t{\n\t\t\t\tDrawPoint( m_draw, b2Add( origin1, mp->anchorA ), 5.0f, b2_colorRed );\n\t\t\t\tDrawPoint( m_draw, b2Add( origin2, mp->anchorB ), 5.0f, b2_colorGreen );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDrawPoint( m_draw, p1, 10.0f, b2_colorBlue );\n\t\t\t}\n\n\t\t\tif ( m_showIds )\n\t\t\t{\n\t\t\t\t// uint32_t indexA = mp->id >> 8;\n\t\t\t\t// uint32_t indexB = 0xFF & mp->id;\n\t\t\t\tb2Vec2 p = { p1.x + 0.05f, p1.y - 0.02f };\n\t\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \"0x%04x\", mp->id );\n\t\t\t}\n\n\t\t\tif ( m_showSeparation )\n\t\t\t{\n\t\t\t\tb2Vec2 p = { p1.x + 0.05f, p1.y + 0.03f };\n\t\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \"%.3f\", mp->separation );\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tb2Vec2 offset = { -10.0f, -5.0f };\n\t\tb2Vec2 increment = { 4.0f, 0.0f };\n\n\t\tb2HexColor color1 = b2_colorAquamarine;\n\t\tb2HexColor color2 = b2_colorPaleGoldenRod;\n\n\t\tif ( m_enableCaching == false )\n\t\t{\n\t\t\tm_smgroxCache1 = b2_emptySimplexCache;\n\t\t\tm_smgroxCache2 = b2_emptySimplexCache;\n\t\t\tm_smgcapCache1 = b2_emptySimplexCache;\n\t\t\tm_smgcapCache2 = b2_emptySimplexCache;\n\t\t}\n\n#if 1\n\t\t// circle-circle\n\t\t{\n\t\t\tb2Circle circle1 = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2Circle circle2 = { { 0.0f, 0.0f }, 1.0f };\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m = b2CollideCircles( &circle1, transform1, &circle2, transform2 );\n\n\t\t\tb2Vec2 center1 = b2TransformPoint( transform1, circle1.center );\n\t\t\tDrawSolidCircle( m_draw, { center1, transform1.q }, circle1.radius, color1 );\n\t\t\tb2Vec2 center2 = b2TransformPoint( transform2, circle2.center );\n\t\t\tDrawSolidCircle( m_draw, { center2, transform2.q }, circle2.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// capsule-circle\n\t\t{\n\t\t\tb2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m = b2CollideCapsuleAndCircle( &capsule, transform1, &circle, transform2 );\n\n\t\t\tb2Vec2 v1 = b2TransformPoint( transform1, capsule.center1 );\n\t\t\tb2Vec2 v2 = b2TransformPoint( transform1, capsule.center2 );\n\t\t\tDrawSolidCapsule( m_draw, v1, v2, capsule.radius, color1 );\n\n\t\t\tb2Vec2 center = b2TransformPoint( transform2, circle.center );\n\t\t\tDrawSolidCircle( m_draw, { center, transform2.q }, circle.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// segment-circle\n\t\t{\n\t\t\tb2Segment segment = { { -1.0f, 0.0f }, { 1.0f, 0.0f } };\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m = b2CollideSegmentAndCircle( &segment, transform1, &circle, transform2 );\n\n\t\t\tb2Vec2 p1 = b2TransformPoint( transform1, segment.point1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( transform1, segment.point2 );\n\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\n\t\t\tb2Vec2 center = b2TransformPoint( transform2, circle.center );\n\t\t\tDrawSolidCircle( m_draw, { center, transform2.q }, circle.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// box-circle\n\t\t{\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2Polygon box = b2MakeSquare( 0.5f );\n\t\t\tbox.radius = m_round;\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m = b2CollidePolygonAndCircle( &box, transform1, &circle, transform2 );\n\n\t\t\tDrawSolidPolygon( m_draw, transform1, box.vertices, box.count, m_round, color1 );\n\n\t\t\tb2Vec2 center = b2TransformPoint( transform2, circle.center );\n\t\t\tDrawSolidCircle( m_draw, { center, transform2.q }, circle.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// capsule-capsule\n\t\t{\n\t\t\tb2Capsule capsule1 = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\t\t\tb2Capsule capsule2 = { { 0.25f, 0.0f }, { 1.0f, 0.0f }, 0.1f };\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m = b2CollideCapsules( &capsule1, transform1, &capsule2, transform2 );\n\n\t\t\tb2Vec2 v1 = b2TransformPoint( transform1, capsule1.center1 );\n\t\t\tb2Vec2 v2 = b2TransformPoint( transform1, capsule1.center2 );\n\t\t\tDrawSolidCapsule( m_draw, v1, v2, capsule1.radius, color1 );\n\n\t\t\tv1 = b2TransformPoint( transform2, capsule2.center1 );\n\t\t\tv2 = b2TransformPoint( transform2, capsule2.center2 );\n\t\t\tDrawSolidCapsule( m_draw, v1, v2, capsule2.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// box-capsule\n\t\t{\n\t\t\tb2Capsule capsule = { { -0.4f, 0.0f }, { -0.1f, 0.0f }, 0.1f };\n\t\t\tb2Polygon box = b2MakeOffsetBox( 0.25f, 1.0f, { 1.0f, -1.0f }, b2MakeRot( 0.25f * B2_PI ) );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m = b2CollidePolygonAndCapsule( &box, transform1, &capsule, transform2 );\n\n\t\t\tDrawSolidPolygon( m_draw, transform1, box.vertices, box.count, box.radius, color1 );\n\n\t\t\tb2Vec2 v1 = b2TransformPoint( transform2, capsule.center1 );\n\t\t\tb2Vec2 v2 = b2TransformPoint( transform2, capsule.center2 );\n\t\t\tDrawSolidCapsule( m_draw, v1, v2, capsule.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// segment-capsule\n\t\t{\n\t\t\tb2Segment segment = { { -1.0f, 0.0f }, { 1.0f, 0.0f } };\n\t\t\tb2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m = b2CollideSegmentAndCapsule( &segment, transform1, &capsule, transform2 );\n\n\t\t\tb2Vec2 p1 = b2TransformPoint( transform1, segment.point1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( transform1, segment.point2 );\n\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\n\t\t\tp1 = b2TransformPoint( transform2, capsule.center1 );\n\t\t\tp2 = b2TransformPoint( transform2, capsule.center2 );\n\t\t\tDrawSolidCapsule( m_draw, p1, p2, capsule.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\toffset = { -10.0f, 0.0f };\n#endif\n\n#if 1\n\t\t// square-square\n\t\t{\n\t\t\tb2Polygon box1 = b2MakeSquare( 0.5f );\n\t\t\tb2Polygon box = b2MakeSquare( 0.5f );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m = b2CollidePolygons( &box1, transform1, &box, transform2 );\n\n\t\t\tDrawSolidPolygon( m_draw, transform1, box1.vertices, box1.count, box1.radius, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, box.vertices, box.count, box.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// box-box\n\t\t{\n\t\t\tb2Polygon box1 = b2MakeBox( 2.0f, 0.1f );\n\t\t\tb2Polygon box = b2MakeSquare( 0.25f );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\t// b2Transform transform2 = {b2Add({0.0f, -0.1f}, offset), {0.0f, 1.0f}};\n\n\t\t\tb2Manifold m = b2CollidePolygons( &box1, transform1, &box, transform2 );\n\n\t\t\tDrawSolidPolygon( m_draw, transform1, box1.vertices, box1.count, box1.radius, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, box.vertices, box.count, box.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// box-rox\n\t\t{\n\t\t\tb2Polygon box = b2MakeSquare( 0.5f );\n\t\t\tfloat h = 0.5f - m_round;\n\t\t\tb2Polygon rox = b2MakeRoundedBox( h, h, m_round );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\t// b2Transform transform2 = {b2Add({0.0f, -0.1f}, offset), {0.0f, 1.0f}};\n\n\t\t\tb2Manifold m = b2CollidePolygons( &box, transform1, &rox, transform2 );\n\n\t\t\tDrawSolidPolygon( m_draw, transform1, box.vertices, box.count, box.radius, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, rox.vertices, rox.count, rox.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// rox-rox\n\t\t{\n\t\t\tfloat h = 0.5f - m_round;\n\t\t\tb2Polygon rox = b2MakeRoundedBox( h, h, m_round );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\t// b2Transform transform1 = {{6.48024225f, 2.07872653f}, {-0.938356698f, 0.345668465f}};\n\t\t\t// b2Transform transform2 = {{5.52862263f, 2.51146317f}, {-0.859374702f, -0.511346340f}};\n\n\t\t\tb2Manifold m = b2CollidePolygons( &rox, transform1, &rox, transform2 );\n\n\t\t\tDrawSolidPolygon( m_draw, transform1, rox.vertices, rox.count, rox.radius, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, rox.vertices, rox.count, rox.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// segment-rox\n\t\t{\n\t\t\tb2Segment segment = { { -1.0f, 0.0f }, { 1.0f, 0.0f } };\n\t\t\tfloat h = 0.5f - m_round;\n\t\t\tb2Polygon rox = b2MakeRoundedBox( h, h, m_round );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\t// b2Transform transform2 = {b2Add({-1.44583416f, 0.397352695f}, offset), m_transform.q};\n\n\t\t\tb2Manifold m = b2CollideSegmentAndPolygon( &segment, transform1, &rox, transform2 );\n\n\t\t\tb2Vec2 p1 = b2TransformPoint( transform1, segment.point1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( transform1, segment.point2 );\n\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, rox.vertices, rox.count, rox.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n#endif\n\n\t\t// wox-wox\n\t\t{\n\t\t\tb2Polygon wox = b2MakePolygon( &m_wedge, m_round );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\t// b2Transform transform2 = {b2Add({0.0f, -0.1f}, offset), {0.0f, 1.0f}};\n\n\t\t\tb2Manifold m = b2CollidePolygons( &wox, transform1, &wox, transform2 );\n\n\t\t\tDrawSolidPolygon( m_draw, transform1, wox.vertices, wox.count, wox.radius, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform1, wox.vertices, wox.count, 0.0f, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, wox.vertices, wox.count, wox.radius, color2 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, wox.vertices, wox.count, 0.0f, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n#if 1\n\t\t// wox-wox\n\t\t{\n\t\t\tb2Vec2 p1s[3] = { { 0.175740838, 0.224936664 }, { -0.301293969, 0.194021404 }, { -0.105151534, -0.432157338 } };\n\t\t\tb2Vec2 p2s[3] = { { -0.427884758, -0.225028217 }, { 0.0566576123, -0.128772855 }, { 0.176625848, 0.338923335 } };\n\n\t\t\tb2Hull h1 = b2ComputeHull( p1s, 3 );\n\t\t\tb2Hull h2 = b2ComputeHull( p2s, 3 );\n\t\t\tb2Polygon w1 = b2MakePolygon( &h1, 0.158798501 );\n\t\t\tb2Polygon w2 = b2MakePolygon( &h2, 0.205900759 );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\t// b2Transform transform2 = {b2Add({0.0f, -0.1f}, offset), {0.0f, 1.0f}};\n\n\t\t\tb2Manifold m = b2CollidePolygons( &w1, transform1, &w2, transform2 );\n\n\t\t\tDrawSolidPolygon( m_draw, transform1, w1.vertices, w1.count, w1.radius, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform1, w1.vertices, w1.count, 0.0f, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, w2.vertices, w2.count, w2.radius, color2 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, w2.vertices, w2.count, 0.0f, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\toffset = { -10.0f, 5.0f };\n\n\t\t// box-triangle\n\t\t{\n\t\t\tb2Polygon box = b2MakeBox( 1.0f, 1.0f );\n\t\t\tb2Vec2 points[3] = { { -0.05f, 0.0f }, { 0.05f, 0.0f }, { 0.0f, 0.1f } };\n\t\t\tb2Hull hull = b2ComputeHull( points, 3 );\n\t\t\tb2Polygon tri = b2MakePolygon( &hull, 0.0f );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\t\t\t// b2Transform transform2 = {b2Add({0.0f, -0.1f}, offset), {0.0f, 1.0f}};\n\n\t\t\tb2Manifold m = b2CollidePolygons( &box, transform1, &tri, transform2 );\n\n\t\t\tDrawSolidPolygon( m_draw, transform1, box.vertices, box.count, 0.0f, color1 );\n\t\t\tDrawSolidPolygon( m_draw, transform2, tri.vertices, tri.count, 0.0f, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset = b2Add( offset, increment );\n\t\t}\n\n\t\t// chain-segment vs circle\n\t\t{\n\t\t\tb2ChainSegment segment = { { 2.0f, 1.0f }, { { 1.0f, 1.0f }, { -1.0f, 0.0f } }, { -2.0f, 0.0f }, -1 };\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m = b2CollideChainSegmentAndCircle( &segment, transform1, &circle, transform2 );\n\n\t\t\tb2Vec2 g1 = b2TransformPoint( transform1, segment.ghost1 );\n\t\t\tb2Vec2 g2 = b2TransformPoint( transform1, segment.ghost2 );\n\t\t\tb2Vec2 p1 = b2TransformPoint( transform1, segment.segment.point1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( transform1, segment.segment.point2 );\n\t\t\tDrawLine( m_draw, g1, p1, b2_colorLightGray );\n\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\t\t\tDrawLine( m_draw, p2, g2, b2_colorLightGray );\n\n\t\t\tb2Vec2 center = b2TransformPoint( transform2, circle.center );\n\t\t\tDrawSolidCircle( m_draw, { center, transform2.q }, circle.radius, color2 );\n\n\t\t\tDrawManifold( &m, transform1.p, transform2.p );\n\n\t\t\toffset.x += 2.0f * increment.x;\n\t\t}\n\n\t\t// chain-segment vs rounded polygon\n\t\t{\n\t\t\tb2ChainSegment segment1 = { { 2.0f, 1.0f }, { { 1.0f, 1.0f }, { -1.0f, 0.0f } }, { -2.0f, 0.0f }, -1 };\n\t\t\tb2ChainSegment segment2 = { { 3.0f, 1.0f }, { { 2.0f, 1.0f }, { 1.0f, 1.0f } }, { -1.0f, 0.0f }, -1 };\n\t\t\t// b2ChainSegment segment1 = {{2.0f, 0.0f}, {{1.0f, 0.0f}, {-1.0f, 0.0f}}, {-2.0f, 0.0f}, -1};\n\t\t\t// b2ChainSegment segment2 = {{3.0f, 0.0f}, {{2.0f, 0.0f}, {1.0f, 0.0f}}, {-1.0f, 0.0f}, -1};\n\t\t\t// b2ChainSegment segment1 = {{0.5f, 1.0f}, {{0.0f, 2.0f}, {-0.5f, 1.0f}}, {-1.0f, 0.0f}, -1};\n\t\t\t// b2ChainSegment segment2 = {{1.0f, 0.0f}, {{0.5f, 1.0f}, {0.0f, 2.0f}}, {-0.5f, 1.0f}, -1};\n\t\t\tfloat h = 0.5f - m_round;\n\t\t\tb2Polygon rox = b2MakeRoundedBox( h, h, m_round );\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m1 = b2CollideChainSegmentAndPolygon( &segment1, transform1, &rox, transform2, &m_smgroxCache1 );\n\t\t\tb2Manifold m2 = b2CollideChainSegmentAndPolygon( &segment2, transform1, &rox, transform2, &m_smgroxCache2 );\n\n\t\t\t{\n\t\t\t\tb2Vec2 g2 = b2TransformPoint( transform1, segment1.ghost2 );\n\t\t\t\tb2Vec2 p1 = b2TransformPoint( transform1, segment1.segment.point1 );\n\t\t\t\tb2Vec2 p2 = b2TransformPoint( transform1, segment1.segment.point2 );\n\t\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\t\t\t\tDrawPoint( m_draw, p1, 4.0f, color1 );\n\t\t\t\tDrawPoint( m_draw, p2, 4.0f, color1 );\n\t\t\t\tDrawLine( m_draw, p2, g2, b2_colorLightGray );\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tb2Vec2 g1 = b2TransformPoint( transform1, segment2.ghost1 );\n\t\t\t\tb2Vec2 p1 = b2TransformPoint( transform1, segment2.segment.point1 );\n\t\t\t\tb2Vec2 p2 = b2TransformPoint( transform1, segment2.segment.point2 );\n\t\t\t\tDrawLine( m_draw, g1, p1, b2_colorLightGray );\n\t\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\t\t\t\tDrawPoint( m_draw, p1, 4.0f, color1 );\n\t\t\t\tDrawPoint( m_draw, p2, 4.0f, color1 );\n\t\t\t}\n\n\t\t\tDrawSolidPolygon( m_draw, transform2, rox.vertices, rox.count, rox.radius, color2 );\n\t\t\tDrawPoint( m_draw, b2TransformPoint( transform2, rox.centroid ), 5.0f, b2_colorGainsboro );\n\n\t\t\tDrawManifold( &m1, transform1.p, transform2.p );\n\t\t\tDrawManifold( &m2, transform1.p, transform2.p );\n\n\t\t\toffset.x += 2.0f * increment.x;\n\t\t}\n\n\t\t// chain-segment vs capsule\n\t\t{\n\t\t\tb2ChainSegment segment1 = { { 2.0f, 1.0f }, { { 1.0f, 1.0f }, { -1.0f, 0.0f } }, { -2.0f, 0.0f }, -1 };\n\t\t\tb2ChainSegment segment2 = { { 3.0f, 1.0f }, { { 2.0f, 1.0f }, { 1.0f, 1.0f } }, { -1.0f, 0.0f }, -1 };\n\t\t\tb2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\n\t\t\tb2Transform transform1 = { offset, b2Rot_identity };\n\t\t\tb2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q };\n\n\t\t\tb2Manifold m1 = b2CollideChainSegmentAndCapsule( &segment1, transform1, &capsule, transform2, &m_smgcapCache1 );\n\t\t\tb2Manifold m2 = b2CollideChainSegmentAndCapsule( &segment2, transform1, &capsule, transform2, &m_smgcapCache2 );\n\n\t\t\t{\n\t\t\t\tb2Vec2 g2 = b2TransformPoint( transform1, segment1.ghost2 );\n\t\t\t\tb2Vec2 p1 = b2TransformPoint( transform1, segment1.segment.point1 );\n\t\t\t\tb2Vec2 p2 = b2TransformPoint( transform1, segment1.segment.point2 );\n\t\t\t\t// DrawSegment(g1, p1, b2_colorLightGray);\n\t\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\t\t\t\tDrawPoint( m_draw, p1, 4.0f, color1 );\n\t\t\t\tDrawPoint( m_draw, p2, 4.0f, color1 );\n\t\t\t\tDrawLine( m_draw, p2, g2, b2_colorLightGray );\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tb2Vec2 g1 = b2TransformPoint( transform1, segment2.ghost1 );\n\t\t\t\tb2Vec2 p1 = b2TransformPoint( transform1, segment2.segment.point1 );\n\t\t\t\tb2Vec2 p2 = b2TransformPoint( transform1, segment2.segment.point2 );\n\t\t\t\tDrawLine( m_draw, g1, p1, b2_colorLightGray );\n\t\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\t\t\t\tDrawPoint( m_draw, p1, 4.0f, color1 );\n\t\t\t\tDrawPoint( m_draw, p2, 4.0f, color1 );\n\t\t\t\t// DrawSegment(p2, g2, b2_colorLightGray);\n\t\t\t}\n\n\t\t\tb2Vec2 p1 = b2TransformPoint( transform2, capsule.center1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( transform2, capsule.center2 );\n\t\t\tDrawSolidCapsule( m_draw, p1, p2, capsule.radius, color2 );\n\n\t\t\tDrawPoint( m_draw, b2Lerp( p1, p2, 0.5f ), 5.0f, b2_colorGainsboro );\n\n\t\t\tDrawManifold( &m1, transform1.p, transform2.p );\n\t\t\tDrawManifold( &m2, transform1.p, transform2.p );\n\n\t\t\toffset.x += 2.0f * increment.x;\n\t\t}\n#endif\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Manifold( context );\n\t}\n\n\tb2SimplexCache m_smgroxCache1;\n\tb2SimplexCache m_smgroxCache2;\n\tb2SimplexCache m_smgcapCache1;\n\tb2SimplexCache m_smgcapCache2;\n\n\tb2Hull m_wedge;\n\n\tb2Transform m_transform;\n\tfloat m_angle;\n\tfloat m_round;\n\n\tb2Vec2 m_basePosition;\n\tb2Vec2 m_startPoint;\n\tfloat m_baseAngle;\n\n\tbool m_dragging;\n\tbool m_rotating;\n\tbool m_showCount;\n\tbool m_showIds;\n\tbool m_showAnchors;\n\tbool m_showSeparation;\n\tbool m_enableCaching;\n};\n\nstatic int sampleManifoldIndex = RegisterSample( \"Collision\", \"Manifold\", Manifold::Create );\n\nclass SmoothManifold : public Sample\n{\npublic:\n\tenum ShapeType\n\t{\n\t\te_circleShape = 0,\n\t\te_boxShape\n\t};\n\n\texplicit SmoothManifold( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 2.0f, 20.0f };\n\t\t\tm_context->camera.zoom = 21.0f;\n\t\t}\n\n\t\tm_shapeType = e_boxShape;\n\t\tm_transform = { { 0.0f, 20.0f }, b2Rot_identity };\n\t\tm_angle = 0.0f;\n\t\tm_round = 0.0f;\n\n\t\tm_startPoint = { 0.0f, 00.0f };\n\t\tm_basePosition = { 0.0f, 0.0f };\n\t\tm_baseAngle = 0.0f;\n\n\t\tm_dragging = false;\n\t\tm_rotating = false;\n\t\tm_showIds = false;\n\t\tm_showAnchors = false;\n\t\tm_showSeparation = false;\n\n\t\t// https://betravis.github.io/shape-tools/path-to-polygon/\n\t\tm_count = 36;\n\n\t\tb2Vec2 points[36];\n\t\tpoints[0] = { -20.58325, 14.54175 };\n\t\tpoints[1] = { -21.90625, 15.8645 };\n\t\tpoints[2] = { -24.552, 17.1875 };\n\t\tpoints[3] = { -27.198, 11.89575 };\n\t\tpoints[4] = { -29.84375, 15.8645 };\n\t\tpoints[5] = { -29.84375, 21.15625 };\n\t\tpoints[6] = { -25.875, 23.802 };\n\t\tpoints[7] = { -20.58325, 25.125 };\n\t\tpoints[8] = { -25.875, 29.09375 };\n\t\tpoints[9] = { -20.58325, 31.7395 };\n\t\tpoints[10] = { -11.0089998, 23.2290001 };\n\t\tpoints[11] = { -8.67700005, 21.15625 };\n\t\tpoints[12] = { -6.03125, 21.15625 };\n\t\tpoints[13] = { -7.35424995, 29.09375 };\n\t\tpoints[14] = { -3.38549995, 29.09375 };\n\t\tpoints[15] = { 1.90625, 30.41675 };\n\t\tpoints[16] = { 5.875, 17.1875 };\n\t\tpoints[17] = { 11.16675, 25.125 };\n\t\tpoints[18] = { 9.84375, 29.09375 };\n\t\tpoints[19] = { 13.8125, 31.7395 };\n\t\tpoints[20] = { 21.75, 30.41675 };\n\t\tpoints[21] = { 28.3644981, 26.448 };\n\t\tpoints[22] = { 25.71875, 18.5105 };\n\t\tpoints[23] = { 24.3957481, 13.21875 };\n\t\tpoints[24] = { 17.78125, 11.89575 };\n\t\tpoints[25] = { 15.1355, 7.92700005 };\n\t\tpoints[26] = { 5.875, 9.25 };\n\t\tpoints[27] = { 1.90625, 11.89575 };\n\t\tpoints[28] = { -3.25, 11.89575 };\n\t\tpoints[29] = { -3.25, 9.9375 };\n\t\tpoints[30] = { -4.70825005, 9.25 };\n\t\tpoints[31] = { -8.67700005, 9.25 };\n\t\tpoints[32] = { -11.323, 11.89575 };\n\t\tpoints[33] = { -13.96875, 11.89575 };\n\t\tpoints[34] = { -15.29175, 14.54175 };\n\t\tpoints[35] = { -19.2605, 14.54175 };\n\n\t\tm_segments = (b2ChainSegment*)malloc( m_count * sizeof( b2ChainSegment ) );\n\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tint i0 = i > 0 ? i - 1 : m_count - 1;\n\t\t\tint i1 = i;\n\t\t\tint i2 = i1 < m_count - 1 ? i1 + 1 : 0;\n\t\t\tint i3 = i2 < m_count - 1 ? i2 + 1 : 0;\n\n\t\t\tb2Vec2 g1 = points[i0];\n\t\t\tb2Vec2 p1 = points[i1];\n\t\t\tb2Vec2 p2 = points[i2];\n\t\t\tb2Vec2 g2 = points[i3];\n\n\t\t\tm_segments[i] = { g1, { p1, p2 }, g2, -1 };\n\t\t}\n\t}\n\n\tvirtual ~SmoothManifold() override\n\t{\n\t\tfree( m_segments );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 290.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 180.0f, height ) );\n\n\t\tImGui::Begin( \"Smooth Manifold\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 100.0f );\n\n\t\t{\n\t\t\tconst char* shapeTypes[] = { \"Circle\", \"Box\" };\n\t\t\tint shapeType = int( m_shapeType );\n\t\t\tImGui::Combo( \"Shape\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) );\n\t\t\tm_shapeType = ShapeType( shapeType );\n\t\t}\n\n\t\tImGui::SliderFloat( \"x Offset\", &m_transform.p.x, -2.0f, 2.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"y Offset\", &m_transform.p.y, -2.0f, 2.0f, \"%.2f\" );\n\n\t\tif ( ImGui::SliderFloat( \"Angle\", &m_angle, -B2_PI, B2_PI, \"%.2f\" ) )\n\t\t{\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\n\t\tImGui::SliderFloat( \"Round\", &m_round, 0.0f, 0.4f, \"%.1f\" );\n\t\tImGui::Checkbox( \"Show Ids\", &m_showIds );\n\t\tImGui::Checkbox( \"Show Separation\", &m_showSeparation );\n\t\tImGui::Checkbox( \"Show Anchors\", &m_showAnchors );\n\n\t\tif ( ImGui::Button( \"Reset\" ) )\n\t\t{\n\t\t\tm_transform = b2Transform_identity;\n\t\t\tm_angle = 0.0f;\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::Text( \"mouse button 1: drag\" );\n\t\tImGui::Text( \"mouse button 1 + shift: rotate\" );\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tvoid MouseDown( b2Vec2 p, int button, int mods ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tif ( mods == 0 && m_rotating == false )\n\t\t\t{\n\t\t\t\tm_dragging = true;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_basePosition = m_transform.p;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_SHIFT && m_dragging == false )\n\t\t\t{\n\t\t\t\tm_rotating = true;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_baseAngle = m_angle;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseUp( b2Vec2, int button ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tm_dragging = false;\n\t\t\tm_rotating = false;\n\t\t}\n\t}\n\n\tvoid MouseMove( b2Vec2 p ) override\n\t{\n\t\tif ( m_dragging )\n\t\t{\n\t\t\tm_transform.p.x = m_basePosition.x + ( p.x - m_startPoint.x );\n\t\t\tm_transform.p.y = m_basePosition.y + ( p.y - m_startPoint.y );\n\t\t}\n\t\telse if ( m_rotating )\n\t\t{\n\t\t\tfloat dx = p.x - m_startPoint.x;\n\t\t\tm_angle = b2ClampFloat( m_baseAngle + 1.0f * dx, -B2_PI, B2_PI );\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\t}\n\n\tvoid DrawManifold( const b2Manifold* manifold )\n\t{\n\t\tfor ( int i = 0; i < manifold->pointCount; ++i )\n\t\t{\n\t\t\tconst b2ManifoldPoint* mp = manifold->points + i;\n\n\t\t\tb2Vec2 p1 = mp->point;\n\t\t\tb2Vec2 p2 = b2MulAdd( p1, 0.5f, manifold->normal );\n\t\t\tDrawLine( m_draw, p1, p2, b2_colorWhite );\n\n\t\t\tif ( m_showAnchors )\n\t\t\t{\n\t\t\t\tDrawPoint( m_draw, p1, 5.0f, b2_colorGreen );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDrawPoint( m_draw, p1, 5.0f, b2_colorGreen );\n\t\t\t}\n\n\t\t\tif ( m_showIds )\n\t\t\t{\n\t\t\t\t// uint32_t indexA = mp->id >> 8;\n\t\t\t\t// uint32_t indexB = 0xFF & mp->id;\n\t\t\t\tb2Vec2 p = { p1.x + 0.05f, p1.y - 0.02f };\n\t\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \"0x%04x\", mp->id );\n\t\t\t}\n\n\t\t\tif ( m_showSeparation )\n\t\t\t{\n\t\t\t\tb2Vec2 p = { p1.x + 0.05f, p1.y + 0.03f };\n\t\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \"%.3f\", mp->separation );\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tb2HexColor color1 = b2_colorYellow;\n\t\tb2HexColor color2 = b2_colorMagenta;\n\n\t\tb2Transform transform1 = b2Transform_identity;\n\t\tb2Transform transform2 = m_transform;\n\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tconst b2ChainSegment* segment = m_segments + i;\n\t\t\tb2Vec2 p1 = b2TransformPoint( transform1, segment->segment.point1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( transform1, segment->segment.point2 );\n\t\t\tDrawLine( m_draw, p1, p2, color1 );\n\t\t\tDrawPoint( m_draw, p1, 4.0f, color1 );\n\t\t}\n\n\t\t// chain-segment vs circle\n\t\tif ( m_shapeType == e_circleShape )\n\t\t{\n\t\t\tfloat radius = 0.5f;\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tDrawSolidCircle( m_draw, transform2, circle.radius, color2 );\n\n\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t{\n\t\t\t\tconst b2ChainSegment* segment = m_segments + i;\n\t\t\t\tb2Manifold m = b2CollideChainSegmentAndCircle( segment, transform1, &circle, transform2 );\n\t\t\t\tDrawManifold( &m );\n\t\t\t}\n\t\t}\n\t\telse if ( m_shapeType == e_boxShape )\n\t\t{\n\t\t\tfloat h = 0.5f - m_round;\n\t\t\tb2Polygon rox = b2MakeRoundedBox( h, h, m_round );\n\t\t\tDrawSolidPolygon( m_draw, transform2, rox.vertices, rox.count, rox.radius, color2 );\n\n\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t{\n\t\t\t\tconst b2ChainSegment* segment = m_segments + i;\n\t\t\t\tb2SimplexCache cache = {};\n\t\t\t\tb2Manifold m = b2CollideChainSegmentAndPolygon( segment, transform1, &rox, transform2, &cache );\n\t\t\t\tDrawManifold( &m );\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SmoothManifold( context );\n\t}\n\n\tShapeType m_shapeType;\n\n\tb2ChainSegment* m_segments;\n\tint m_count;\n\n\tb2Transform m_transform;\n\tfloat m_angle;\n\tfloat m_round;\n\n\tb2Vec2 m_basePosition;\n\tb2Vec2 m_startPoint;\n\tfloat m_baseAngle;\n\n\tbool m_dragging;\n\tbool m_rotating;\n\tbool m_showIds;\n\tbool m_showAnchors;\n\tbool m_showSeparation;\n};\n\nstatic int sampleSmoothManifoldIndex = RegisterSample( \"Collision\", \"Smooth Manifold\", SmoothManifold::Create );\n\nclass ShapeCast : public Sample\n{\npublic:\n\tenum ShapeType\n\t{\n\t\te_point,\n\t\te_segment,\n\t\te_triangle,\n\t\te_box\n\t};\n\n\texplicit ShapeCast( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.25f };\n\t\t\tm_context->camera.zoom = 3.0f;\n\t\t}\n\n\t\tm_point = b2Vec2_zero;\n\t\tm_segment = { { 0.0f, 0.0f }, { 0.5f, 0.0f } };\n\n\t\t{\n\t\t\tb2Vec2 points[3] = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 1.0f } };\n\t\t\tb2Hull hull = b2ComputeHull( points, 3 );\n\t\t\tm_triangle = b2MakePolygon( &hull, 0.0f );\n\t\t}\n\n#if 0\n\t\t{\n\t\t\tb2Vec2 points[4] = {};\n\t\t\tpoints[0].x = -0.599999964;\n\t\t\tpoints[0].y = -0.700000048;\n\t\t\tpoints[1].x = 0.449999988;\n\t\t\tpoints[1].y = -0.700000048;\n\t\t\tpoints[2].x = 0.449999988;\n\t\t\tpoints[2].y = 0.350000024;\n\t\t\tpoints[3].x = -0.599999964;\n\t\t\tpoints[3].y = 0.350000024;\n\n\t\t\tpoints[0] = { 3.0, -0.5 };\n\t\t\tpoints[1] = { 3.0, 0.5 };\n\t\t\tpoints[2] = { -3.0, 0.5 };\n\t\t\tpoints[3] = { -3.0, -0.5 };\n\t\t\tb2Hull hull = b2ComputeHull( points, 4 );\n\t\t\tbool isValid = b2ValidateHull( &hull );\n\t\t\tassert( isValid );\n\n\t\t\tm_triangle = b2MakePolygon( &hull, 0.0f );\n\t\t}\n#endif\n\n\t\tm_box = b2MakeOffsetBox( 0.5f, 0.5f, { 0.0f, 0.0f }, b2Rot_identity );\n\n#if 0\n\t\t{\n\t\t\tb2Vec2 points[4] = {};\n\t\t\tpoints[0].x = 0.449999988;\n\t\t\tpoints[0].y = -0.100000001;\n\t\t\tpoints[1].x = 0.550000012;\n\t\t\tpoints[1].y = -0.100000001;\n\t\t\tpoints[2].x = 0.550000012;\n\t\t\tpoints[2].y = 0.100000001;\n\t\t\tpoints[3].x = 0.449999988;\n\t\t\tpoints[3].y = 0.100000001;\n\t\t\tb2Hull hull = b2ComputeHull( points, 4 );\n\t\t\tm_box = b2MakePolygon( &hull, 0.0f );\n\t\t}\n#endif\n\n\t\tm_transform = { { -0.6f, 0.0f }, b2Rot_identity };\n\t\tm_translation = { 2.0f, 0.0f };\n\t\tm_angle = 0.0f;\n\t\tm_startPoint = { 0.0f, 0.0f };\n\t\tm_basePosition = { 0.0f, 0.0f };\n\t\tm_baseAngle = 0.0f;\n\n\t\tm_dragging = false;\n\t\tm_sweeping = false;\n\t\tm_rotating = false;\n\t\tm_showIndices = false;\n\t\tm_drawSimplex = false;\n\t\tm_encroach = false;\n\n\t\tm_typeA = e_box;\n\t\tm_typeB = e_point;\n\t\tm_radiusA = 0.0f;\n\t\tm_radiusB = 0.2f;\n\n\t\tm_proxyA = MakeProxy( m_typeA, m_radiusA );\n\t\tm_proxyB = MakeProxy( m_typeB, m_radiusB );\n\t}\n\n\tb2ShapeProxy MakeProxy( ShapeType type, float radius ) const\n\t{\n\t\tb2ShapeProxy proxy = {};\n\t\tproxy.radius = radius;\n\n\t\tswitch ( type )\n\t\t{\n\t\t\tcase e_point:\n\t\t\t\tproxy.points[0] = b2Vec2_zero;\n\t\t\t\tproxy.count = 1;\n\t\t\t\tbreak;\n\n\t\t\tcase e_segment:\n\t\t\t\tproxy.points[0] = m_segment.point1;\n\t\t\t\tproxy.points[1] = m_segment.point2;\n\t\t\t\tproxy.count = 2;\n\t\t\t\tbreak;\n\n\t\t\tcase e_triangle:\n\t\t\t\tfor ( int i = 0; i < m_triangle.count; ++i )\n\t\t\t\t{\n\t\t\t\t\tproxy.points[i] = m_triangle.vertices[i];\n\t\t\t\t}\n\t\t\t\tproxy.count = m_triangle.count;\n\t\t\t\tbreak;\n\n\t\t\tcase e_box:\n\t\t\t\tproxy.points[0] = m_box.vertices[0];\n\t\t\t\tproxy.points[1] = m_box.vertices[1];\n\t\t\t\tproxy.points[2] = m_box.vertices[2];\n\t\t\t\tproxy.points[3] = m_box.vertices[3];\n\t\t\t\tproxy.count = 4;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tassert( false );\n\t\t}\n\n\t\treturn proxy;\n\t}\n\n\tvoid DrawShape( ShapeType type, b2Transform transform, float radius, b2HexColor color )\n\t{\n\t\tswitch ( type )\n\t\t{\n\t\t\tcase e_point:\n\t\t\t{\n\t\t\t\tb2Vec2 p = b2TransformPoint( transform, m_point );\n\t\t\t\tif ( radius > 0.0f )\n\t\t\t\t{\n\t\t\t\t\tDrawSolidCircle( m_draw, { p, transform.q }, radius, color );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDrawPoint( m_draw, p, 5.0f, color );\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\t\tcase e_segment:\n\t\t\t{\n\t\t\t\tb2Vec2 p1 = b2TransformPoint( transform, m_segment.point1 );\n\t\t\t\tb2Vec2 p2 = b2TransformPoint( transform, m_segment.point2 );\n\n\t\t\t\tif ( radius > 0.0f )\n\t\t\t\t{\n\t\t\t\t\tDrawSolidCapsule( m_draw, p1, p2, radius, color );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDrawLine( m_draw, p1, p2, color );\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\t\tcase e_triangle:\n\t\t\t\tDrawSolidPolygon( m_draw, transform, m_triangle.vertices, m_triangle.count, radius, color );\n\t\t\t\tbreak;\n\n\t\t\tcase e_box:\n\t\t\t\tDrawSolidPolygon( m_draw, transform, m_box.vertices, m_box.count, radius, color );\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tassert( false );\n\t\t}\n\t}\n\n\tvoid MouseDown( b2Vec2 p, int button, int mods ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tif ( mods == 0 )\n\t\t\t{\n\t\t\t\tm_dragging = true;\n\t\t\t\tm_sweeping = false;\n\t\t\t\tm_rotating = false;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_basePosition = m_transform.p;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_SHIFT )\n\t\t\t{\n\t\t\t\tm_dragging = false;\n\t\t\t\tm_sweeping = false;\n\t\t\t\tm_rotating = true;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_baseAngle = m_angle;\n\t\t\t}\n\t\t\telse if ( mods == GLFW_MOD_CONTROL )\n\t\t\t{\n\t\t\t\tm_dragging = false;\n\t\t\t\tm_sweeping = true;\n\t\t\t\tm_rotating = false;\n\t\t\t\tm_startPoint = p;\n\t\t\t\tm_basePosition = b2Vec2_zero;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseUp( b2Vec2, int button ) override\n\t{\n\t\tif ( button == GLFW_MOUSE_BUTTON_1 )\n\t\t{\n\t\t\tm_dragging = false;\n\t\t\tm_sweeping = false;\n\t\t\tm_rotating = false;\n\t\t}\n\t}\n\n\tvoid MouseMove( b2Vec2 p ) override\n\t{\n\t\tif ( m_dragging )\n\t\t{\n\t\t\tm_transform.p = m_basePosition + 0.5f * ( p - m_startPoint );\n\t\t}\n\t\telse if ( m_rotating )\n\t\t{\n\t\t\tfloat dx = p.x - m_startPoint.x;\n\t\t\tm_angle = b2ClampFloat( m_baseAngle + 1.0f * dx, -B2_PI, B2_PI );\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\t\telse if ( m_sweeping )\n\t\t{\n\t\t\tm_translation = p - m_startPoint;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 300.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Shape Distance\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tconst char* shapeTypes[] = { \"point\", \"segment\", \"triangle\", \"box\" };\n\t\tint shapeType = int( m_typeA );\n\t\tif ( ImGui::Combo( \"shape A\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) )\n\t\t{\n\t\t\tm_typeA = ShapeType( shapeType );\n\t\t\tm_proxyA = MakeProxy( m_typeA, m_radiusA );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"radius A\", &m_radiusA, 0.0f, 0.5f, \"%.2f\" ) )\n\t\t{\n\t\t\tm_proxyA.radius = m_radiusA;\n\t\t}\n\n\t\tshapeType = int( m_typeB );\n\t\tif ( ImGui::Combo( \"shape B\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) )\n\t\t{\n\t\t\tm_typeB = ShapeType( shapeType );\n\t\t\tm_proxyB = MakeProxy( m_typeB, m_radiusB );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"radius B\", &m_radiusB, 0.0f, 0.5f, \"%.2f\" ) )\n\t\t{\n\t\t\tm_proxyB.radius = m_radiusB;\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::SliderFloat( \"x offset\", &m_transform.p.x, -2.0f, 2.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"y offset\", &m_transform.p.y, -2.0f, 2.0f, \"%.2f\" );\n\n\t\tif ( ImGui::SliderFloat( \"angle\", &m_angle, -B2_PI, B2_PI, \"%.2f\" ) )\n\t\t{\n\t\t\tm_transform.q = b2MakeRot( m_angle );\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::Checkbox( \"show indices\", &m_showIndices );\n\t\tImGui::Checkbox( \"encroach\", &m_encroach );\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2ShapeCastPairInput input = {};\n\t\tinput.proxyA = m_proxyA;\n\t\tinput.proxyB = m_proxyB;\n\t\tinput.transformA = b2Transform_identity;\n\t\tinput.transformB = m_transform;\n\t\tinput.translationB = m_translation;\n\t\tinput.maxFraction = 1.0f;\n\t\tinput.canEncroach = m_encroach;\n\n\t\tb2CastOutput output = b2ShapeCast( &input );\n\n\t\tb2Transform transform;\n\t\ttransform.q = m_transform.q;\n\t\ttransform.p = b2MulAdd( m_transform.p, output.fraction, input.translationB );\n\n\t\tb2DistanceInput distanceInput;\n\t\tdistanceInput.proxyA = m_proxyA;\n\t\tdistanceInput.proxyB = m_proxyB;\n\t\tdistanceInput.transformA = b2Transform_identity;\n\t\tdistanceInput.transformB = transform;\n\t\tdistanceInput.useRadii = false;\n\t\tb2SimplexCache distanceCache;\n\t\tdistanceCache.count = 0;\n\t\tb2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &distanceCache, nullptr, 0 );\n\n\t\tDrawTextLine( \"hit = %s, iterations = %d, fraction = %g, distance = %g\", output.hit ? \"true\" : \"false\", output.iterations,\n\t\t\t\t\t  output.fraction, distanceOutput.distance );\n\n\t\tDrawShape( m_typeA, b2Transform_identity, m_radiusA, b2_colorCyan );\n\t\tDrawShape( m_typeB, m_transform, m_radiusB, b2_colorLightGreen );\n\t\tb2Transform transform2 = { m_transform.p + m_translation, m_transform.q };\n\t\tDrawShape( m_typeB, transform2, m_radiusB, b2_colorIndianRed );\n\n\t\tif ( output.hit )\n\t\t{\n\t\t\tDrawShape( m_typeB, transform, m_radiusB, b2_colorPlum );\n\n\t\t\tif ( output.fraction > 0.0f )\n\t\t\t{\n\t\t\t\tDrawPoint( m_draw, output.point, 5.0f, b2_colorWhite );\n\t\t\t\tDrawLine( m_draw, output.point, output.point + 0.5f * output.normal, b2_colorYellow );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDrawPoint( m_draw, output.point, 5.0f, b2_colorPeru );\n\t\t\t}\n\t\t}\n\n\t\tif ( m_showIndices )\n\t\t{\n\t\t\tfor ( int i = 0; i < m_proxyA.count; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 p = m_proxyA.points[i];\n\t\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \" %d\", i );\n\t\t\t}\n\n\t\t\tfor ( int i = 0; i < m_proxyB.count; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 p = b2TransformPoint( m_transform, m_proxyB.points[i] );\n\t\t\t\tDrawWorldString( m_draw, m_camera, p, b2_colorWhite, \" %d\", i );\n\t\t\t}\n\t\t}\n\n\t\tDrawTextLine( \"mouse button 1: drag\" );\n\t\tDrawTextLine( \"mouse button 1 + shift: rotate\" );\n\t\tDrawTextLine( \"mouse button 1 + control: sweep\" );\n\t\tDrawTextLine( \"distance = %.2f, iterations = %d\", distanceOutput.distance, output.iterations );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ShapeCast( context );\n\t}\n\n\tb2Polygon m_box;\n\tb2Polygon m_triangle;\n\tb2Vec2 m_point;\n\tb2Segment m_segment;\n\n\tShapeType m_typeA;\n\tShapeType m_typeB;\n\tfloat m_radiusA;\n\tfloat m_radiusB;\n\tb2ShapeProxy m_proxyA;\n\tb2ShapeProxy m_proxyB;\n\n\tb2Transform m_transform;\n\tfloat m_angle;\n\tb2Vec2 m_translation;\n\n\tb2Vec2 m_basePosition;\n\tb2Vec2 m_startPoint;\n\tfloat m_baseAngle;\n\n\tbool m_dragging;\n\tbool m_sweeping;\n\tbool m_rotating;\n\tbool m_showIndices;\n\tbool m_drawSimplex;\n\tbool m_encroach;\n};\n\nstatic int sampleShapeCast = RegisterSample( \"Collision\", \"Shape Cast\", ShapeCast::Create );\n\nclass TimeOfImpact : public Sample\n{\npublic:\n\texplicit TimeOfImpact( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.6f, 2.0f };\n\t\t\tm_context->camera.center = { -16, 45 };\n\t\t\tm_context->camera.zoom = 5.0f;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new TimeOfImpact( context );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2Sweep sweepA = {\n\t\t\tb2Vec2_zero, { 0.0f, 0.0f }, { 0.0f, 0.0f }, b2Rot_identity, b2Rot_identity,\n\t\t};\n\t\tb2Sweep sweepB = {\n\t\t\tb2Vec2_zero,\n\t\t\t{ -15.8332710, 45.3520279 },\n\t\t\t{ -15.8324337, 45.3413048 },\n\t\t\t{ -0.540891349, 0.841092527 },\n\t\t\t{ -0.457797021, 0.889056742 },\n\t\t};\n\n\t\tb2TOIInput input;\n\t\tinput.proxyA = b2MakeProxy( m_verticesA, m_countA, m_radiusA );\n\t\tinput.proxyB = b2MakeProxy( m_verticesB, m_countB, m_radiusB );\n\t\tinput.sweepA = sweepA;\n\t\tinput.sweepB = sweepB;\n\t\tinput.maxFraction = 1.0f;\n\n\t\tb2TOIOutput output = b2TimeOfImpact( &input );\n\n\t\tDrawTextLine( \"toi = %g\", output.fraction );\n\n\t\t// DrawString(5, m_textLine, \"max toi iters = %d, max root iters = %d\", b2_toiMaxIters,\n\t\t//                        b2_toiMaxRootIters);\n\n\t\tb2Vec2 vertices[B2_MAX_POLYGON_VERTICES];\n\n\t\t// Draw A\n\t\tb2Transform transformA = b2GetSweepTransform( &sweepA, 0.0f );\n\t\tfor ( int i = 0; i < m_countA; ++i )\n\t\t{\n\t\t\tvertices[i] = b2TransformPoint( transformA, m_verticesA[i] );\n\t\t}\n\t\tDrawPolygon( m_draw, vertices, m_countA, b2_colorGray );\n\n\t\t// Draw B at t = 0\n\t\tb2Transform transformB = b2GetSweepTransform( &sweepB, 0.0f );\n\t\tfor ( int i = 0; i < m_countB; ++i )\n\t\t{\n\t\t\tvertices[i] = b2TransformPoint( transformB, m_verticesB[i] );\n\t\t}\n\t\tDrawSolidCapsule( m_draw, vertices[0], vertices[1], m_radiusB, b2_colorGreen );\n\t\t// DrawPolygon( vertices, m_countB, b2_colorGreen );\n\n\t\t// Draw B at t = hit_time\n\t\ttransformB = b2GetSweepTransform( &sweepB, output.fraction );\n\t\tfor ( int i = 0; i < m_countB; ++i )\n\t\t{\n\t\t\tvertices[i] = b2TransformPoint( transformB, m_verticesB[i] );\n\t\t}\n\t\tDrawPolygon( m_draw, vertices, m_countB, b2_colorOrange );\n\n\t\t// Draw B at t = 1\n\t\ttransformB = b2GetSweepTransform( &sweepB, 1.0f );\n\t\tfor ( int i = 0; i < m_countB; ++i )\n\t\t{\n\t\t\tvertices[i] = b2TransformPoint( transformB, m_verticesB[i] );\n\t\t}\n\t\tDrawSolidCapsule( m_draw, vertices[0], vertices[1], m_radiusB, b2_colorRed );\n\t\t// DrawPolygon( vertices, m_countB, b2_colorRed );\n\n\t\tif ( output.state == b2_toiStateHit )\n\t\t{\n\t\t\tb2DistanceInput distanceInput;\n\t\t\tdistanceInput.proxyA = input.proxyA;\n\t\t\tdistanceInput.proxyB = input.proxyB;\n\t\t\tdistanceInput.transformA = b2GetSweepTransform( &sweepA, output.fraction );\n\t\t\tdistanceInput.transformB = b2GetSweepTransform( &sweepB, output.fraction );\n\t\t\tdistanceInput.useRadii = false;\n\t\t\tb2SimplexCache cache = { 0 };\n\t\t\tb2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, nullptr, 0 );\n\t\t\tDrawTextLine( \"distance = %g\", distanceOutput.distance );\n\t\t}\n\n#if 0\n\t\tfor (float t = 0.0f; t < 1.0f; t += 0.1f)\n\t\t{\n\t\t\ttransformB = b2GetSweepTransform(&sweepB, t);\n\t\t\tfor (int i = 0; i < m_countB; ++i)\n\t\t\t{\n\t\t\t\tvertices[i] = b2TransformPoint(transformB, m_verticesB[i]);\n\t\t\t}\n\t\t\tDrawPolygon(vertices, m_countB, {0.3f, 0.3f, 0.3f});\n\t\t}\n#endif\n\t}\n\n\tb2Vec2 m_verticesA[4] = { { -16.25, 44.75 }, { -15.75, 44.75 }, { -15.75, 45.25 }, { -16.25, 45.25 } };\n\tb2Vec2 m_verticesB[2] = { { 0.0f, -0.125000000f }, { 0.0f, 0.125000000f } };\n\n\tint m_countA = ARRAY_COUNT( m_verticesA );\n\tint m_countB = ARRAY_COUNT( m_verticesB );\n\n\tfloat m_radiusA = 0.0f;\n\tfloat m_radiusB = 0.0299999993f;\n};\n\nstatic int sampleTimeOfImpact = RegisterSample( \"Collision\", \"Time of Impact\", TimeOfImpact::Create );\n"
  },
  {
    "path": "samples/sample_continuous.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"draw.h\"\n#include \"human.h\"\n#include \"random.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n#include <imgui.h>\n#include <vector>\n\n// extern \"C\" int b2_toiHitCount;\n\nclass BounceHouse : public Sample\n{\npublic:\n\tenum ShapeType\n\t{\n\t\te_circleShape = 0,\n\t\te_capsuleShape,\n\t\te_boxShape\n\t};\n\n\tstruct HitEvent\n\t{\n\t\tb2Vec2 point;\n\t\tfloat speed;\n\t\tint stepIndex;\n\t};\n\n\texplicit BounceHouse( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.45f;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t{\n\t\t\tb2Segment segment = { { -10.0f, -10.0f }, { 10.0f, -10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2Segment segment = { { 10.0f, -10.0f }, { 10.0f, 10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2Segment segment = { { 10.0f, 10.0f }, { -10.0f, 10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2Segment segment = { { -10.0f, 10.0f }, { -10.0f, -10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tm_shapeType = e_circleShape;\n\t\tm_bodyId = b2_nullBodyId;\n\t\tm_enableHitEvents = true;\n\n\t\tmemset( m_hitEvents, 0, sizeof( m_hitEvents ) );\n\n\t\tLaunch();\n\t}\n\n\tvoid Launch()\n\t{\n\t\tif ( B2_IS_NON_NULL( m_bodyId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyId );\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.linearVelocity = { 10.0f, 20.0f };\n\t\tbodyDef.position = { 0.0f, 0.0f };\n\t\tbodyDef.gravityScale = 0.0f;\n\t\tbodyDef.isBullet = true;\n\n\t\t// Circle shapes centered on the body can spin fast without risk of tunnelling.\n\t\tbodyDef.allowFastRotation = m_shapeType == e_circleShape;\n\n\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.restitution = 1.0f;\n\t\tshapeDef.material.friction = 0.0f;\n\t\tshapeDef.enableHitEvents = m_enableHitEvents;\n\n\t\tif ( m_shapeType == e_circleShape )\n\t\t{\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2CreateCircleShape( m_bodyId, &shapeDef, &circle );\n\t\t}\n\t\telse if ( m_shapeType == e_capsuleShape )\n\t\t{\n\t\t\tb2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\t\t\tb2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfloat h = 0.1f;\n\t\t\tb2Polygon box = b2MakeBox( 20.0f * h, h );\n\t\t\tb2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 100.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Bounce House\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tconst char* shapeTypes[] = { \"Circle\", \"Capsule\", \"Box\" };\n\t\tint shapeType = int( m_shapeType );\n\t\tif ( ImGui::Combo( \"Shape\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) )\n\t\t{\n\t\t\tm_shapeType = ShapeType( shapeType );\n\t\t\tLaunch();\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"hit events\", &m_enableHitEvents ) )\n\t\t{\n\t\t\tb2Body_EnableHitEvents( m_bodyId, m_enableHitEvents );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2ContactEvents events = b2World_GetContactEvents( m_worldId );\n\t\tfor ( int i = 0; i < events.hitCount; ++i )\n\t\t{\n\t\t\tb2ContactHitEvent* event = events.hitEvents + i;\n\n\t\t\tHitEvent* e = m_hitEvents + 0;\n\t\t\tfor ( int j = 1; j < 4; ++j )\n\t\t\t{\n\t\t\t\tif ( m_hitEvents[j].stepIndex < e->stepIndex )\n\t\t\t\t{\n\t\t\t\t\te = m_hitEvents + j;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\te->point = event->point;\n\t\t\te->speed = event->approachSpeed;\n\t\t\te->stepIndex = m_stepCount;\n\t\t}\n\n\t\tfor ( int i = 0; i < 4; ++i )\n\t\t{\n\t\t\tHitEvent* e = m_hitEvents + i;\n\t\t\tif ( e->stepIndex > 0 && m_stepCount <= e->stepIndex + 30 )\n\t\t\t{\n\t\t\t\tDrawCircle(m_draw, e->point, 0.1f, b2_colorOrangeRed );\n\t\t\t\tDrawWorldString( m_draw, m_camera, e->point, b2_colorWhite, \"%.1f\", e->speed );\n\t\t\t}\n\t\t}\n\n\t\tif ( m_stepCount == 1000 )\n\t\t{\n\t\t\tm_stepCount += 0;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BounceHouse( context );\n\t}\n\n\tHitEvent m_hitEvents[4];\n\tb2BodyId m_bodyId;\n\tShapeType m_shapeType;\n\tbool m_enableHitEvents;\n};\n\nstatic int sampleBounceHouse = RegisterSample( \"Continuous\", \"Bounce House\", BounceHouse::Create );\n\nclass BounceHumans : public Sample\n{\npublic:\n\texplicit BounceHumans( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\tm_context->camera.zoom = 12.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.restitution = 1.3f;\n\t\tshapeDef.material.friction = 0.1f;\n\n\t\t{\n\t\t\tb2Segment segment = { { -10.0f, -10.0f }, { 10.0f, -10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2Segment segment = { { 10.0f, -10.0f }, { 10.0f, 10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2Segment segment = { { 10.0f, 10.0f }, { -10.0f, 10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2Segment segment = { { -10.0f, 10.0f }, { -10.0f, -10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 2.0f };\n\t\tshapeDef.material.restitution = 2.0f;\n\t\tb2CreateCircleShape( groundId, &shapeDef, &circle );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( m_humanCount < 5 && m_countDown <= 0.0f )\n\t\t{\n\t\t\tfloat jointFrictionTorque = 0.0f;\n\t\t\tfloat jointHertz = 1.0f;\n\t\t\tfloat jointDampingRatio = 0.1f;\n\n\t\t\tCreateHuman( m_humans + m_humanCount, m_worldId, { 0.0f, 5.0f }, 1.0f, jointFrictionTorque, jointHertz,\n\t\t\t\t\t\t jointDampingRatio, 1, nullptr, true );\n\t\t\t// Human_SetVelocity( m_humans + m_humanCount, { 10.0f - 5.0f * m_humanCount, -20.0f + 5.0f * m_humanCount } );\n\n\t\t\tm_countDown = 2.0f;\n\t\t\tm_humanCount += 1;\n\t\t}\n\n\t\tfloat timeStep = 1.0f / 60.0f;\n\t\tb2CosSin cs1 = b2ComputeCosSin( 0.5f * m_time );\n\t\tb2CosSin cs2 = b2ComputeCosSin( m_time );\n\t\tfloat gravity = 10.0f;\n\t\tb2Vec2 gravityVec = { gravity * cs1.sine, gravity * cs2.cosine };\n\t\tDrawLine( m_draw, b2Vec2_zero, b2Vec2{ 3.0f * cs1.sine, 3.0f * cs2.cosine }, b2_colorWhite );\n\t\tm_time += timeStep;\n\t\tm_countDown -= timeStep;\n\t\tb2World_SetGravity( m_worldId, gravityVec );\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BounceHumans( context );\n\t}\n\n\tHuman m_humans[5] = {};\n\tint m_humanCount = 0;\n\tfloat m_countDown = 0.0f;\n\tfloat m_time = 0.0f;\n};\n\nstatic int sampleBounceHumans = RegisterSample( \"Continuous\", \"Bounce Humans\", BounceHumans::Create );\n\nclass ChainDrop : public Sample\n{\npublic:\n\texplicit ChainDrop( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.35f;\n\t\t}\n\n\t\t//\n\t\t// b2World_SetContactTuning( m_worldId, 30.0f, 1.0f, 100.0f );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.position = { 0.0f, -6.0f };\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2Vec2 points[4] = { { -10.0f, -2.0f }, { 10.0f, -2.0f }, { 10.0f, 1.0f }, { -10.0f, 1.0f } };\n\n\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\tchainDef.points = points;\n\t\tchainDef.count = 4;\n\t\tchainDef.isLoop = true;\n\n\t\tb2CreateChain( groundId, &chainDef );\n\n\t\tm_bodyId = b2_nullBodyId;\n\t\tm_yOffset = -0.1f;\n\t\tm_speed = -42.0f;\n\n\t\tLaunch();\n\t}\n\n\tvoid Launch()\n\t{\n\t\tif ( B2_IS_NON_NULL( m_bodyId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyId );\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.linearVelocity = { 0.0f, m_speed };\n\t\tbodyDef.position = { 0.0f, 10.0f + m_yOffset };\n\t\tbodyDef.rotation = b2MakeRot( 0.5f * B2_PI );\n\t\tbodyDef.motionLocks.angularZ = true;\n\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\tm_shapeId = b2CreateCircleShape( m_bodyId, &shapeDef, &circle );\n\n\t\t// b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\t\t// m_shapeId = b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule );\n\n\t\t// float h = 0.5f;\n\t\t// b2Polygon box = b2MakeBox( h, h );\n\t\t// m_shapeId = b2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 140.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Chain Drop\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::SliderFloat( \"Speed\", &m_speed, -100.0f, 0.0f, \"%.0f\" );\n\t\tImGui::SliderFloat( \"Y Offset\", &m_yOffset, -1.0f, 1.0f, \"%.1f\" );\n\n\t\tif ( ImGui::Button( \"Launch\" ) )\n\t\t{\n\t\t\tLaunch();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ChainDrop( context );\n\t}\n\n\tb2BodyId m_bodyId;\n\tb2ShapeId m_shapeId;\n\tfloat m_yOffset;\n\tfloat m_speed;\n};\n\nstatic int sampleChainDrop = RegisterSample( \"Continuous\", \"Chain Drop\", ChainDrop::Create );\n\nclass ChainSlide : public Sample\n{\npublic:\n\texplicit ChainSlide( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 10.0f };\n\t\t\tm_context->camera.zoom = 15.0f;\n\t\t}\n\n\t\t// b2_toiHitCount = 0;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tconstexpr int count = 80;\n\t\t\tb2Vec2 points[count];\n\n\t\t\tfloat w = 2.0f;\n\t\t\tfloat h = 1.0f;\n\t\t\tfloat x = 20.0f, y = 0.0f;\n\t\t\tfor ( int i = 0; i < 20; ++i )\n\t\t\t{\n\t\t\t\tpoints[i] = { x, y };\n\t\t\t\tx -= w;\n\t\t\t}\n\n\t\t\tfor ( int i = 20; i < 40; ++i )\n\t\t\t{\n\t\t\t\tpoints[i] = { x, y };\n\t\t\t\ty += h;\n\t\t\t}\n\n\t\t\tfor ( int i = 40; i < 60; ++i )\n\t\t\t{\n\t\t\t\tpoints[i] = { x, y };\n\t\t\t\tx += w;\n\t\t\t}\n\n\t\t\tfor ( int i = 60; i < 80; ++i )\n\t\t\t{\n\t\t\t\tpoints[i] = { x, y };\n\t\t\t\ty -= h;\n\t\t\t}\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = count;\n\t\t\tchainDef.isLoop = true;\n\n\t\t\tb2CreateChain( groundId, &chainDef );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.linearVelocity = { 100.0f, 0.0f };\n\t\t\tbodyDef.position = { -19.5f, 0.0f + 0.5f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.0f;\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\t// DrawTextLine( \"toi hits = %d\", b2_toiHitCount );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ChainSlide( context );\n\t}\n};\n\nstatic int sampleChainSlide = RegisterSample( \"Continuous\", \"Chain Slide\", ChainSlide::Create );\n\nclass SegmentSlide : public Sample\n{\npublic:\n\texplicit SegmentSlide( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 10.0f };\n\t\t\tm_context->camera.zoom = 15.0f;\n\t\t}\n\n\t\t// b2_toiHitCount = 0;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tsegment = { { 40.0f, 0.0f }, { 40.0f, 10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.linearVelocity = { 100.0f, 0.0f };\n\t\t\tbodyDef.position = { -20.0f, 0.7f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\t// shapeDef.friction = 0.0f;\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\t// DrawTextLine(\"toi hits = %d\", b2_toiHitCount );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SegmentSlide( context );\n\t}\n};\n\nstatic int sampleSegmentSlide = RegisterSample( \"Continuous\", \"Segment Slide\", SegmentSlide::Create );\n\nclass SkinnyBox : public Sample\n{\npublic:\n\texplicit SkinnyBox( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 1.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.25f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.9f;\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 0.1f, 1.0f, { 0.0f, 1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tm_autoTest = false;\n\t\tm_bullet = false;\n\t\tm_capsule = false;\n\n\t\tm_bodyId = b2_nullBodyId;\n\t\tm_bulletId = b2_nullBodyId;\n\n\t\tLaunch();\n\t}\n\n\tvoid Launch()\n\t{\n\t\tif ( B2_IS_NON_NULL( m_bodyId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyId );\n\t\t}\n\n\t\tif ( B2_IS_NON_NULL( m_bulletId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_bulletId );\n\t\t}\n\n\t\tm_angularVelocity = RandomFloatRange( -50.0f, 50.0f );\n\t\t// m_angularVelocity = -30.6695766f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { 0.0f, 8.0f };\n\t\tbodyDef.angularVelocity = m_angularVelocity;\n\t\tbodyDef.linearVelocity = { 0.0f, -100.0f };\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.friction = 0.9f;\n\n\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tif ( m_capsule )\n\t\t{\n\t\t\tb2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 0.1f };\n\t\t\tb2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb2Polygon polygon = b2MakeBox( 2.0f, 0.05f );\n\t\t\tb2CreatePolygonShape( m_bodyId, &shapeDef, &polygon );\n\t\t}\n\n\t\tif ( m_bullet )\n\t\t{\n\t\t\tb2Polygon polygon = b2MakeBox( 0.25f, 0.25f );\n\t\t\tm_x = RandomFloatRange( -1.0f, 1.0f );\n\t\t\tbodyDef.position = { m_x, 10.0f };\n\t\t\tbodyDef.linearVelocity = { 0.0f, -50.0f };\n\t\t\tm_bulletId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bulletId, &shapeDef, &polygon );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 110.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 140.0f, height ) );\n\n\t\tImGui::Begin( \"Skinny Box\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::Checkbox( \"Capsule\", &m_capsule );\n\n\t\tif ( ImGui::Button( \"Launch\" ) )\n\t\t{\n\t\t\tLaunch();\n\t\t}\n\n\t\tImGui::Checkbox( \"Auto Test\", &m_autoTest );\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tif ( m_autoTest && m_stepCount % 60 == 0 )\n\t\t{\n\t\t\tLaunch();\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SkinnyBox( context );\n\t}\n\n\tb2BodyId m_bodyId, m_bulletId;\n\tfloat m_angularVelocity;\n\tfloat m_x;\n\tbool m_capsule;\n\tbool m_autoTest;\n\tbool m_bullet;\n};\n\nstatic int sampleSkinnyBox = RegisterSample( \"Continuous\", \"Skinny Box\", SkinnyBox::Create );\n\n// This sample shows ghost bumps\nclass GhostBumps : public Sample\n{\npublic:\n\tenum ShapeType\n\t{\n\t\te_circleShape = 0,\n\t\te_capsuleShape,\n\t\te_boxShape\n\t};\n\n\texplicit GhostBumps( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 1.5f, 16.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.8f;\n\t\t}\n\n\t\tm_groundId = b2_nullBodyId;\n\t\tm_bodyId = b2_nullBodyId;\n\t\tm_shapeId = b2_nullShapeId;\n\t\tm_shapeType = e_circleShape;\n\t\tm_round = 0.0f;\n\t\tm_friction = 0.2f;\n\t\tm_bevel = 0.0f;\n\t\tm_useChain = true;\n\n\t\tCreateScene();\n\t\tLaunch();\n\t}\n\n\tvoid CreateScene()\n\t{\n\t\tif ( B2_IS_NON_NULL( m_groundId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_groundId );\n\t\t}\n\n\t\tm_shapeId = b2_nullShapeId;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tm_groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tfloat m = 1.0f / sqrt( 2.0f );\n\t\tfloat mm = 2.0f * ( sqrt( 2.0f ) - 1.0f );\n\t\tfloat hx = 4.0f, hy = 0.25f;\n\n\t\tif ( m_useChain )\n\t\t{\n\t\t\tb2Vec2 points[20];\n\t\t\tpoints[0] = { -3.0f * hx, hy };\n\t\t\tpoints[1] = b2Add( points[0], { -2.0f * hx * m, 2.0f * hx * m } );\n\t\t\tpoints[2] = b2Add( points[1], { -2.0f * hx * m, 2.0f * hx * m } );\n\t\t\tpoints[3] = b2Add( points[2], { -2.0f * hx * m, 2.0f * hx * m } );\n\t\t\tpoints[4] = b2Add( points[3], { -2.0f * hy * m, -2.0f * hy * m } );\n\t\t\tpoints[5] = b2Add( points[4], { 2.0f * hx * m, -2.0f * hx * m } );\n\t\t\tpoints[6] = b2Add( points[5], { 2.0f * hx * m, -2.0f * hx * m } );\n\t\t\tpoints[7] =\n\t\t\t\tb2Add( points[6], { 2.0f * hx * m + 2.0f * hy * ( 1.0f - m ), -2.0f * hx * m - 2.0f * hy * ( 1.0f - m ) } );\n\t\t\tpoints[8] = b2Add( points[7], { 2.0f * hx + hy * mm, 0.0f } );\n\t\t\tpoints[9] = b2Add( points[8], { 2.0f * hx, 0.0f } );\n\t\t\tpoints[10] = b2Add( points[9], { 2.0f * hx + hy * mm, 0.0f } );\n\t\t\tpoints[11] =\n\t\t\t\tb2Add( points[10], { 2.0f * hx * m + 2.0f * hy * ( 1.0f - m ), 2.0f * hx * m + 2.0f * hy * ( 1.0f - m ) } );\n\t\t\tpoints[12] = b2Add( points[11], { 2.0f * hx * m, 2.0f * hx * m } );\n\t\t\tpoints[13] = b2Add( points[12], { 2.0f * hx * m, 2.0f * hx * m } );\n\t\t\tpoints[14] = b2Add( points[13], { -2.0f * hy * m, 2.0f * hy * m } );\n\t\t\tpoints[15] = b2Add( points[14], { -2.0f * hx * m, -2.0f * hx * m } );\n\t\t\tpoints[16] = b2Add( points[15], { -2.0f * hx * m, -2.0f * hx * m } );\n\t\t\tpoints[17] = b2Add( points[16], { -2.0f * hx * m, -2.0f * hx * m } );\n\t\t\tpoints[18] = b2Add( points[17], { -2.0f * hx, 0.0f } );\n\t\t\tpoints[19] = b2Add( points[18], { -2.0f * hx, 0.0f } );\n\n\t\t\tb2SurfaceMaterial material = {};\n\t\t\tmaterial.friction = m_friction;\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = 20;\n\t\t\tchainDef.isLoop = true;\n\t\t\tchainDef.materials = &material;\n\t\t\tchainDef.materialCount = 1;\n\n\t\t\tb2CreateChain( m_groundId, &chainDef );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = m_friction;\n\n\t\t\tb2Hull hull = {};\n\n\t\t\tif ( m_bevel > 0.0f )\n\t\t\t{\n\t\t\t\tfloat hb = m_bevel;\n\t\t\t\tb2Vec2 vs[8] = { { hx + hb, hy - 0.05f },\t{ hx, hy },\t  { -hx, hy }, { -hx - hb, hy - 0.05f },\n\t\t\t\t\t\t\t\t { -hx - hb, -hy + 0.05f }, { -hx, -hy }, { hx, -hy }, { hx + hb, -hy + 0.05f } };\n\t\t\t\thull = b2ComputeHull( vs, 8 );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2Vec2 vs[4] = { { hx, hy }, { -hx, hy }, { -hx, -hy }, { hx, -hy } };\n\t\t\t\thull = b2ComputeHull( vs, 4 );\n\t\t\t}\n\n\t\t\tb2Transform transform;\n\t\t\tfloat x, y;\n\n\t\t\t// Left slope\n\t\t\tx = -3.0f * hx - m * hx - m * hy;\n\t\t\ty = hy + m * hx - m * hy;\n\t\t\ttransform.q = b2MakeRot( -0.25f * B2_PI );\n\n\t\t\t{\n\t\t\t\ttransform.p = { x, y };\n\t\t\t\tb2Polygon polygon = b2MakeOffsetPolygon( &hull, transform.p, transform.q );\n\t\t\t\tb2CreatePolygonShape( m_groundId, &shapeDef, &polygon );\n\t\t\t\tx -= 2.0f * m * hx;\n\t\t\t\ty += 2.0f * m * hx;\n\t\t\t}\n\t\t\t{\n\t\t\t\ttransform.p = { x, y };\n\t\t\t\tb2Polygon polygon = b2MakeOffsetPolygon( &hull, transform.p, transform.q );\n\t\t\t\tb2CreatePolygonShape( m_groundId, &shapeDef, &polygon );\n\t\t\t\tx -= 2.0f * m * hx;\n\t\t\t\ty += 2.0f * m * hx;\n\t\t\t}\n\t\t\t{\n\t\t\t\ttransform.p = { x, y };\n\t\t\t\tb2Polygon polygon = b2MakeOffsetPolygon( &hull, transform.p, transform.q );\n\t\t\t\tb2CreatePolygonShape( m_groundId, &shapeDef, &polygon );\n\t\t\t\tx -= 2.0f * m * hx;\n\t\t\t\ty += 2.0f * m * hx;\n\t\t\t}\n\n\t\t\tx = -2.0f * hx;\n\t\t\ty = 0.0f;\n\t\t\ttransform.q = b2MakeRot( 0.0f );\n\n\t\t\t{\n\t\t\t\ttransform.p = { x, y };\n\t\t\t\tb2Polygon polygon = b2MakeOffsetPolygon( &hull, transform.p, transform.q );\n\t\t\t\tb2CreatePolygonShape( m_groundId, &shapeDef, &polygon );\n\t\t\t\tx += 2.0f * hx;\n\t\t\t}\n\t\t\t{\n\t\t\t\ttransform.p = { x, y };\n\t\t\t\tb2Polygon polygon = b2MakeOffsetPolygon( &hull, transform.p, transform.q );\n\t\t\t\tb2CreatePolygonShape( m_groundId, &shapeDef, &polygon );\n\t\t\t\tx += 2.0f * hx;\n\t\t\t}\n\t\t\t{\n\t\t\t\ttransform.p = { x, y };\n\t\t\t\tb2Polygon polygon = b2MakeOffsetPolygon( &hull, transform.p, transform.q );\n\t\t\t\tb2CreatePolygonShape( m_groundId, &shapeDef, &polygon );\n\t\t\t\tx += 2.0f * hx;\n\t\t\t}\n\n\t\t\tx = 3.0f * hx + m * hx + m * hy;\n\t\t\ty = hy + m * hx - m * hy;\n\t\t\ttransform.q = b2MakeRot( 0.25f * B2_PI );\n\n\t\t\t{\n\t\t\t\ttransform.p = { x, y };\n\t\t\t\tb2Polygon polygon = b2MakeOffsetPolygon( &hull, transform.p, transform.q );\n\t\t\t\tb2CreatePolygonShape( m_groundId, &shapeDef, &polygon );\n\t\t\t\tx += 2.0f * m * hx;\n\t\t\t\ty += 2.0f * m * hx;\n\t\t\t}\n\t\t\t{\n\t\t\t\ttransform.p = { x, y };\n\t\t\t\tb2Polygon polygon = b2MakeOffsetPolygon( &hull, transform.p, transform.q );\n\t\t\t\tb2CreatePolygonShape( m_groundId, &shapeDef, &polygon );\n\t\t\t\tx += 2.0f * m * hx;\n\t\t\t\ty += 2.0f * m * hx;\n\t\t\t}\n\t\t\t{\n\t\t\t\ttransform.p = { x, y };\n\t\t\t\tb2Polygon polygon = b2MakeOffsetPolygon( &hull, transform.p, transform.q );\n\t\t\t\tb2CreatePolygonShape( m_groundId, &shapeDef, &polygon );\n\t\t\t\tx += 2.0f * m * hx;\n\t\t\t\ty += 2.0f * m * hx;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid Launch()\n\t{\n\t\tif ( B2_IS_NON_NULL( m_bodyId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyId );\n\t\t\tm_shapeId = b2_nullShapeId;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { -28.0f, 18.0f };\n\t\tbodyDef.linearVelocity = { 0.0f, 0.0f };\n\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.friction = m_friction;\n\n\t\tif ( m_shapeType == e_circleShape )\n\t\t{\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tm_shapeId = b2CreateCircleShape( m_bodyId, &shapeDef, &circle );\n\t\t}\n\t\telse if ( m_shapeType == e_capsuleShape )\n\t\t{\n\t\t\tb2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\t\t\tm_shapeId = b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfloat h = 0.5f - m_round;\n\t\t\tb2Polygon box = b2MakeRoundedBox( h, 2.0f * h, m_round );\n\t\t\tm_shapeId = b2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 140.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 180.0f, height ) );\n\n\t\tImGui::Begin( \"Ghost Bumps\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 100.0f );\n\n\t\tif ( ImGui::Checkbox( \"Chain\", &m_useChain ) )\n\t\t{\n\t\t\tCreateScene();\n\t\t}\n\n\t\tif ( m_useChain == false )\n\t\t{\n\t\t\tif ( ImGui::SliderFloat( \"Bevel\", &m_bevel, 0.0f, 1.0f, \"%.2f\" ) )\n\t\t\t{\n\t\t\t\tCreateScene();\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\tconst char* shapeTypes[] = { \"Circle\", \"Capsule\", \"Box\" };\n\t\t\tint shapeType = int( m_shapeType );\n\t\t\tImGui::Combo( \"Shape\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) );\n\t\t\tm_shapeType = ShapeType( shapeType );\n\t\t}\n\n\t\tif ( m_shapeType == e_boxShape )\n\t\t{\n\t\t\tImGui::SliderFloat( \"Round\", &m_round, 0.0f, 0.4f, \"%.1f\" );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Friction\", &m_friction, 0.0f, 1.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_shapeId ) )\n\t\t\t{\n\t\t\t\tb2Shape_SetFriction( m_shapeId, m_friction );\n\t\t\t}\n\n\t\t\tCreateScene();\n\t\t}\n\n\t\tif ( ImGui::Button( \"Launch\" ) )\n\t\t{\n\t\t\tLaunch();\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new GhostBumps( context );\n\t}\n\n\tb2BodyId m_groundId;\n\tb2BodyId m_bodyId;\n\tb2ShapeId m_shapeId;\n\tShapeType m_shapeType;\n\tfloat m_round;\n\tfloat m_friction;\n\tfloat m_bevel;\n\tbool m_useChain;\n};\n\nstatic int sampleGhostCollision = RegisterSample( \"Continuous\", \"Ghost Bumps\", GhostBumps::Create );\n\n// Speculative collision failure case suggested by Dirk Gregorius. This uses\n// a simple fallback scheme to prevent tunneling.\nclass SpeculativeFallback : public Sample\n{\npublic:\n\texplicit SpeculativeFallback( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 1.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.25f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tb2Vec2 points[5] = { { -2.0f, 4.0f }, { 2.0f, 4.0f }, { 2.0f, 4.1f }, { -0.5f, 4.2f }, { -2.0f, 4.2f } };\n\t\t\tb2Hull hull = b2ComputeHull( points, 5 );\n\t\t\tb2Polygon poly = b2MakePolygon( &hull, 0.0f );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &poly );\n\t\t}\n\n\t\t// Fast moving skinny box. Also testing a large shape offset.\n\t\t{\n\t\t\tfloat offset = 8.0f;\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { offset, 12.0f };\n\t\t\tbodyDef.linearVelocity = { 0.0f, -100.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 2.0f, 0.05f, { -offset, 0.0f }, b2MakeRot( B2_PI ) );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SpeculativeFallback( context );\n\t}\n};\n\nstatic int sampleSpeculativeFallback = RegisterSample( \"Continuous\", \"Speculative Fallback\", SpeculativeFallback::Create );\n\nclass SpeculativeSliver : public Sample\n{\npublic:\n\texplicit SpeculativeSliver( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 1.75f };\n\t\t\tm_context->camera.zoom = 2.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 0.0f, 12.0f };\n\t\t\tbodyDef.linearVelocity = { 0.0f, -100.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Vec2 points[3] = { { -2.0f, 0.0f }, { -1.0f, 0.0f }, { 2.0f, 0.5f } };\n\t\t\tb2Hull hull = b2ComputeHull( points, 3 );\n\t\t\tb2Polygon poly = b2MakePolygon( &hull, 0.0f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &poly );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SpeculativeSliver( context );\n\t}\n};\n\nstatic int sampleSpeculativeSliver = RegisterSample( \"Continuous\", \"Speculative Sliver\", SpeculativeSliver::Create );\n\n// This shows that while Box2D uses speculative collision, it does not lead to speculative ghost collisions at small distances\nclass SpeculativeGhost : public Sample\n{\npublic:\n\texplicit SpeculativeGhost( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 1.75f };\n\t\t\tm_context->camera.zoom = 2.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 1.0f, 0.1f, { 0.0f, 0.9f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\t// The speculative distance is 0.02 meters, so this avoid it\n\t\t\tbodyDef.position = { 0.015f, 2.515f };\n\t\t\tbodyDef.linearVelocity = { 0.1f * 1.25f * m_context->hertz, -0.1f * 1.25f * m_context->hertz };\n\t\t\tbodyDef.gravityScale = 0.0f;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeSquare( 0.25f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SpeculativeGhost( context );\n\t}\n};\n\nstatic int sampleSpeculativeGhost = RegisterSample( \"Continuous\", \"Speculative Ghost\", SpeculativeGhost::Create );\n\n// This shows that Box2D does not have pixel perfect collision.\nclass PixelImperfect : public Sample\n{\npublic:\n\texplicit PixelImperfect( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 7.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 6.0f;\n\t\t}\n\n\t\tfloat pixelsPerMeter = 30.f;\n\n\t\t{\n\t\t\tb2BodyDef block4BodyDef = b2DefaultBodyDef();\n\t\t\tblock4BodyDef.type = b2_staticBody;\n\t\t\tblock4BodyDef.position = { 175.f / pixelsPerMeter, 150.f / pixelsPerMeter };\n\t\t\tb2BodyId block4BodyId = b2CreateBody( m_worldId, &block4BodyDef );\n\t\t\tb2Polygon block4Shape = b2MakeBox( 20.f / pixelsPerMeter, 10.f / pixelsPerMeter );\n\t\t\tb2ShapeDef block4ShapeDef = b2DefaultShapeDef();\n\t\t\tblock4ShapeDef.material.friction = 0.f;\n\t\t\tb2CreatePolygonShape( block4BodyId, &block4ShapeDef, &block4Shape );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef ballBodyDef = b2DefaultBodyDef();\n\t\t\tballBodyDef.type = b2_dynamicBody;\n\t\t\tballBodyDef.position = { 200.0f / pixelsPerMeter, 275.f / pixelsPerMeter };\n\t\t\tballBodyDef.gravityScale = 0.0f;\n\n\t\t\tm_ballId = b2CreateBody( m_worldId, &ballBodyDef );\n\t\t\t// Ball shape\n\t\t\t// b2Polygon ballShape = b2MakeBox( 5.f / pixelsPerMeter, 5.f / pixelsPerMeter );\n\t\t\tb2Polygon ballShape = b2MakeRoundedBox( 4.0f / pixelsPerMeter, 4.0f / pixelsPerMeter, 0.9f / pixelsPerMeter );\n\t\t\tb2ShapeDef ballShapeDef = b2DefaultShapeDef();\n\t\t\tballShapeDef.material.friction = 0.f;\n\t\t\t// ballShapeDef.restitution = 1.f;\n\t\t\tb2CreatePolygonShape( m_ballId, &ballShapeDef, &ballShape );\n\t\t\tb2Body_SetLinearVelocity( m_ballId, { 0.f, -5.0f } );\n\t\t\tb2Body_SetMotionLocks( m_ballId, { false, false, true } );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tb2ContactData data;\n\t\tb2Body_GetContactData( m_ballId, &data, 1 );\n\n\t\tb2Vec2 p = b2Body_GetPosition( m_ballId );\n\t\tb2Vec2 v = b2Body_GetLinearVelocity( m_ballId );\n\t\tDrawTextLine( \"p.x = %.9f, v.y = %.9f\", p.x, v.y );\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new PixelImperfect( context );\n\t}\n\n\tb2BodyId m_ballId;\n};\n\nstatic int samplePixelImperfect = RegisterSample( \"Continuous\", \"Pixel Imperfect\", PixelImperfect::Create );\n\nclass RestitutionThreshold : public Sample\n{\npublic:\n\texplicit RestitutionThreshold( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 7.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 6.0f;\n\t\t}\n\n\t\tfloat pixelsPerMeter = 30.f;\n\n\t\t// With the default threshold the ball will not bounce.\n\t\tb2World_SetRestitutionThreshold( m_worldId, 0.1f );\n\n\t\t{\n\t\t\tb2BodyDef block0BodyDef = b2DefaultBodyDef();\n\t\t\tblock0BodyDef.type = b2_staticBody;\n\t\t\tblock0BodyDef.position = { 205.f / pixelsPerMeter, 120.f / pixelsPerMeter };\n\t\t\tblock0BodyDef.rotation = b2MakeRot( 70.f * 3.14f / 180.f );\n\t\t\tb2BodyId block0BodyId = b2CreateBody( m_worldId, &block0BodyDef );\n\t\t\tb2Polygon block0Shape = b2MakeBox( 50.f / pixelsPerMeter, 5.f / pixelsPerMeter );\n\t\t\tb2ShapeDef block0ShapeDef = b2DefaultShapeDef();\n\t\t\tblock0ShapeDef.material.friction = 0.f;\n\t\t\tb2CreatePolygonShape( block0BodyId, &block0ShapeDef, &block0Shape );\n\t\t}\n\n\t\t{\n\t\t\t// Make a ball\n\t\t\tb2BodyDef ballBodyDef = b2DefaultBodyDef();\n\t\t\tballBodyDef.type = b2_dynamicBody;\n\t\t\tballBodyDef.position = { 200.f / pixelsPerMeter, 250.f / pixelsPerMeter };\n\t\t\tm_ballId = b2CreateBody( m_worldId, &ballBodyDef );\n\n\t\t\tb2Circle ballShape = {};\n\t\t\tballShape.radius = 5.f / pixelsPerMeter;\n\t\t\tb2ShapeDef ballShapeDef = b2DefaultShapeDef();\n\t\t\tballShapeDef.material.friction = 0.f;\n\t\t\tballShapeDef.material.restitution = 1.f;\n\t\t\tb2CreateCircleShape( m_ballId, &ballShapeDef, &ballShape );\n\n\t\t\tb2Body_SetLinearVelocity( m_ballId, { 0.f, -2.9f } ); // Initial velocity\n\t\t\tb2Body_SetMotionLocks( m_ballId, { false, false, true } ); // Do not rotate a ball\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tb2ContactData data;\n\t\tb2Body_GetContactData( m_ballId, &data, 1 );\n\n\t\tb2Vec2 p = b2Body_GetPosition( m_ballId );\n\t\tb2Vec2 v = b2Body_GetLinearVelocity( m_ballId );\n\t\tDrawTextLine( \"p.x = %.9f, v.y = %.9f\", p.x, v.y );\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new RestitutionThreshold( context );\n\t}\n\n\tb2BodyId m_ballId;\n};\n\nstatic int sampleRestitutionThreshold = RegisterSample( \"Continuous\", \"Restitution Threshold\", RestitutionThreshold::Create );\n\nclass Drop : public Sample\n{\npublic:\n\texplicit Drop( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 1.5f };\n\t\t\tm_context->camera.zoom = 3.0f;\n\t\t\tm_context->enableSleep = false;\n\t\t\tm_context->debugDraw.drawJoints = false;\n\t\t}\n\n#if 0\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tfloat w = 0.25f;\n\t\t\tint count = 40;\n\n\t\t\tfloat x = -0.5f * count * w;\n\t\t\tfloat h = 0.05f;\n\t\t\tfor ( int j = 0; j <= count; ++j )\n\t\t\t{\n\t\t\t\tb2Polygon box = b2MakeOffsetBox( w, h, { x, -h }, b2Rot_identity );\n\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t\t\tx += w;\n\t\t\t}\n\t\t}\n#endif\n\n\t\tm_human = {};\n\t\tm_frameSkip = 0;\n\t\tm_frameCount = 0;\n\t\tm_continuous = true;\n\t\tm_speculative = true;\n\n\t\tScene1();\n\t}\n\n\tvoid Clear()\n\t{\n\t\tfor ( int i = 0; i < m_bodyIds.size(); ++i )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t}\n\n\t\tm_bodyIds.clear();\n\n\t\tif ( m_human.isSpawned )\n\t\t{\n\t\t\tDestroyHuman( &m_human );\n\t\t}\n\t}\n\n\tvoid CreateGround1()\n\t{\n\t\tfor ( int i = 0; i < m_groundIds.size(); ++i )\n\t\t{\n\t\t\tb2DestroyBody( m_groundIds[i] );\n\t\t}\n\t\tm_groundIds.clear();\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tfloat w = 0.25f;\n\t\tint count = 40;\n\t\tb2Segment segment = { { -0.5f * count * w, 0.0f }, { 0.5f * count * w, 0.0f } };\n\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\tm_groundIds.push_back( groundId );\n\t}\n\n\tvoid CreateGround2()\n\t{\n\t\tfor ( int i = 0; i < m_groundIds.size(); ++i )\n\t\t{\n\t\t\tb2DestroyBody( m_groundIds[i] );\n\t\t}\n\t\tm_groundIds.clear();\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tfloat w = 0.25f;\n\t\tint count = 40;\n\n\t\tfloat x = -0.5f * count * w;\n\t\tfloat h = 0.05f;\n\t\tfor ( int j = 0; j <= count; ++j )\n\t\t{\n\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f * w, h, { x, 0.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t\tx += w;\n\t\t}\n\n\t\tm_groundIds.push_back( groundId );\n\t}\n\n\tvoid CreateGround3()\n\t{\n\t\tfor ( int i = 0; i < m_groundIds.size(); ++i )\n\t\t{\n\t\t\tb2DestroyBody( m_groundIds[i] );\n\t\t}\n\t\tm_groundIds.clear();\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tfloat w = 0.25f;\n\t\tint count = 40;\n\t\tb2Segment segment = { { -0.5f * count * w, 0.0f }, { 0.5f * count * w, 0.0f } };\n\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\tsegment = { { 3.0f, 0.0f }, { 3.0f, 8.0f } };\n\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\tm_groundIds.push_back( groundId );\n\t}\n\n\t// ball\n\tvoid Scene1()\n\t{\n\t\tClear();\n\t\tCreateGround2();\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { 0.0f, 4.0f };\n\t\tbodyDef.linearVelocity = { 0.0f, -100.0f };\n\n\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.125f };\n\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\n\t\tm_bodyIds.push_back( bodyId );\n\t\tm_frameCount = 1;\n\t}\n\n\t// ruler\n\tvoid Scene2()\n\t{\n\t\tClear();\n\t\tCreateGround1();\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { 0.0f, 4.0f };\n\t\tbodyDef.rotation = b2MakeRot( 0.5f * B2_PI );\n\t\tbodyDef.linearVelocity = { 0.0f, 0.0f };\n\t\tbodyDef.angularVelocity = -0.5f;\n\n\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Polygon box = b2MakeBox( 0.75f, 0.01f );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\tm_bodyIds.push_back( bodyId );\n\t\tm_frameCount = 1;\n\t}\n\n\t// ragdoll\n\tvoid Scene3()\n\t{\n\t\tClear();\n\t\tCreateGround2();\n\n\t\tfloat jointFrictionTorque = 0.03f;\n\t\tfloat jointHertz = 1.0f;\n\t\tfloat jointDampingRatio = 0.5f;\n\n\t\tCreateHuman( &m_human, m_worldId, { 0.0f, 40.0f }, 1.0f, jointFrictionTorque, jointHertz, jointDampingRatio, 1, nullptr,\n\t\t\t\t\t true );\n\n\t\tm_frameCount = 1;\n\t}\n\n\tvoid Scene4()\n\t{\n\t\tClear();\n\t\tCreateGround3();\n\n\t\tfloat a = 0.25f;\n\t\tb2Polygon box = b2MakeSquare( a );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tfloat offset = 0.01f;\n\n\t\tfor ( int i = 0; i < 5; ++i )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\tfloat shift = ( i % 2 == 0 ? -offset : offset );\n\t\t\tbodyDef.position = { 2.5f + shift, a + 2.0f * a * i };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tm_bodyIds.push_back( bodyId );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.125f };\n\t\tshapeDef.density = 4.0f;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -7.7f, 1.9f };\n\t\t\tbodyDef.linearVelocity = { 200.0f, 0.0f };\n\t\t\tbodyDef.isBullet = true;\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t\tm_bodyIds.push_back( bodyId );\n\t\t}\n\n\t\tm_frameCount = 1;\n\t}\n\n\tvoid Keyboard( int key ) override\n\t{\n\t\tswitch ( key )\n\t\t{\n\t\t\tcase GLFW_KEY_1:\n\t\t\t\tScene1();\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_2:\n\t\t\t\tScene2();\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_3:\n\t\t\t\tScene3();\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_4:\n\t\t\t\tScene4();\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_C:\n\t\t\t\tClear();\n\t\t\t\tm_continuous = !m_continuous;\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_V:\n\t\t\t\tClear();\n\t\t\t\tm_speculative = !m_speculative;\n\t\t\t\tb2World_EnableSpeculative( m_worldId, m_speculative );\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_S:\n\t\t\t\tm_frameSkip = m_frameSkip > 0 ? 0 : 60;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tSample::Keyboard( key );\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n#if 0\n\t\tImGui::SetNextWindowPos( ImVec2( 0.0f, 0.0f ) );\n\t\tImGui::SetNextWindowSize( ImVec2( float( m_context->camera.width ), float( m_camera->height ) ) );\n\t\tImGui::SetNextWindowBgAlpha( 0.0f );\n\t\tImGui::Begin( \"DropBackground\", nullptr,\n\t\t\t\t\t  ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize |\n\t\t\t\t\t\t  ImGuiWindowFlags_NoScrollbar );\n\n\t\tImDrawList* drawList = ImGui::GetWindowDrawList();\n\n\t\tconst char* ContinuousText = m_continuous && m_speculative ? \"Continuous ON\" : \"Continuous OFF\";\n\t\tdrawList->AddText( m_largeFont, m_largeFont->FontSize, { 40.0f, 40.0f }, IM_COL32_WHITE, ContinuousText );\n\n\t\tif ( m_frameSkip > 0 )\n\t\t{\n\t\t\tdrawList->AddText( m_mediumFont, m_mediumFont->FontSize, { 40.0f, 40.0f + 64.0f + 20.0f },\n\t\t\t\t\t\t\t   IM_COL32( 200, 200, 200, 255 ), \"Slow Time\" );\n\t\t}\n\n\t\tImGui::End();\n#endif\n\n\t\t// if (m_frameCount == 165)\n\t\t//{\n\t\t//\tsettings.pause = true;\n\t\t//\tm_frameSkip = 30;\n\t\t// }\n\n\t\tm_context->enableContinuous = m_continuous;\n\n\t\tif ( ( m_frameSkip == 0 || m_frameCount % m_frameSkip == 0 ) && m_context->pause == false )\n\t\t{\n\t\t\tSample::Step();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbool pause = m_context->pause;\n\t\t\tm_context->pause = true;\n\t\t\tSample::Step();\n\t\t\tm_context->pause = pause;\n\t\t}\n\n\t\tm_frameCount += 1;\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Drop( context );\n\t}\n\n\tstd::vector<b2BodyId> m_groundIds;\n\tstd::vector<b2BodyId> m_bodyIds;\n\tHuman m_human;\n\tint m_frameSkip;\n\tint m_frameCount;\n\tbool m_continuous;\n\tbool m_speculative;\n};\n\nstatic int sampleDrop = RegisterSample( \"Continuous\", \"Drop\", Drop::Create );\n\n// This shows a fast moving body that uses continuous collision versus static and dynamic bodies.\n// This is achieved by setting the ball body as a *bullet*.\nclass Pinball : public Sample\n{\npublic:\n\texplicit Pinball( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 9.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t}\n\n\t\tm_context->debugDraw.drawJoints = false;\n\n\t\t// Ground body\n\t\tb2BodyId groundId = {};\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Vec2 vs[5] = { { -8.0f, 6.0f }, { -8.0f, 20.0f }, { 8.0f, 20.0f }, { 8.0f, 6.0f }, { 0.0f, -2.0f } };\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = vs;\n\t\t\tchainDef.count = 5;\n\t\t\tchainDef.isLoop = true;\n\t\t\tb2CreateChain( groundId, &chainDef );\n\t\t}\n\n\t\t// Flippers\n\t\t{\n\t\t\tb2Vec2 p1 = { -2.0f, 0.0f }, p2 = { 2.0f, 0.0f };\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.enableSleep = false;\n\n\t\t\tbodyDef.position = p1;\n\t\t\tb2BodyId leftFlipperId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tbodyDef.position = p2;\n\t\t\tb2BodyId rightFlipperId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 1.75f, 0.2f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2CreatePolygonShape( leftFlipperId, &shapeDef, &box );\n\t\t\tb2CreatePolygonShape( rightFlipperId, &shapeDef, &box );\n\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.localFrameB.p = b2Vec2_zero;\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorTorque = 1000.0f;\n\t\t\tjointDef.enableLimit = true;\n\n\t\t\tjointDef.motorSpeed = 0.0f;\n\t\t\tjointDef.base.localFrameA.p = p1;\n\t\t\tjointDef.base.bodyIdB = leftFlipperId;\n\t\t\tjointDef.lowerAngle = -30.0f * B2_PI / 180.0f;\n\t\t\tjointDef.upperAngle = 5.0f * B2_PI / 180.0f;\n\t\t\tm_leftJointId = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\tjointDef.motorSpeed = 0.0f;\n\t\t\tjointDef.base.localFrameA.p = p2;\n\t\t\tjointDef.base.bodyIdB = rightFlipperId;\n\t\t\tjointDef.lowerAngle = -5.0f * B2_PI / 180.0f;\n\t\t\tjointDef.upperAngle = 30.0f * B2_PI / 180.0f;\n\t\t\tm_rightJointId = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\t// Spinners\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -4.0f, 17.0f };\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box1 = b2MakeBox( 1.5f, 0.125f );\n\t\t\tb2Polygon box2 = b2MakeBox( 0.125f, 1.5f );\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box1 );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box2 );\n\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = bodyDef.position;\n\t\t\tjointDef.base.localFrameB.p = b2Vec2_zero;\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorTorque = 0.1f;\n\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\tbodyDef.position = { 4.0f, 8.0f };\n\t\t\tbodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box1 );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box2 );\n\t\t\tjointDef.base.localFrameA.p = bodyDef.position;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\t// Bumpers\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { -4.0f, 8.0f };\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.restitution = 1.5f;\n\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 1.0f };\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\n\t\t\tbodyDef.position = { 4.0f, 17.0f };\n\t\t\tbodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\n\t\t// Ball\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 1.0f, 15.0f };\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.isBullet = true;\n\n\t\t\tm_ballId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.2f };\n\t\t\tb2CreateCircleShape( m_ballId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_SPACE ) == GLFW_PRESS )\n\t\t{\n\t\t\tb2RevoluteJoint_SetMotorSpeed( m_leftJointId, 20.0f );\n\t\t\tb2RevoluteJoint_SetMotorSpeed( m_rightJointId, -20.0f );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb2RevoluteJoint_SetMotorSpeed( m_leftJointId, -10.0f );\n\t\t\tb2RevoluteJoint_SetMotorSpeed( m_rightJointId, 10.0f );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Pinball( context );\n\t}\n\n\tb2JointId m_leftJointId;\n\tb2JointId m_rightJointId;\n\tb2BodyId m_ballId;\n};\n\nstatic int samplePinball = RegisterSample( \"Continuous\", \"Pinball\", Pinball::Create );\n\n// This shows the importance of secondary collisions in continuous physics.\n// This also shows a difficult setup for the solver with an acute angle.\nclass Wedge : public Sample\n{\npublic:\n\texplicit Wedge( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 5.5f };\n\t\t\tm_context->camera.zoom = 6.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -4.0f, 8.0f }, { 0.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t\tsegment = { { 0.0f, 0.0f }, { 0.0f, 8.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -0.45f, 10.75f };\n\t\t\tbodyDef.linearVelocity = { 0.0f, -200.0f };\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Circle circle = {};\n\t\t\tcircle.radius = 0.3f;\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.2f;\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Wedge( context );\n\t}\n};\n\nstatic int sampleWedge = RegisterSample( \"Continuous\", \"Wedge\", Wedge::Create );\n"
  },
  {
    "path": "samples/sample_determinism.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"determinism.h\"\n#include \"sample.h\"\n\n#include \"box2d/math_functions.h\"\n\n// This sample provides a visual representation of the cross platform determinism unit test.\n// The scenario is designed to produce a chaotic result engaging:\n// - continuous collision\n// - joint limits (approximate atan2)\n// - b2MakeRot (approximate sin/cos)\n// Once all the bodies go to sleep the step counter and transform hash is emitted which\n// can then be transferred to the unit test and tested in GitHub build actions.\n// See CrossPlatformTest in the unit tests.\nclass FallingHinges : public Sample\n{\npublic:\n\n\texplicit FallingHinges( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 7.5f };\n\t\t\tm_context->camera.zoom = 10.0f;\n\t\t}\n\n\t\tm_data = CreateFallingHinges( m_worldId );\n\t\tm_done = false;\n\t}\n\n\t~FallingHinges() override\n\t{\n\t\tDestroyFallingHinges( &m_data );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tif (m_context->pause == false && m_done == false)\n\t\t{\n\t\t\tm_done = UpdateFallingHinges( m_worldId, &m_data );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDrawTextLine( \"sleep step = %d, hash = 0x%08X\", m_data.sleepStep, m_data.hash );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new FallingHinges( context );\n\t}\n\n\tFallingHingeData m_data;\n\tbool m_done;\n};\n\nstatic int sampleFallingHinges = RegisterSample( \"Determinism\", \"Falling Hinges\", FallingHinges::Create );\n"
  },
  {
    "path": "samples/sample_events.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"donut.h\"\n#include \"draw.h\"\n#include \"human.h\"\n#include \"random.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n#include <imgui.h>\n#include <stdio.h>\n#include <vector>\n\nclass SensorFunnel : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_donut = 1,\n\t\te_human = 2,\n\t\te_count = 32\n\t};\n\n\texplicit SensorFunnel( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 1.333f;\n\t\t}\n\n\t\tm_context->debugDraw.drawJoints = false;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t// b2Vec2 points[] = {\n\t\t\t//{42.333, 44.979},\t{177.271, 44.979},\t{177.271, 100.542}, {142.875, 121.708}, {177.271, 121.708},\n\t\t\t//{177.271, 171.979}, {142.875, 193.146}, {177.271, 193.146}, {177.271, 222.250}, {124.354, 261.938},\n\t\t\t//{124.354, 293.688}, {95.250, 293.688},\t{95.250, 261.938},\t{42.333, 222.250},\t{42.333, 193.146},\n\t\t\t//{76.729, 193.146},\t{42.333, 171.979},\t{42.333, 121.708},\t{76.729, 121.708},\t{42.333, 100.542},\n\t\t\t//};\n\n\t\t\tb2Vec2 points[] = {\n\t\t\t\t{ -16.8672504, 31.088623 },\t { 16.8672485, 31.088623 },\t\t{ 16.8672485, 17.1978741 },\n\t\t\t\t{ 8.26824951, 11.906374 },\t { 16.8672485, 11.906374 },\t\t{ 16.8672485, -0.661376953 },\n\t\t\t\t{ 8.26824951, -5.953125 },\t { 16.8672485, -5.953125 },\t\t{ 16.8672485, -13.229126 },\n\t\t\t\t{ 3.63799858, -23.151123 },\t { 3.63799858, -31.088623 },\t{ -3.63800049, -31.088623 },\n\t\t\t\t{ -3.63800049, -23.151123 }, { -16.8672504, -13.229126 },\t{ -16.8672504, -5.953125 },\n\t\t\t\t{ -8.26825142, -5.953125 },\t { -16.8672504, -0.661376953 }, { -16.8672504, 11.906374 },\n\t\t\t\t{ -8.26825142, 11.906374 },\t { -16.8672504, 17.1978741 },\n\t\t\t};\n\n\t\t\tint count = std::size( points );\n\n\t\t\t// float scale = 0.25f;\n\t\t\t// b2Vec2 lower = {FLT_MAX, FLT_MAX};\n\t\t\t// b2Vec2 upper = {-FLT_MAX, -FLT_MAX};\n\t\t\t// for (int i = 0; i < count; ++i)\n\t\t\t//{\n\t\t\t//\tpoints[i].x = scale * points[i].x;\n\t\t\t//\tpoints[i].y = -scale * points[i].y;\n\n\t\t\t//\tlower = b2Min(lower, points[i]);\n\t\t\t//\tupper = b2Max(upper, points[i]);\n\t\t\t//}\n\n\t\t\t// b2Vec2 center = b2MulSV(0.5f, b2Add(lower, upper));\n\t\t\t// for (int i = 0; i < count; ++i)\n\t\t\t//{\n\t\t\t//\tpoints[i] = b2Sub(points[i], center);\n\t\t\t// }\n\n\t\t\t// for (int i = 0; i < count / 2; ++i)\n\t\t\t//{\n\t\t\t//\tb2Vec2 temp = points[i];\n\t\t\t//\tpoints[i] = points[count - 1 - i];\n\t\t\t//\tpoints[count - 1 - i] = temp;\n\t\t\t// }\n\n\t\t\t// printf(\"{\");\n\t\t\t// for (int i = 0; i < count; ++i)\n\t\t\t//{\n\t\t\t//\tprintf(\"{%.9g, %.9g},\", points[i].x, points[i].y);\n\t\t\t// }\n\t\t\t// printf(\"};\\n\");\n\n\t\t\tb2SurfaceMaterial material = {};\n\t\t\tmaterial.friction = 0.2f;\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = count;\n\t\t\tchainDef.isLoop = true;\n\t\t\tchainDef.materials = &material;\n\t\t\tchainDef.materialCount = 1;\n\t\t\tb2CreateChain( groundId, &chainDef );\n\n\t\t\tfloat sign = 1.0f;\n\t\t\tfloat y = 14.0f;\n\t\t\tfor (int i = 0; i < 3; ++i)\n\t\t\t{\n\t\t\t\tbodyDef.position = { 0.0f, y };\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\tb2Polygon box = b2MakeBox( 6.0f, 0.5f );\n\t\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\t\tshapeDef.material.friction = 0.1f;\n\t\t\t\tshapeDef.material.restitution = 1.0f;\n\t\t\t\tshapeDef.density = 1.0f;\n\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\t\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\t\t\t\trevoluteDef.base.bodyIdA = groundId;\n\t\t\t\trevoluteDef.base.bodyIdB = bodyId;\n\t\t\t\trevoluteDef.base.localFrameA.p = bodyDef.position;\n\t\t\t\trevoluteDef.base.localFrameB.p = b2Vec2_zero;\n\t\t\t\trevoluteDef.maxMotorTorque = 200.0f;\n\t\t\t\trevoluteDef.motorSpeed = 2.0f * sign;\n\t\t\t\trevoluteDef.enableMotor = true;\n\n\t\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\n\t\t\t\ty -= 14.0f;\n\t\t\t\tsign = -sign;\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tb2Polygon box = b2MakeOffsetBox( 4.0f, 1.0f, { 0.0f, -30.5f }, b2Rot_identity );\n\t\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\t\tshapeDef.isSensor = true;\n\t\t\t\tshapeDef.enableSensorEvents = true;\n\n\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t\t}\n\t\t}\n\n\t\tm_wait = 0.5f;\n\t\tm_side = -15.0f;\n\t\tm_type = e_human;\n\n\t\tfor (int i = 0; i < e_count; ++i)\n\t\t{\n\t\t\tm_isSpawned[i] = false;\n\t\t}\n\n\t\tmemset( m_humans, 0, sizeof( m_humans ) );\n\n\t\tCreateElement();\n\t}\n\n\tvoid CreateElement()\n\t{\n\t\tint index = -1;\n\t\tfor (int i = 0; i < e_count; ++i)\n\t\t{\n\t\t\tif (m_isSpawned[i] == false)\n\t\t\t{\n\t\t\t\tindex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (index == -1)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tb2Vec2 center = { m_side, 29.5f };\n\n\t\tif (m_type == e_donut)\n\t\t{\n\t\t\tDonut* donut = m_donuts + index;\n\t\t\tdonut->Create( m_worldId, center, 1.0f, 0, true, donut );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tHuman* human = m_humans + index;\n\t\t\tfloat scale = 2.0f;\n\t\t\tfloat jointFriction = 0.05f;\n\t\t\tfloat jointHertz = 6.0f;\n\t\t\tfloat jointDamping = 0.5f;\n\t\t\tbool colorize = true;\n\t\t\tCreateHuman( human, m_worldId, center, scale, jointFriction, jointHertz, jointDamping, index + 1, human, colorize );\n\t\t\tHuman_EnableSensorEvents( human, true );\n\t\t}\n\n\t\tm_isSpawned[index] = true;\n\t\tm_side = -m_side;\n\t}\n\n\tvoid DestroyElement( int index )\n\t{\n\t\tif (m_type == e_donut)\n\t\t{\n\t\t\tDonut* donut = m_donuts + index;\n\t\t\tdonut->Destroy();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tHuman* human = m_humans + index;\n\t\t\tDestroyHuman( human );\n\t\t}\n\n\t\tm_isSpawned[index] = false;\n\t}\n\n\tvoid Clear()\n\t{\n\t\tfor (int i = 0; i < e_count; ++i)\n\t\t{\n\t\t\tif (m_isSpawned[i] == true)\n\t\t\t{\n\t\t\t\tif (m_type == e_donut)\n\t\t\t\t{\n\t\t\t\t\tm_donuts[i].Destroy();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDestroyHuman( m_humans + i );\n\t\t\t\t}\n\n\t\t\t\tm_isSpawned[i] = false;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 90.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 140.0f, height ) );\n\n\t\tImGui::Begin( \"Sensor Event\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tif (ImGui::RadioButton( \"donut\", m_type == e_donut ))\n\t\t{\n\t\t\tClear();\n\t\t\tm_type = e_donut;\n\t\t}\n\n\t\tif (ImGui::RadioButton( \"human\", m_type == e_human ))\n\t\t{\n\t\t\tClear();\n\t\t\tm_type = e_human;\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif (m_stepCount == 832)\n\t\t{\n\t\t\tm_stepCount += 0;\n\t\t}\n\n\t\tSample::Step();\n\n\t\t// Discover rings that touch the bottom sensor\n\t\tbool deferredDestruction[e_count] = {};\n\t\tb2SensorEvents sensorEvents = b2World_GetSensorEvents( m_worldId );\n\t\tfor (int i = 0; i < sensorEvents.beginCount; ++i)\n\t\t{\n\t\t\tb2SensorBeginTouchEvent event = sensorEvents.beginEvents[i];\n\t\t\tb2ShapeId visitorId = event.visitorShapeId;\n\t\t\tb2BodyId bodyId = b2Shape_GetBody( visitorId );\n\n\t\t\tif (m_type == e_donut)\n\t\t\t{\n\t\t\t\tDonut* donut = (Donut*)b2Body_GetUserData( bodyId );\n\t\t\t\tif (donut != nullptr)\n\t\t\t\t{\n\t\t\t\t\tint index = (int)( donut - m_donuts );\n\t\t\t\t\tassert( 0 <= index && index < e_count );\n\n\t\t\t\t\t// Defer destruction to avoid double destruction and event invalidation (orphaned shape ids)\n\t\t\t\t\tdeferredDestruction[index] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tHuman* human = (Human*)b2Body_GetUserData( bodyId );\n\t\t\t\tif (human != nullptr)\n\t\t\t\t{\n\t\t\t\t\tint index = (int)( human - m_humans );\n\t\t\t\t\tassert( 0 <= index && index < e_count );\n\n\t\t\t\t\t// Defer destruction to avoid double destruction and event invalidation (orphaned shape ids)\n\t\t\t\t\tdeferredDestruction[index] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// todo destroy mouse joint if necessary\n\n\t\t// Safely destroy rings that hit the bottom sensor\n\t\tfor (int i = 0; i < e_count; ++i)\n\t\t{\n\t\t\tif (deferredDestruction[i])\n\t\t\t{\n\t\t\t\tDestroyElement( i );\n\t\t\t}\n\t\t}\n\n\t\tif (m_context->hertz > 0.0f && m_context->pause == false)\n\t\t{\n\t\t\tm_wait -= 1.0f / m_context->hertz;\n\t\t\tif (m_wait < 0.0f)\n\t\t\t{\n\t\t\t\tCreateElement();\n\t\t\t\tm_wait += 0.5f;\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SensorFunnel( context );\n\t}\n\n\tHuman m_humans[e_count];\n\tDonut m_donuts[e_count];\n\tbool m_isSpawned[e_count];\n\tint m_type;\n\tfloat m_wait;\n\tfloat m_side;\n};\n\nstatic int sampleSensorBeginEvent = RegisterSample( \"Events\", \"Sensor Funnel\", SensorFunnel::Create );\n\nclass SensorBookend : public Sample\n{\npublic:\n\texplicit SensorBookend( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 6.0f };\n\t\t\tm_context->camera.zoom = 7.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2Segment groundSegment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &groundSegment );\n\n\t\t\tgroundSegment = { { -10.0f, 0.0f }, { -10.0f, 10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &groundSegment );\n\n\t\t\tgroundSegment = { { 10.0f, 0.0f }, { 10.0f, 10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &groundSegment );\n\n\t\t\tm_isVisiting1 = false;\n\t\t\tm_isVisiting2 = false;\n\t\t\tm_sensorsOverlapCount = 0;\n\t\t}\n\n\t\tCreateSensor1();\n\t\tCreateSensor2();\n\t\tCreateVisitor();\n\t}\n\n\tvoid CreateSensor1()\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\n\t\tbodyDef.position = { -2.0f, 1.0f };\n\t\tm_sensorBodyId1 = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.isSensor = true;\n\t\tshapeDef.enableSensorEvents = true;\n\n\t\tb2Polygon box = b2MakeSquare( 1.0f );\n\t\tm_sensorShapeId1 = b2CreatePolygonShape( m_sensorBodyId1, &shapeDef, &box );\n\t}\n\n\tvoid CreateSensor2()\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { 2.0f, 1.0f };\n\t\tm_sensorBodyId2 = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.isSensor = true;\n\t\tshapeDef.enableSensorEvents = true;\n\n\t\tb2Polygon box = b2MakeRoundedBox( 0.5f, 0.5f, 0.5f );\n\t\tm_sensorShapeId2 = b2CreatePolygonShape( m_sensorBodyId2, &shapeDef, &box );\n\n\t\t// Solid middle\n\t\tshapeDef.isSensor = false;\n\t\tshapeDef.enableSensorEvents = false;\n\t\tbox = b2MakeSquare( 0.5f );\n\t\tb2CreatePolygonShape( m_sensorBodyId2, &shapeDef, &box );\n\t}\n\n\tvoid CreateVisitor()\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.position = { -4.0f, 1.0f };\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tm_visitorBodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.enableSensorEvents = true;\n\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\tm_visitorShapeId = b2CreateCircleShape( m_visitorBodyId, &shapeDef, &circle );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 19.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 12.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Sensor Bookend\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tif (B2_IS_NULL( m_visitorBodyId ))\n\t\t{\n\t\t\tif (ImGui::Button( \"create visitor\" ))\n\t\t\t{\n\t\t\t\tCreateVisitor();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ImGui::Button( \"destroy visitor\" ))\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_visitorBodyId );\n\t\t\t\tm_visitorBodyId = b2_nullBodyId;\n\t\t\t\t// Retain m_visitorShapeId for end events.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbool enabledEvents = b2Shape_AreSensorEventsEnabled( m_visitorShapeId );\n\t\t\t\tif (ImGui::Checkbox( \"visitor events\", &enabledEvents ))\n\t\t\t\t{\n\t\t\t\t\tb2Shape_EnableSensorEvents( m_visitorShapeId, enabledEvents );\n\t\t\t\t}\n\n\t\t\t\tbool enabledBody = b2Body_IsEnabled( m_visitorBodyId );\n\t\t\t\tif (ImGui::Checkbox( \"enable visitor body\", &enabledBody ))\n\t\t\t\t{\n\t\t\t\t\tif (enabledBody)\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Body_Enable( m_visitorBodyId );\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Body_Disable( m_visitorBodyId );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tif (B2_IS_NULL( m_sensorBodyId1 ))\n\t\t{\n\t\t\tif (ImGui::Button( \"create sensor1\" ))\n\t\t\t{\n\t\t\t\tCreateSensor1();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ImGui::Button( \"destroy sensor1\" ))\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_sensorBodyId1 );\n\t\t\t\tm_sensorBodyId1 = b2_nullBodyId;\n\t\t\t\t// Retain m_sensorShapeId1 for end events.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbool enabledEvents = b2Shape_AreSensorEventsEnabled( m_sensorShapeId1 );\n\t\t\t\tif (ImGui::Checkbox( \"sensor 1 events\", &enabledEvents ))\n\t\t\t\t{\n\t\t\t\t\tb2Shape_EnableSensorEvents( m_sensorShapeId1, enabledEvents );\n\t\t\t\t}\n\n\t\t\t\tbool enabledBody = b2Body_IsEnabled( m_sensorBodyId1 );\n\t\t\t\tif (ImGui::Checkbox( \"enable sensor1 body\", &enabledBody ))\n\t\t\t\t{\n\t\t\t\t\tif (enabledBody)\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Body_Enable( m_sensorBodyId1 );\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Body_Disable( m_sensorBodyId1 );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tif (B2_IS_NULL( m_sensorBodyId2 ))\n\t\t{\n\t\t\tif (ImGui::Button( \"create sensor2\" ))\n\t\t\t{\n\t\t\t\tCreateSensor2();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ImGui::Button( \"destroy sensor2\" ))\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_sensorBodyId2 );\n\t\t\t\tm_sensorBodyId2 = b2_nullBodyId;\n\t\t\t\t// Retain m_sensorShapeId2 for end events.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbool enabledEvents = b2Shape_AreSensorEventsEnabled( m_sensorShapeId2 );\n\t\t\t\tif (ImGui::Checkbox( \"sensor2 events\", &enabledEvents ))\n\t\t\t\t{\n\t\t\t\t\tb2Shape_EnableSensorEvents( m_sensorShapeId2, enabledEvents );\n\t\t\t\t}\n\n\t\t\t\tbool enabledBody = b2Body_IsEnabled( m_sensorBodyId2 );\n\t\t\t\tif (ImGui::Checkbox( \"enable sensor2 body\", &enabledBody ))\n\t\t\t\t{\n\t\t\t\t\tif (enabledBody)\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Body_Enable( m_sensorBodyId2 );\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Body_Disable( m_sensorBodyId2 );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2SensorEvents sensorEvents = b2World_GetSensorEvents( m_worldId );\n\t\tfor (int i = 0; i < sensorEvents.beginCount; ++i)\n\t\t{\n\t\t\tb2SensorBeginTouchEvent event = sensorEvents.beginEvents[i];\n\n\t\t\tif (B2_ID_EQUALS( event.sensorShapeId, m_sensorShapeId1 ))\n\t\t\t{\n\t\t\t\tif (B2_ID_EQUALS( event.visitorShapeId, m_visitorShapeId ))\n\t\t\t\t{\n\t\t\t\t\tassert( m_isVisiting1 == false );\n\t\t\t\t\tm_isVisiting1 = true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( B2_ID_EQUALS( event.visitorShapeId, m_sensorShapeId2 ) );\n\t\t\t\t\tm_sensorsOverlapCount += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tassert( B2_ID_EQUALS( event.sensorShapeId, m_sensorShapeId2 ) );\n\n\t\t\t\tif (B2_ID_EQUALS( event.visitorShapeId, m_visitorShapeId ))\n\t\t\t\t{\n\t\t\t\t\tassert( m_isVisiting2 == false );\n\t\t\t\t\tm_isVisiting2 = true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( B2_ID_EQUALS( event.visitorShapeId, m_sensorShapeId1 ) );\n\t\t\t\t\tm_sensorsOverlapCount += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tassert( m_sensorsOverlapCount == 0 || m_sensorsOverlapCount == 2 );\n\n\t\tfor (int i = 0; i < sensorEvents.endCount; ++i)\n\t\t{\n\t\t\tb2SensorEndTouchEvent event = sensorEvents.endEvents[i];\n\n\t\t\tif (B2_ID_EQUALS( event.sensorShapeId, m_sensorShapeId1 ))\n\t\t\t{\n\t\t\t\tif (B2_ID_EQUALS( event.visitorShapeId, m_visitorShapeId ))\n\t\t\t\t{\n\t\t\t\t\tassert( m_isVisiting1 == true );\n\t\t\t\t\tm_isVisiting1 = false;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( B2_ID_EQUALS( event.visitorShapeId, m_sensorShapeId2 ) );\n\t\t\t\t\tm_sensorsOverlapCount -= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tassert( B2_ID_EQUALS( event.sensorShapeId, m_sensorShapeId2 ) );\n\n\t\t\t\tif (B2_ID_EQUALS( event.visitorShapeId, m_visitorShapeId ))\n\t\t\t\t{\n\t\t\t\t\tassert( m_isVisiting2 == true );\n\t\t\t\t\tm_isVisiting2 = false;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert( B2_ID_EQUALS( event.visitorShapeId, m_sensorShapeId1 ) );\n\t\t\t\t\tm_sensorsOverlapCount -= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tassert( m_sensorsOverlapCount == 0 || m_sensorsOverlapCount == 2 );\n\n\t\t// Nullify invalid shape ids after end events are processed.\n\t\tif (b2Shape_IsValid( m_visitorShapeId ) == false)\n\t\t{\n\t\t\tm_visitorShapeId = b2_nullShapeId;\n\t\t}\n\n\t\tif (b2Shape_IsValid( m_sensorShapeId1 ) == false)\n\t\t{\n\t\t\tm_sensorShapeId1 = b2_nullShapeId;\n\t\t}\n\n\t\tif (b2Shape_IsValid( m_sensorShapeId2 ) == false)\n\t\t{\n\t\t\tm_sensorShapeId2 = b2_nullShapeId;\n\t\t}\n\n\t\tDrawTextLine( \"visiting 1 == %s\", m_isVisiting1 ? \"true\" : \"false\" );\n\t\tDrawTextLine( \"visiting 2 == %s\", m_isVisiting2 ? \"true\" : \"false\" );\n\t\tDrawTextLine( \"sensors overlap count == %d\", m_sensorsOverlapCount );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SensorBookend( context );\n\t}\n\n\tb2BodyId m_sensorBodyId1;\n\tb2ShapeId m_sensorShapeId1;\n\n\tb2BodyId m_sensorBodyId2;\n\tb2ShapeId m_sensorShapeId2;\n\n\tb2BodyId m_visitorBodyId;\n\tb2ShapeId m_visitorShapeId;\n\n\tbool m_isVisiting1;\n\tbool m_isVisiting2;\n\tint m_sensorsOverlapCount;\n};\n\nstatic int sampleSensorBookendEvent = RegisterSample( \"Events\", \"Sensor Bookend\", SensorBookend::Create );\n\nclass FootSensor : public Sample\n{\npublic:\n\tenum CollisionBits\n\t{\n\t\tGROUND = 0x00000001,\n\t\tPLAYER = 0x00000002,\n\t\tFOOT = 0x00000004,\n\n\t\tALL_BITS = ( ~0u )\n\t};\n\n\texplicit FootSensor( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 6.0f };\n\t\t\tm_context->camera.zoom = 7.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Vec2 points[20];\n\t\t\tfloat x = 10.0f;\n\t\t\tfor (int i = 0; i < 20; ++i)\n\t\t\t{\n\t\t\t\tpoints[i] = { x, 0.0f };\n\t\t\t\tx -= 1.0f;\n\t\t\t}\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = 20;\n\t\t\tchainDef.filter.categoryBits = GROUND;\n\t\t\tchainDef.filter.maskBits = FOOT | PLAYER;\n\t\t\tchainDef.isLoop = false;\n\t\t\tchainDef.enableSensorEvents = true;\n\n\t\t\tb2CreateChain( groundId, &chainDef );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.motionLocks.angularZ = true;\n\t\t\tbodyDef.position = { 0.0f, 1.0f };\n\t\t\tm_playerId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter.categoryBits = PLAYER;\n\t\t\tshapeDef.filter.maskBits = GROUND;\n\t\t\tshapeDef.material.friction = 0.3f;\n\t\t\tb2Capsule capsule = { { 0.0f, -0.5f }, { 0.0f, 0.5f }, 0.5f };\n\t\t\tb2CreateCapsuleShape( m_playerId, &shapeDef, &capsule );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f, 0.25f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tshapeDef.filter.categoryBits = FOOT;\n\t\t\tshapeDef.filter.maskBits = GROUND;\n\t\t\tshapeDef.isSensor = true;\n\t\t\tshapeDef.enableSensorEvents = true;\n\t\t\tm_sensorId = b2CreatePolygonShape( m_playerId, &shapeDef, &box );\n\t\t}\n\n\t\tm_overlapCount = 0;\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif (glfwGetKey( m_context->window, GLFW_KEY_A ) == GLFW_PRESS)\n\t\t{\n\t\t\tb2Body_ApplyForceToCenter( m_playerId, { -50.0f, 0.0f }, true );\n\t\t}\n\n\t\tif (glfwGetKey( m_context->window, GLFW_KEY_D ) == GLFW_PRESS)\n\t\t{\n\t\t\tb2Body_ApplyForceToCenter( m_playerId, { 50.0f, 0.0f }, true );\n\t\t}\n\n\t\tSample::Step();\n\n\t\tb2SensorEvents sensorEvents = b2World_GetSensorEvents( m_worldId );\n\t\tfor (int i = 0; i < sensorEvents.beginCount; ++i)\n\t\t{\n\t\t\tb2SensorBeginTouchEvent event = sensorEvents.beginEvents[i];\n\n\t\t\tassert( B2_ID_EQUALS( event.visitorShapeId, m_sensorId ) == false );\n\n\t\t\tif (B2_ID_EQUALS( event.sensorShapeId, m_sensorId ))\n\t\t\t{\n\t\t\t\tm_overlapCount += 1;\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = 0; i < sensorEvents.endCount; ++i)\n\t\t{\n\t\t\tb2SensorEndTouchEvent event = sensorEvents.endEvents[i];\n\n\t\t\tassert( B2_ID_EQUALS( event.visitorShapeId, m_sensorId ) == false );\n\n\t\t\tif (B2_ID_EQUALS( event.sensorShapeId, m_sensorId ))\n\t\t\t{\n\t\t\t\tm_overlapCount -= 1;\n\t\t\t}\n\t\t}\n\n\t\tDrawTextLine( \"count == %d\", m_overlapCount );\n\n\t\tint capacity = b2Shape_GetSensorCapacity( m_sensorId );\n\t\tm_visitorIds.clear();\n\t\tm_visitorIds.resize( capacity );\n\t\tint count = b2Shape_GetSensorData( m_sensorId, m_visitorIds.data(), capacity );\n\t\tfor (int i = 0; i < count; ++i)\n\t\t{\n\t\t\tb2ShapeId shapeId = m_visitorIds[i];\n\t\t\tb2AABB aabb = b2Shape_GetAABB( shapeId );\n\t\t\tb2Vec2 point = b2AABB_Center( aabb );\n\t\t\tDrawPoint( m_draw, point, 10.0f, b2_colorWhite );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new FootSensor( context );\n\t}\n\n\tb2BodyId m_playerId;\n\tb2ShapeId m_sensorId;\n\tstd::vector<b2ShapeId> m_visitorIds;\n\tint m_overlapCount;\n};\n\nstatic int sampleFootSensor = RegisterSample( \"Events\", \"Foot Sensor\", FootSensor::Create );\n\nstruct BodyUserData\n{\n\tint index;\n};\n\nclass ContactEvent : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_count = 20\n\t};\n\n\texplicit ContactEvent( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 1.75f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Vec2 points[] = { { 40.0f, -40.0f }, { -40.0f, -40.0f }, { -40.0f, 40.0f }, { 40.0f, 40.0f } };\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.count = 4;\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.isLoop = true;\n\n\t\t\tb2CreateChain( groundId, &chainDef );\n\t\t}\n\n\t\t// Player\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.gravityScale = 0.0f;\n\t\t\tbodyDef.linearDamping = 0.5f;\n\t\t\tbodyDef.angularDamping = 0.5f;\n\t\t\tbodyDef.isBullet = true;\n\t\t\tm_playerId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 1.0f };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\t// Enable contact events for the player shape\n\t\t\tshapeDef.enableContactEvents = true;\n\n\t\t\tm_coreShapeId = b2CreateCircleShape( m_playerId, &shapeDef, &circle );\n\t\t}\n\n\t\tfor (int i = 0; i < e_count; ++i)\n\t\t{\n\t\t\tm_debrisIds[i] = b2_nullBodyId;\n\t\t\tm_bodyUserData[i].index = i;\n\t\t}\n\n\t\tm_wait = 0.5f;\n\t\tm_force = 200.0f;\n\t}\n\n\tvoid SpawnDebris()\n\t{\n\t\tint index = -1;\n\t\tfor (int i = 0; i < e_count; ++i)\n\t\t{\n\t\t\tif (B2_IS_NULL( m_debrisIds[i] ))\n\t\t\t{\n\t\t\t\tindex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (index == -1)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Debris\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { RandomFloatRange( -38.0f, 38.0f ), RandomFloatRange( -38.0f, 38.0f ) };\n\t\tbodyDef.rotation = b2MakeRot( RandomFloatRange( -B2_PI, B2_PI ) );\n\t\tbodyDef.linearVelocity = { RandomFloatRange( -5.0f, 5.0f ), RandomFloatRange( -5.0f, 5.0f ) };\n\t\tbodyDef.angularVelocity = RandomFloatRange( -1.0f, 1.0f );\n\t\tbodyDef.gravityScale = 0.0f;\n\t\tbodyDef.userData = m_bodyUserData + index;\n\t\tm_debrisIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.restitution = 0.8f;\n\n\t\t// No events when debris hits debris\n\t\tshapeDef.enableContactEvents = false;\n\n\t\tif (( index + 1 ) % 3 == 0)\n\t\t{\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2CreateCircleShape( m_debrisIds[index], &shapeDef, &circle );\n\t\t}\n\t\telse if (( index + 1 ) % 2 == 0)\n\t\t{\n\t\t\tb2Capsule capsule = { { 0.0f, -0.25f }, { 0.0f, 0.25f }, 0.25f };\n\t\t\tb2CreateCapsuleShape( m_debrisIds[index], &shapeDef, &capsule );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb2Polygon box = b2MakeBox( 0.4f, 0.6f );\n\t\t\tb2CreatePolygonShape( m_debrisIds[index], &shapeDef, &box );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 60.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Contact Event\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tImGui::SliderFloat( \"force\", &m_force, 100.0f, 500.0f, \"%.1f\" );\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tDrawTextLine( \"move using WASD\" );\n\n\t\tb2Vec2 position = b2Body_GetPosition( m_playerId );\n\n\t\tif (glfwGetKey( m_context->window, GLFW_KEY_A ) == GLFW_PRESS)\n\t\t{\n\t\t\tb2Body_ApplyForce( m_playerId, { -m_force, 0.0f }, position, true );\n\t\t}\n\n\t\tif (glfwGetKey( m_context->window, GLFW_KEY_D ) == GLFW_PRESS)\n\t\t{\n\t\t\tb2Body_ApplyForce( m_playerId, { m_force, 0.0f }, position, true );\n\t\t}\n\n\t\tif (glfwGetKey( m_context->window, GLFW_KEY_W ) == GLFW_PRESS)\n\t\t{\n\t\t\tb2Body_ApplyForce( m_playerId, { 0.0f, m_force }, position, true );\n\t\t}\n\n\t\tif (glfwGetKey( m_context->window, GLFW_KEY_S ) == GLFW_PRESS)\n\t\t{\n\t\t\tb2Body_ApplyForce( m_playerId, { 0.0f, -m_force }, position, true );\n\t\t}\n\n\t\tSample::Step();\n\n\t\t// Discover rings that touch the bottom sensor\n\t\tint debrisToAttach[e_count] = {};\n\t\tb2ShapeId shapesToDestroy[e_count] = { b2_nullShapeId };\n\t\tint attachCount = 0;\n\t\tint destroyCount = 0;\n\n\t\tstd::vector<b2ContactData> contactData;\n\n\t\t// Process contact begin touch events.\n\t\tb2ContactEvents contactEvents = b2World_GetContactEvents( m_worldId );\n\t\tfor (int i = 0; i < contactEvents.beginCount; ++i)\n\t\t{\n\t\t\tb2ContactBeginTouchEvent event = contactEvents.beginEvents[i];\n\t\t\tb2BodyId bodyIdA = b2Shape_GetBody( event.shapeIdA );\n\t\t\tb2BodyId bodyIdB = b2Shape_GetBody( event.shapeIdB );\n\n\t\t\t// The begin touch events have the contact manifolds, but the impulses are zero. This is because the manifolds\n\t\t\t// are gathered before the contact solver is run.\n\n\t\t\t// We can get the final contact data from the shapes. The manifold is shared by the two shapes, so we just need the\n\t\t\t// contact data from one of the shapes. Choose the one with the smallest number of contacts.\n\n\t\t\tint capacityA = b2Shape_GetContactCapacity( event.shapeIdA );\n\t\t\tint capacityB = b2Shape_GetContactCapacity( event.shapeIdB );\n\n\t\t\tif (capacityA < capacityB)\n\t\t\t{\n\t\t\t\tcontactData.resize( capacityA );\n\n\t\t\t\t// The count may be less than the capacity\n\t\t\t\tint countA = b2Shape_GetContactData( event.shapeIdA, contactData.data(), capacityA );\n\t\t\t\tassert( countA >= 1 );\n\n\t\t\t\tfor (int j = 0; j < countA; ++j)\n\t\t\t\t{\n\t\t\t\t\tb2ShapeId idA = contactData[j].shapeIdA;\n\t\t\t\t\tb2ShapeId idB = contactData[j].shapeIdB;\n\t\t\t\t\tif (B2_ID_EQUALS( idA, event.shapeIdB ) || B2_ID_EQUALS( idB, event.shapeIdB ))\n\t\t\t\t\t{\n\t\t\t\t\t\tassert( B2_ID_EQUALS( idA, event.shapeIdA ) || B2_ID_EQUALS( idB, event.shapeIdA ) );\n\n\t\t\t\t\t\tb2Manifold manifold = contactData[j].manifold;\n\t\t\t\t\t\tb2Vec2 normal = manifold.normal;\n\t\t\t\t\t\tassert( b2AbsFloat( b2Length( normal ) - 1.0f ) < 4.0f * FLT_EPSILON );\n\n\t\t\t\t\t\tfor (int k = 0; k < manifold.pointCount; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb2ManifoldPoint point = manifold.points[k];\n\t\t\t\t\t\t\tDrawLine( m_draw, point.point, point.point + point.totalNormalImpulse * normal, b2_colorBlueViolet );\n\t\t\t\t\t\t\tDrawPoint( m_draw, point.point, 10.0f, b2_colorWhite );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcontactData.resize( capacityB );\n\n\t\t\t\t// The count may be less than the capacity\n\t\t\t\tint countB = b2Shape_GetContactData( event.shapeIdB, contactData.data(), capacityB );\n\t\t\t\tassert( countB >= 1 );\n\n\t\t\t\tfor (int j = 0; j < countB; ++j)\n\t\t\t\t{\n\t\t\t\t\tb2ShapeId idA = contactData[j].shapeIdA;\n\t\t\t\t\tb2ShapeId idB = contactData[j].shapeIdB;\n\n\t\t\t\t\tif (B2_ID_EQUALS( idA, event.shapeIdA ) || B2_ID_EQUALS( idB, event.shapeIdA ))\n\t\t\t\t\t{\n\t\t\t\t\t\tassert( B2_ID_EQUALS( idA, event.shapeIdB ) || B2_ID_EQUALS( idB, event.shapeIdB ) );\n\n\t\t\t\t\t\tb2Manifold manifold = contactData[j].manifold;\n\t\t\t\t\t\tb2Vec2 normal = manifold.normal;\n\t\t\t\t\t\tassert( b2AbsFloat( b2Length( normal ) - 1.0f ) < 4.0f * FLT_EPSILON );\n\n\t\t\t\t\t\tfor (int k = 0; k < manifold.pointCount; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb2ManifoldPoint point = manifold.points[k];\n\t\t\t\t\t\t\tDrawLine( m_draw, point.point, point.point + point.totalNormalImpulse * normal, b2_colorYellowGreen );\n\t\t\t\t\t\t\tDrawPoint( m_draw, point.point, 10.0f, b2_colorWhite );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (B2_ID_EQUALS( bodyIdA, m_playerId ))\n\t\t\t{\n\t\t\t\tBodyUserData* userDataB = static_cast<BodyUserData*>( b2Body_GetUserData( bodyIdB ) );\n\t\t\t\tif (userDataB == nullptr)\n\t\t\t\t{\n\t\t\t\t\tif (B2_ID_EQUALS( event.shapeIdA, m_coreShapeId ) == false && destroyCount < e_count)\n\t\t\t\t\t{\n\t\t\t\t\t\t// player non-core shape hit the wall\n\n\t\t\t\t\t\tbool found = false;\n\t\t\t\t\t\tfor (int j = 0; j < destroyCount; ++j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (B2_ID_EQUALS( event.shapeIdA, shapesToDestroy[j] ))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// avoid double deletion\n\t\t\t\t\t\tif (found == false)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tshapesToDestroy[destroyCount] = event.shapeIdA;\n\t\t\t\t\t\t\tdestroyCount += 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (attachCount < e_count)\n\t\t\t\t{\n\t\t\t\t\tdebrisToAttach[attachCount] = userDataB->index;\n\t\t\t\t\tattachCount += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Only expect events for the player\n\t\t\t\tassert( B2_ID_EQUALS( bodyIdB, m_playerId ) );\n\t\t\t\tBodyUserData* userDataA = static_cast<BodyUserData*>( b2Body_GetUserData( bodyIdA ) );\n\t\t\t\tif (userDataA == nullptr)\n\t\t\t\t{\n\t\t\t\t\tif (B2_ID_EQUALS( event.shapeIdB, m_coreShapeId ) == false && destroyCount < e_count)\n\t\t\t\t\t{\n\t\t\t\t\t\t// player non-core shape hit the wall\n\n\t\t\t\t\t\tbool found = false;\n\t\t\t\t\t\tfor (int j = 0; j < destroyCount; ++j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (B2_ID_EQUALS( event.shapeIdB, shapesToDestroy[j] ))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// avoid double deletion\n\t\t\t\t\t\tif (found == false)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tshapesToDestroy[destroyCount] = event.shapeIdB;\n\t\t\t\t\t\t\tdestroyCount += 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (attachCount < e_count)\n\t\t\t\t{\n\t\t\t\t\tdebrisToAttach[attachCount] = userDataA->index;\n\t\t\t\t\tattachCount += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Attach debris to player body\n\t\tfor (int i = 0; i < attachCount; ++i)\n\t\t{\n\t\t\tint index = debrisToAttach[i];\n\t\t\tb2BodyId debrisId = m_debrisIds[index];\n\t\t\tif (B2_IS_NULL( debrisId ))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2Transform playerTransform = b2Body_GetTransform( m_playerId );\n\t\t\tb2Transform debrisTransform = b2Body_GetTransform( debrisId );\n\t\t\tb2Transform relativeTransform = b2InvMulTransforms( playerTransform, debrisTransform );\n\n\t\t\tint shapeCount = b2Body_GetShapeCount( debrisId );\n\t\t\tif (shapeCount == 0)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2ShapeId shapeId;\n\t\t\tb2Body_GetShapes( debrisId, &shapeId, 1 );\n\n\t\t\tb2ShapeType type = b2Shape_GetType( shapeId );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.enableContactEvents = true;\n\n\t\t\tswitch (type)\n\t\t\t{\n\t\t\t\tcase b2_circleShape:\n\t\t\t\t{\n\t\t\t\t\tb2Circle circle = b2Shape_GetCircle( shapeId );\n\t\t\t\t\tcircle.center = b2TransformPoint( relativeTransform, circle.center );\n\n\t\t\t\t\tb2CreateCircleShape( m_playerId, &shapeDef, &circle );\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\t\tcase b2_capsuleShape:\n\t\t\t\t{\n\t\t\t\t\tb2Capsule capsule = b2Shape_GetCapsule( shapeId );\n\t\t\t\t\tcapsule.center1 = b2TransformPoint( relativeTransform, capsule.center1 );\n\t\t\t\t\tcapsule.center2 = b2TransformPoint( relativeTransform, capsule.center2 );\n\n\t\t\t\t\tb2CreateCapsuleShape( m_playerId, &shapeDef, &capsule );\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\t\tcase b2_polygonShape:\n\t\t\t\t{\n\t\t\t\t\tb2Polygon originalPolygon = b2Shape_GetPolygon( shapeId );\n\t\t\t\t\tb2Polygon polygon = b2TransformPolygon( relativeTransform, &originalPolygon );\n\n\t\t\t\t\tb2CreatePolygonShape( m_playerId, &shapeDef, &polygon );\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tassert( false );\n\t\t\t}\n\n\t\t\tb2DestroyBody( debrisId );\n\t\t\tm_debrisIds[index] = b2_nullBodyId;\n\t\t}\n\n\t\tfor (int i = 0; i < destroyCount; ++i)\n\t\t{\n\t\t\tbool updateMass = false;\n\t\t\tb2DestroyShape( shapesToDestroy[i], updateMass );\n\t\t}\n\n\t\tif (destroyCount > 0)\n\t\t{\n\t\t\t// Update mass just once\n\t\t\tb2Body_ApplyMassFromShapes( m_playerId );\n\t\t}\n\n\t\tif (m_context->hertz > 0.0f && m_context->pause == false)\n\t\t{\n\t\t\tm_wait -= 1.0f / m_context->hertz;\n\t\t\tif (m_wait < 0.0f)\n\t\t\t{\n\t\t\t\tSpawnDebris();\n\t\t\t\tm_wait += 0.5f;\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ContactEvent( context );\n\t}\n\n\tb2BodyId m_playerId;\n\tb2ShapeId m_coreShapeId;\n\tb2BodyId m_debrisIds[e_count];\n\tBodyUserData m_bodyUserData[e_count];\n\tfloat m_force;\n\tfloat m_wait;\n};\n\nstatic int sampleWeeble = RegisterSample( \"Events\", \"Contact\", ContactEvent::Create );\n\n// Shows how to make a rigid body character mover and use the pre-solve callback. In this\n// case the platform should get the pre-solve event, not the player.\nclass Platform : public Sample\n{\npublic:\n\texplicit Platform( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.5f, 7.5f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t}\n\n\t\tb2World_SetPreSolveCallback( m_worldId, PreSolveStatic, this );\n\n\t\t// Ground\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Static Platform\n\t\t// This tests pre-solve with continuous collision\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_staticBody;\n\t\t\tbodyDef.position = { -6.0f, 6.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\t// Need to turn this on to get the callback\n\t\t\tshapeDef.enablePreSolveEvents = true;\n\n\t\t\tb2Polygon box = b2MakeBox( 2.0f, 0.5f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t// Moving Platform\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.position = { 0.0f, 6.0f };\n\t\t\tbodyDef.linearVelocity = { 2.0f, 0.0f };\n\t\t\tm_movingPlatformId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\t// Need to turn this on to get the callback\n\t\t\tshapeDef.enablePreSolveEvents = true;\n\n\t\t\tb2Polygon box = b2MakeBox( 3.0f, 0.5f );\n\t\t\tb2CreatePolygonShape( m_movingPlatformId, &shapeDef, &box );\n\t\t}\n\n\t\t// Player\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.motionLocks.angularZ = true;\n\t\t\tbodyDef.linearDamping = 0.5f;\n\t\t\tbodyDef.position = { 0.0f, 1.0f };\n\t\t\tm_playerId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tm_radius = 0.5f;\n\t\t\tb2Capsule capsule = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, m_radius };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.1f;\n\n\t\t\tm_playerShapeId = b2CreateCapsuleShape( m_playerId, &shapeDef, &capsule );\n\t\t}\n\n\t\tm_force = 25.0f;\n\t\tm_impulse = 25.0f;\n\t\tm_jumpDelay = 0.25f;\n\t\tm_jumping = false;\n\t}\n\n\tstatic bool PreSolveStatic( b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Vec2 point, b2Vec2 normal, void* context )\n\t{\n\t\tPlatform* self = static_cast<Platform*>( context );\n\t\treturn self->PreSolve( shapeIdA, shapeIdB, point, normal );\n\t}\n\n\t// This callback must be thread-safe. It may be called multiple times simultaneously.\n\t// Notice how this method is constant and doesn't change any data. It also\n\t// does not try to access any values in the world that may be changing, such as contact data.\n\tbool PreSolve( b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Vec2 point, b2Vec2 normal ) const\n\t{\n\t\tassert( b2Shape_IsValid( shapeIdA ) );\n\t\tassert( b2Shape_IsValid( shapeIdB ) );\n\n\t\tfloat sign = 0.0f;\n\t\tif (B2_ID_EQUALS( shapeIdA, m_playerShapeId ))\n\t\t{\n\t\t\tsign = -1.0f;\n\t\t}\n\t\telse if (B2_ID_EQUALS( shapeIdB, m_playerShapeId ))\n\t\t{\n\t\t\tsign = 1.0f;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// not colliding with the player, enable contact\n\t\t\treturn true;\n\t\t}\n\n\t\tif (sign * normal.y > 0.95f)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\t// normal points down, disable contact\n\t\treturn false;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 100.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"One-Sided Platform\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tImGui::SliderFloat( \"force\", &m_force, 0.0f, 50.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"impulse\", &m_impulse, 0.0f, 50.0f, \"%.1f\" );\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tbool canJump = false;\n\t\tb2Vec2 velocity = b2Body_GetLinearVelocity( m_playerId );\n\t\tif (m_jumpDelay == 0.0f && m_jumping == false && velocity.y < 0.01f)\n\t\t{\n\t\t\tint capacity = b2Body_GetContactCapacity( m_playerId );\n\t\t\tcapacity = b2MinInt( capacity, 4 );\n\t\t\tb2ContactData contactData[4];\n\t\t\tint count = b2Body_GetContactData( m_playerId, contactData, capacity );\n\t\t\tfor (int i = 0; i < count; ++i)\n\t\t\t{\n\t\t\t\tb2BodyId bodyIdA = b2Shape_GetBody( contactData[i].shapeIdA );\n\t\t\t\tfloat sign = 0.0f;\n\t\t\t\tif (B2_ID_EQUALS( bodyIdA, m_playerId ))\n\t\t\t\t{\n\t\t\t\t\t// normal points from A to B\n\t\t\t\t\tsign = -1.0f;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsign = 1.0f;\n\t\t\t\t}\n\n\t\t\t\tif (sign * contactData[i].manifold.normal.y > 0.9f)\n\t\t\t\t{\n\t\t\t\t\tcanJump = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// A kinematic body is moved by setting its velocity. This\n\t\t// ensure friction works correctly.\n\t\tb2Vec2 platformPosition = b2Body_GetPosition( m_movingPlatformId );\n\t\tif (platformPosition.x < -15.0f)\n\t\t{\n\t\t\tb2Body_SetLinearVelocity( m_movingPlatformId, { 2.0f, 0.0f } );\n\t\t}\n\t\telse if (platformPosition.x > 15.0f)\n\t\t{\n\t\t\tb2Body_SetLinearVelocity( m_movingPlatformId, { -2.0f, 0.0f } );\n\t\t}\n\n\t\tif (glfwGetKey( m_context->window, GLFW_KEY_A ) == GLFW_PRESS)\n\t\t{\n\t\t\tb2Body_ApplyForceToCenter( m_playerId, { -m_force, 0.0f }, true );\n\t\t}\n\n\t\tif (glfwGetKey( m_context->window, GLFW_KEY_D ) == GLFW_PRESS)\n\t\t{\n\t\t\tb2Body_ApplyForceToCenter( m_playerId, { m_force, 0.0f }, true );\n\t\t}\n\n\t\tint keyState = glfwGetKey( m_context->window, GLFW_KEY_SPACE );\n\t\tif (keyState == GLFW_PRESS)\n\t\t{\n\t\t\tif (canJump)\n\t\t\t{\n\t\t\t\tb2Body_ApplyLinearImpulseToCenter( m_playerId, { 0.0f, m_impulse }, true );\n\t\t\t\tm_jumpDelay = 0.5f;\n\t\t\t\tm_jumping = true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_jumping = false;\n\t\t}\n\n\t\tSample::Step();\n\n\t\tb2ContactData contactData = {};\n\t\tint contactCount = b2Body_GetContactData( m_movingPlatformId, &contactData, 1 );\n\t\tDrawTextLine( \"Platform contact count = %d, point count = %d\", contactCount, contactData.manifold.pointCount );\n\t\tDrawTextLine( \"Movement: A/D/Space\" );\n\t\tDrawTextLine( \"Can jump = %s\", canJump ? \"true\" : \"false\" );\n\n\t\tif (m_context->hertz > 0.0f)\n\t\t{\n\t\t\tm_jumpDelay = b2MaxFloat( 0.0f, m_jumpDelay - 1.0f / m_context->hertz );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Platform( context );\n\t}\n\n\tbool m_jumping;\n\tfloat m_radius;\n\tfloat m_force;\n\tfloat m_impulse;\n\tfloat m_jumpDelay;\n\tb2BodyId m_playerId;\n\tb2ShapeId m_playerShapeId;\n\tb2BodyId m_movingPlatformId;\n};\n\nstatic int samplePlatformer = RegisterSample( \"Events\", \"Platformer\", Platform::Create );\n\n// This shows how to process body events.\nclass BodyMove : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_count = 50\n\t};\n\n\texplicit BodyMove( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 2.0f, 8.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.55f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.1f;\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 12.0f, 0.1f, { -10.0f, -0.1f }, b2MakeRot( -0.15f * B2_PI ) );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 12.0f, 0.1f, { 10.0f, -0.1f }, b2MakeRot( 0.15f * B2_PI ) );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tshapeDef.material.restitution = 0.8f;\n\n\t\t\tbox = b2MakeOffsetBox( 0.1f, 10.0f, { 19.9f, 10.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 0.1f, 10.0f, { -19.9f, 10.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 20.0f, 0.1f, { 0.0f, 20.1f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tm_sleepCount = 0;\n\t\tm_count = 0;\n\n\t\tm_explosionPosition = { 0.0f, -5.0f };\n\t\tm_explosionRadius = 10.0f;\n\t\tm_explosionMagnitude = 10.0f;\n\t}\n\n\tvoid CreateBodies()\n\t{\n\t\tb2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f };\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.35f };\n\t\tb2Polygon square = b2MakeSquare( 0.35f );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tfloat x = -5.0f, y = 10.0f;\n\t\tfor (int i = 0; i < 10 && m_count < e_count; ++i)\n\t\t{\n\t\t\tbodyDef.position = { x, y };\n\t\t\tbodyDef.isBullet = ( m_count % 12 == 0 );\n\t\t\tbodyDef.userData = m_bodyIds + m_count;\n\t\t\tm_bodyIds[m_count] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tm_sleeping[m_count] = false;\n\n\t\t\tint remainder = m_count % 4;\n\t\t\tif (remainder == 0)\n\t\t\t{\n\t\t\t\tb2CreateCapsuleShape( m_bodyIds[m_count], &shapeDef, &capsule );\n\t\t\t}\n\t\t\telse if (remainder == 1)\n\t\t\t{\n\t\t\t\tb2CreateCircleShape( m_bodyIds[m_count], &shapeDef, &circle );\n\t\t\t}\n\t\t\telse if (remainder == 2)\n\t\t\t{\n\t\t\t\tb2CreatePolygonShape( m_bodyIds[m_count], &shapeDef, &square );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2Polygon poly = RandomPolygon( 0.75f );\n\t\t\t\tpoly.radius = 0.1f;\n\t\t\t\tb2CreatePolygonShape( m_bodyIds[m_count], &shapeDef, &poly );\n\t\t\t}\n\n\t\t\tm_count += 1;\n\t\t\tx += 1.0f;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 100.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Body Move\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tif (ImGui::Button( \"Explode\" ))\n\t\t{\n\t\t\tb2ExplosionDef def = b2DefaultExplosionDef();\n\t\t\tdef.position = m_explosionPosition;\n\t\t\tdef.radius = m_explosionRadius;\n\t\t\tdef.falloff = 0.1f;\n\t\t\tdef.impulsePerLength = m_explosionMagnitude;\n\t\t\tb2World_Explode( m_worldId, &def );\n\t\t}\n\n\t\tImGui::SliderFloat( \"Magnitude\", &m_explosionMagnitude, -20.0f, 20.0f, \"%.1f\" );\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif (m_context->pause == false && ( m_stepCount & 15 ) == 15 && m_count < e_count)\n\t\t{\n\t\t\tCreateBodies();\n\t\t}\n\n\t\tSample::Step();\n\n\t\t// Process body events\n\t\tb2BodyEvents events = b2World_GetBodyEvents( m_worldId );\n\t\tfor (int i = 0; i < events.moveCount; ++i)\n\t\t{\n\t\t\tconst b2BodyMoveEvent* event = events.moveEvents + i;\n\n\t\t\tif (event->userData == nullptr)\n\t\t\t{\n\t\t\t\t// The mouse joint body has no user data\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// draw the transform of every body that moved (not sleeping)\n\t\t\tDrawTransform( m_draw, event->transform, 1.0f );\n\n\t\t\tb2Transform transform = b2Body_GetTransform( event->bodyId );\n\t\t\tB2_ASSERT( transform.p.x == event->transform.p.x );\n\t\t\tB2_ASSERT( transform.p.y == event->transform.p.y );\n\t\t\tB2_ASSERT( transform.q.c == event->transform.q.c );\n\t\t\tB2_ASSERT( transform.q.s == event->transform.q.s );\n\n\t\t\t// this shows a somewhat contrived way to track body sleeping\n\t\t\tb2BodyId* bodyId = static_cast<b2BodyId*>( event->userData );\n\t\t\tptrdiff_t diff = bodyId - m_bodyIds;\n\t\t\tbool* sleeping = m_sleeping + diff;\n\n\t\t\tif (event->fellAsleep)\n\t\t\t{\n\t\t\t\t*sleeping = true;\n\t\t\t\tm_sleepCount += 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (*sleeping)\n\t\t\t\t{\n\t\t\t\t\t*sleeping = false;\n\t\t\t\t\tm_sleepCount -= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tDrawCircle( m_draw, m_explosionPosition, m_explosionRadius, b2_colorAzure );\n\n\t\tDrawTextLine( \"sleep count: %d\", m_sleepCount );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BodyMove( context );\n\t}\n\n\tb2BodyId m_bodyIds[e_count] = {};\n\tbool m_sleeping[e_count] = {};\n\tint m_count;\n\tint m_sleepCount;\n\tb2Vec2 m_explosionPosition;\n\tfloat m_explosionRadius;\n\tfloat m_explosionMagnitude;\n};\n\nstatic int sampleBodyMove = RegisterSample( \"Events\", \"Body Move\", BodyMove::Create );\n\nclass SensorTypes : public Sample\n{\npublic:\n\tenum CollisionBits\n\t{\n\t\tGROUND = 0x00000001,\n\t\tSENSOR = 0x00000002,\n\t\tDEFAULT = 0x00000004,\n\n\t\tALL_BITS = ( ~0u )\n\t};\n\n\texplicit SensorTypes( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 3.0f };\n\t\t\tm_context->camera.zoom = 4.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"ground\";\n\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\t// Enable sensor events, but filter them out as a test\n\t\t\tshapeDef.filter.categoryBits = GROUND;\n\t\t\tshapeDef.filter.maskBits = DEFAULT;\n\t\t\tshapeDef.enableSensorEvents = true;\n\n\t\t\tb2Segment groundSegment = { { -6.0f, 0.0f }, { 6.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &groundSegment );\n\n\t\t\tgroundSegment = { { -6.0f, 0.0f }, { -6.0f, 4.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &groundSegment );\n\n\t\t\tgroundSegment = { { 6.0f, 0.0f }, { 6.0f, 4.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &groundSegment );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"static sensor\";\n\t\t\tbodyDef.type = b2_staticBody;\n\t\t\tbodyDef.position = { -3.0f, 0.8f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter.categoryBits = SENSOR;\n\t\t\tshapeDef.isSensor = true;\n\t\t\tshapeDef.enableSensorEvents = true;\n\t\t\tb2Polygon box = b2MakeSquare( 1.0f );\n\t\t\tm_staticSensorId = b2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"kinematic sensor\";\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.position = { 0.0f, 0.0f };\n\t\t\tbodyDef.linearVelocity = { 0.0f, 1.0f };\n\t\t\tm_kinematicBodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter.categoryBits = SENSOR;\n\t\t\tshapeDef.isSensor = true;\n\t\t\tshapeDef.enableSensorEvents = true;\n\t\t\tb2Polygon box = b2MakeSquare( 1.0f );\n\t\t\tm_kinematicSensorId = b2CreatePolygonShape( m_kinematicBodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"dynamic sensor\";\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 3.0f, 1.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter.categoryBits = SENSOR;\n\t\t\tshapeDef.isSensor = true;\n\t\t\tshapeDef.enableSensorEvents = true;\n\t\t\tb2Polygon box = b2MakeSquare( 1.0f );\n\t\t\tm_dynamicSensorId = b2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\t// Add some real collision so the dynamic body is valid\n\t\t\tshapeDef.filter.categoryBits = DEFAULT;\n\t\t\tshapeDef.isSensor = false;\n\t\t\tshapeDef.enableSensorEvents = false;\n\t\t\tbox = b2MakeSquare( 0.8f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"ball_01\";\n\t\t\tbodyDef.position = { -5.0f, 1.0f };\n\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter.categoryBits = DEFAULT;\n\t\t\tshapeDef.filter.maskBits = GROUND | DEFAULT | SENSOR;\n\t\t\tshapeDef.enableSensorEvents = true;\n\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid PrintOverlaps( b2ShapeId sensorShapeId, const char* prefix )\n\t{\n\t\tchar buffer[256] = {};\n\n\t\t// Determine the necessary capacity\n\t\tint capacity = b2Shape_GetSensorCapacity( sensorShapeId );\n\t\tm_visitorIds.resize( capacity );\n\n\t\t// Get all overlaps and record the actual count\n\t\tint count = b2Shape_GetSensorData( sensorShapeId, m_visitorIds.data(), capacity );\n\t\tm_visitorIds.resize( count );\n\n\t\tint start = snprintf( buffer, sizeof( buffer ), \"%s: \", prefix );\n\t\tfor (int i = 0; i < count && start < sizeof( buffer ); ++i)\n\t\t{\n\t\t\tb2ShapeId visitorId = m_visitorIds[i];\n\t\t\tif (b2Shape_IsValid( visitorId ) == false)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2BodyId bodyId = b2Shape_GetBody( visitorId );\n\t\t\tconst char* name = b2Body_GetName( bodyId );\n\t\t\tif (name == nullptr)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// todo fix this\n\t\t\tstart += snprintf( buffer + start, sizeof( buffer ) - start, \"%s, \", name );\n\t\t}\n\n\t\tDrawTextLine( buffer );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tb2Vec2 position = b2Body_GetPosition( m_kinematicBodyId );\n\t\tif (position.y < 0.0f)\n\t\t{\n\t\t\tb2Body_SetLinearVelocity( m_kinematicBodyId, { 0.0f, 1.0f } );\n\t\t\t// b2Body_SetKinematicTarget( m_kinematicBodyId );\n\t\t}\n\t\telse if (position.y > 3.0f)\n\t\t{\n\t\t\tb2Body_SetLinearVelocity( m_kinematicBodyId, { 0.0f, -1.0f } );\n\t\t}\n\n\t\tSample::Step();\n\n\t\tPrintOverlaps( m_staticSensorId, \"static\" );\n\t\tPrintOverlaps( m_kinematicSensorId, \"kinematic\" );\n\t\tPrintOverlaps( m_dynamicSensorId, \"dynamic\" );\n\n\t\tb2Vec2 origin = { 5.0f, 1.0f };\n\t\tb2Vec2 translation = { -10.0f, 0.0f };\n\t\tb2RayResult result = b2World_CastRayClosest( m_worldId, origin, translation, b2DefaultQueryFilter() );\n\t\tDrawLine( m_draw, origin, origin + translation, b2_colorDimGray );\n\n\t\tif (result.hit)\n\t\t{\n\t\t\tDrawPoint( m_draw, result.point, 10.0f, b2_colorCyan );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SensorTypes( context );\n\t}\n\n\tb2ShapeId m_staticSensorId;\n\tb2ShapeId m_kinematicSensorId;\n\tb2ShapeId m_dynamicSensorId;\n\n\tb2BodyId m_kinematicBodyId;\n\n\tstd::vector<b2ShapeId> m_visitorIds;\n};\n\nstatic int sampleSensorTypes = RegisterSample( \"Events\", \"Sensor Types\", SensorTypes::Create );\n\n// This sample shows how to break joints when the internal reaction force becomes large. Instead of polling, this uses events.\nclass JointEvent : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_count = 6\n\t};\n\n\texplicit JointEvent( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 8.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.7f;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } };\n\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\tfor (int i = 0; i < e_count; ++i)\n\t\t{\n\t\t\tm_jointIds[i] = b2_nullJointId;\n\t\t}\n\n\t\tb2Vec2 position = { -12.5f, 10.0f };\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.enableSleep = false;\n\n\t\tb2Polygon box = b2MakeBox( 1.0f, 1.0f );\n\n\t\tint index = 0;\n\n\t\tfloat forceThreshold = 20000.0f;\n\t\tfloat torqueThreshold = 10000.0f;\n\n\t\t// distance joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tfloat length = 2.0f;\n\t\t\tb2Vec2 pivot1 = { position.x, position.y + 1.0f + length };\n\t\t\tb2Vec2 pivot2 = { position.x, position.y + 1.0f };\n\t\t\tb2DistanceJointDef jointDef = b2DefaultDistanceJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot1 );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot2 );\n\t\t\tjointDef.length = length;\n\t\t\tjointDef.base.forceThreshold = forceThreshold;\n\t\t\tjointDef.base.torqueThreshold = torqueThreshold;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tjointDef.base.userData = (void*)(intptr_t)index;\n\t\t\tm_jointIds[index] = b2CreateDistanceJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// motor joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2MotorJointDef jointDef = b2DefaultMotorJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = position;\n\t\t\tjointDef.maxVelocityForce = 1000.0f;\n\t\t\tjointDef.maxVelocityTorque = 20.0f;\n\t\t\tjointDef.base.forceThreshold = forceThreshold;\n\t\t\tjointDef.base.torqueThreshold = torqueThreshold;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tjointDef.base.userData = (void*)(intptr_t)index;\n\t\t\tm_jointIds[index] = b2CreateMotorJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// prismatic joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.base.forceThreshold = forceThreshold;\n\t\t\tjointDef.base.torqueThreshold = torqueThreshold;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tjointDef.base.userData = (void*)(intptr_t)index;\n\t\t\tm_jointIds[index] = b2CreatePrismaticJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// revolute joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.base.forceThreshold = forceThreshold;\n\t\t\tjointDef.base.torqueThreshold = torqueThreshold;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tjointDef.base.userData = (void*)(intptr_t)index;\n\t\t\tm_jointIds[index] = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// weld joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2WeldJointDef jointDef = b2DefaultWeldJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.angularHertz = 2.0f;\n\t\t\tjointDef.angularDampingRatio = 0.5f;\n\t\t\tjointDef.base.forceThreshold = forceThreshold;\n\t\t\tjointDef.base.torqueThreshold = torqueThreshold;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tjointDef.base.userData = (void*)(intptr_t)index;\n\t\t\tm_jointIds[index] = b2CreateWeldJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// wheel joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2WheelJointDef jointDef = b2DefaultWheelJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.hertz = 1.0f;\n\t\t\tjointDef.dampingRatio = 0.7f;\n\t\t\tjointDef.lowerTranslation = -1.0f;\n\t\t\tjointDef.upperTranslation = 1.0f;\n\t\t\tjointDef.enableLimit = true;\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorTorque = 10.0f;\n\t\t\tjointDef.motorSpeed = 1.0f;\n\t\t\tjointDef.base.forceThreshold = forceThreshold;\n\t\t\tjointDef.base.torqueThreshold = torqueThreshold;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tjointDef.base.userData = (void*)(intptr_t)index;\n\t\t\tm_jointIds[index] = b2CreateWheelJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\t// Process joint events\n\t\tb2JointEvents events = b2World_GetJointEvents( m_worldId );\n\t\tfor (int i = 0; i < events.count; ++i)\n\t\t{\n\t\t\t// Destroy the joint if it is still valid\n\t\t\tconst b2JointEvent* event = events.jointEvents + i;\n\n\t\t\tif (b2Joint_IsValid( event->jointId ))\n\t\t\t{\n\t\t\t\tint index = (int)(intptr_t)event->userData;\n\t\t\t\tassert( 0 <= index && index < e_count );\n\t\t\t\tb2DestroyJoint( event->jointId, true );\n\t\t\t\tm_jointIds[index] = b2_nullJointId;\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new JointEvent( context );\n\t}\n\n\tb2JointId m_jointIds[e_count];\n};\n\nstatic int sampleJointEvent = RegisterSample( \"Events\", \"Joint\", JointEvent::Create );\n\nclass PersistentContact : public Sample\n{\npublic:\n\texplicit PersistentContact( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 6.0f };\n\t\t\tm_context->camera.zoom = 7.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Vec2 points[22];\n\t\t\tfloat x = 10.0f;\n\t\t\tfor (int i = 0; i < 20; ++i)\n\t\t\t{\n\t\t\t\tpoints[i] = { x, 0.0f };\n\t\t\t\tx -= 1.0f;\n\t\t\t}\n\n\t\t\tpoints[20] = { -9.0f, 10.0f };\n\t\t\tpoints[21] = { 10.0f, 10.0f };\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = 22;\n\t\t\tchainDef.isLoop = true;\n\n\t\t\tb2CreateChain( groundId, &chainDef );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -8.0f, 1.0f };\n\t\t\tbodyDef.linearVelocity = { 2.0f, 0.0f };\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.enableContactEvents = true;\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\n\t\tm_contactId = b2_nullContactId;\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2ContactEvents events = b2World_GetContactEvents( m_worldId );\n\t\tfor (int i = 0; i < events.beginCount && i < 1; ++i)\n\t\t{\n\t\t\tb2ContactBeginTouchEvent event = events.beginEvents[i];\n\t\t\tm_contactId = events.beginEvents[i].contactId;\n\t\t}\n\n\t\tfor (int i = 0; i < events.endCount; ++i)\n\t\t{\n\t\t\tif (B2_ID_EQUALS( m_contactId, events.endEvents[i].contactId ))\n\t\t\t{\n\t\t\t\tm_contactId = b2_nullContactId;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (B2_IS_NON_NULL( m_contactId ) && b2Contact_IsValid( m_contactId ))\n\t\t{\n\t\t\tb2ContactData data = b2Contact_GetData( m_contactId );\n\n\t\t\tfor (int i = 0; i < data.manifold.pointCount; ++i)\n\t\t\t{\n\t\t\t\tconst b2ManifoldPoint* manifoldPoint = data.manifold.points + i;\n\t\t\t\tb2Vec2 p1 = manifoldPoint->point;\n\t\t\t\tb2Vec2 p2 = p1 + manifoldPoint->totalNormalImpulse * data.manifold.normal;\n\t\t\t\tDrawLine( m_draw, p1, p2, b2_colorCrimson );\n\t\t\t\tDrawPoint( m_draw, p1, 6.0f, b2_colorCrimson );\n\t\t\t\tDrawWorldString( m_draw, m_camera, p1, b2_colorWhite, \"%.2f\", manifoldPoint->totalNormalImpulse );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_contactId = b2_nullContactId;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new PersistentContact( context );\n\t}\n\n\tb2ContactId m_contactId;\n};\n\nstatic int samplePersistentContact = RegisterSample( \"Events\", \"Persistent Contact\", PersistentContact::Create );\n\nclass SensorHits : public Sample\n{\npublic:\n\texplicit SensorHits( SampleContext* context )\n\t\t: Sample( context )\n\t\t, m_transforms{}\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 7.5f;\n\t\t}\n\n\t\tb2BodyId groundId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"ground\";\n\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2Segment groundSegment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &groundSegment );\n\n\t\t\tgroundSegment = { { 10.0f, 0.0f }, { 10.0f, 10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &groundSegment );\n\t\t}\n\n\t\t// Static sensor\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"static sensor\";\n\t\t\tbodyDef.position = { -4.0f, 1.0f };\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.isSensor = true;\n\t\t\tshapeDef.enableSensorEvents = true;\n\n\t\t\tb2Segment segment = { { 0.0f, 0.0f }, { 0.0f, 10.0f } };\n\t\t\tm_staticSensorId = b2CreateSegmentShape( bodyId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Kinematic sensor\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"kinematic sensor\";\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.position = { 0.0f, 1.0f };\n\t\t\tbodyDef.linearVelocity = { 0.5f, 0.0f };\n\n\t\t\tm_kinematicBodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.isSensor = true;\n\t\t\tshapeDef.enableSensorEvents = true;\n\n\t\t\tb2Segment segment = { { 0.0f, 0.0f }, { 0.0f, 10.0f } };\n\t\t\tm_kinematicSensorId = b2CreateSegmentShape( m_kinematicBodyId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Dynamic sensor\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"dynamic sensor\";\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 4.0f, 1.0f };\n\n\t\t\tm_dynamicBodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.isSensor = true;\n\t\t\tshapeDef.enableSensorEvents = true;\n\n\t\t\tb2Capsule capsule = { { 0.0f, 1.0f }, { 0.0f, 9.0f }, 0.1f };\n\t\t\tm_dynamicSensorId = b2CreateCapsuleShape( m_dynamicBodyId, &shapeDef, &capsule );\n\n\t\t\tb2Vec2 pivot = bodyDef.position + b2Vec2{ 0.0f, 6.0f };\n\t\t\tb2Vec2 axis = { 1.0f, 0.0f };\n\t\t\tb2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_dynamicBodyId;\n\t\t\tjointDef.base.localFrameA.q = b2MakeRotFromUnitVector( axis );\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( groundId, pivot );\n\t\t\tjointDef.base.localFrameB.q = b2MakeRotFromUnitVector( axis );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( m_dynamicBodyId, pivot );\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorForce = 1000.0f;\n\t\t\tjointDef.motorSpeed = 0.5f;\n\n\t\t\tm_jointId = b2CreatePrismaticJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tm_beginCount = 0;\n\t\tm_endCount = 0;\n\t\tm_bodyId = {};\n\t\tm_shapeId = {};\n\t\tm_transformCount = 0;\n\t\tm_isBullet = true;\n\n\t\tLaunch();\n\t}\n\n\tvoid Launch()\n\t{\n\t\tif (B2_IS_NON_NULL( m_bodyId ))\n\t\t{\n\t\t\tb2DestroyBody( m_bodyId );\n\t\t}\n\n\t\tm_transformCount = 0;\n\t\tm_beginCount = 0;\n\t\tm_endCount = 0;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { -26.7f, 6.0f };\n\t\tfloat speed = RandomFloatRange( 200.0f, 300.0f );\n\t\tbodyDef.linearVelocity = { speed, 0.0f };\n\t\tbodyDef.isBullet = m_isBullet;\n\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.enableSensorEvents = true;\n\t\tshapeDef.material.friction = 0.8f;\n\t\tshapeDef.material.rollingResistance = 0.01f;\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.25f };\n\t\tm_shapeId = b2CreateCircleShape( m_bodyId, &shapeDef, &circle );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 120.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 120.0f, height ) );\n\n\t\tImGui::Begin( \"Sensor Hit\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::Checkbox( \"Bullet\", &m_isBullet );\n\n\t\tif (ImGui::Button( \"Launch\" ) || glfwGetKey( m_context->window, GLFW_KEY_B ) == GLFW_PRESS)\n\t\t{\n\t\t\tLaunch();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid CollectTransforms( b2ShapeId sensorShapeId )\n\t{\n\t\tconstexpr int capacity = 5;\n\t\tb2ShapeId visitorIds[capacity];\n\t\tint count = b2Shape_GetSensorData( sensorShapeId, visitorIds, capacity );\n\n\t\tfor (int i = 0; i < count && m_transformCount < m_transformCapacity; ++i)\n\t\t{\n\t\t\tb2BodyId sensorBodyId = b2Shape_GetBody( sensorShapeId );\n\t\t\tm_transforms[m_transformCount] = b2Body_GetTransform( sensorBodyId );\n\t\t\tm_transformCount += 1;\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tb2Vec2 p = b2Body_GetPosition( m_kinematicBodyId );\n\t\tif (p.x > 1.0f)\n\t\t{\n\t\t\tb2Body_SetLinearVelocity( m_kinematicBodyId, { -0.5f, 0.0f } );\n\t\t}\n\t\telse if (p.x < -1.0f)\n\t\t{\n\t\t\tb2Body_SetLinearVelocity( m_kinematicBodyId, { 0.5f, 0.0f } );\n\t\t}\n\n\t\tfloat x = b2PrismaticJoint_GetTranslation( m_jointId );\n\t\tif (x > 1.0f)\n\t\t{\n\t\t\tb2PrismaticJoint_SetMotorSpeed( m_jointId, -0.5f );\n\t\t}\n\t\telse if (x < -1.0f)\n\t\t{\n\t\t\tb2PrismaticJoint_SetMotorSpeed( m_jointId, 0.5f );\n\t\t}\n\n\t\tSample::Step();\n\n\t\tfor (int i = 0; i < m_transformCount; ++i)\n\t\t{\n\t\t\tDrawTransform( m_draw, m_transforms[i], 1.0f );\n\t\t}\n\n\t\tb2SensorEvents sensorEvents = b2World_GetSensorEvents( m_worldId );\n\t\tm_beginCount += sensorEvents.beginCount;\n\t\tm_endCount += sensorEvents.endCount;\n\n\t\tfor (int i = 0; i < sensorEvents.beginCount; ++i)\n\t\t{\n\t\t\tconst b2SensorBeginTouchEvent* event = sensorEvents.beginEvents + i;\n\t\t\tif (b2Shape_IsValid( event->sensorShapeId ) == true)\n\t\t\t{\n\t\t\t\tCollectTransforms( event->sensorShapeId );\n\t\t\t}\n\t\t}\n\n\t\tDrawTextLine( \"begin touch count = %d\", m_beginCount );\n\t\tDrawTextLine( \"end touch count = %d\", m_endCount );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SensorHits( context );\n\t}\n\n\tb2ShapeId m_staticSensorId;\n\tb2ShapeId m_kinematicSensorId;\n\tb2ShapeId m_dynamicSensorId;\n\n\tb2BodyId m_kinematicBodyId;\n\tb2BodyId m_dynamicBodyId;\n\tb2JointId m_jointId;\n\n\tb2BodyId m_bodyId;\n\tb2ShapeId m_shapeId;\n\n\tstatic constexpr int m_transformCapacity = 20;\n\tint m_transformCount;\n\tb2Transform m_transforms[m_transformCapacity];\n\n\tbool m_isBullet;\n\tint m_beginCount;\n\tint m_endCount;\n};\n\nstatic int sampleSensorHits = RegisterSample( \"Events\", \"Sensor Hits\", SensorHits::Create );\n\n// This shows how to create a projectile that explodes on impact\nclass ProjectileEvent : public Sample\n{\npublic:\n\texplicit ProjectileEvent( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { -7.0f, 9.0f };\n\t\t\tm_context->camera.zoom = 14.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, 0.0f };\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.enableSensorEvents = true;\n\n\t\t\tb2Segment segment = { { 10.0f, 0.0f }, { 10.0f, 20.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tsegment = { { -30.0f, 0.0f }, { 30.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tm_projectileId = {};\n\t\tm_projectileShapeId = {};\n\t\tm_dragging = false;\n\t\tm_point1 = b2Vec2_zero;\n\t\tm_point2 = b2Vec2_zero;\n\n\t\tb2Polygon box = b2MakeRoundedBox( 0.45f, 0.45f, 0.05f );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.enableSensorEvents = true;\n\n\t\tfloat offset = 0.01f;\n\n\t\tfloat x = 8.0f;\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tfor (int i = 0; i < 8; ++i)\n\t\t{\n\t\t\tfloat shift = ( i % 2 == 0 ? -offset : offset );\n\t\t\tbodyDef.position = { x + shift, 0.5f + 1.0f * i };\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tvoid FireProjectile()\n\t{\n\t\tif (B2_IS_NON_NULL( m_projectileId ))\n\t\t{\n\t\t\tb2DestroyBody( m_projectileId );\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = m_point1;\n\t\tbodyDef.linearVelocity = 4.0f * ( m_point2 - m_point1 );\n\t\tbodyDef.isBullet = true;\n\n\t\tm_projectileId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.25f };\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.enableContactEvents = true;\n\t\tm_projectileShapeId = b2CreateCircleShape( m_projectileId, &shapeDef, &circle );\n\t}\n\n\tvoid MouseDown( b2Vec2 p, int button, int mods ) override\n\t{\n\t\tif (button == GLFW_MOUSE_BUTTON_1)\n\t\t{\n\t\t\tif (mods == GLFW_MOD_CONTROL)\n\t\t\t{\n\t\t\t\tm_dragging = true;\n\t\t\t\tm_point1 = p;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseUp( b2Vec2, int button ) override\n\t{\n\t\tif (button == GLFW_MOUSE_BUTTON_1)\n\t\t{\n\t\t\tif (m_dragging)\n\t\t\t{\n\t\t\t\tm_dragging = false;\n\t\t\t\tFireProjectile();\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MouseMove( b2Vec2 p ) override\n\t{\n\t\tif (m_dragging)\n\t\t{\n\t\t\tm_point2 = p;\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tDrawTextLine( \"Use Ctrl + Left Mouse to drag and shoot a projectile\" );\n\n\t\tSample::Step();\n\n\t\tif (m_dragging)\n\t\t{\n\t\t\tDrawLine( m_draw, m_point1, m_point2, b2_colorWhite );\n\t\t\tDrawPoint( m_draw, m_point1, 5.0f, b2_colorGreen );\n\t\t\tDrawPoint( m_draw, m_point2, 5.0f, b2_colorRed );\n\t\t}\n\n\t\tb2ContactEvents contactEvents = b2World_GetContactEvents( m_worldId );\n\t\tfor (int i = 0; i < contactEvents.beginCount; ++i)\n\t\t{\n\t\t\tconst b2ContactBeginTouchEvent* event = contactEvents.beginEvents + i;\n\n\t\t\tif (B2_ID_EQUALS( event->shapeIdA, m_projectileShapeId ) || B2_ID_EQUALS( event->shapeIdB, m_projectileShapeId ))\n\t\t\t{\n\t\t\t\tif (b2Contact_IsValid( event->contactId ))\n\t\t\t\t{\n\t\t\t\t\tb2ContactData data = b2Contact_GetData( event->contactId );\n\n\t\t\t\t\tif (data.manifold.pointCount > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tb2ExplosionDef explosionDef = b2DefaultExplosionDef();\n\t\t\t\t\t\texplosionDef.position = data.manifold.points[0].point;\n\t\t\t\t\t\texplosionDef.radius = 1.0f;\n\t\t\t\t\t\texplosionDef.impulsePerLength = 20.0f;\n\t\t\t\t\t\tb2World_Explode( m_worldId, &explosionDef );\n\n\t\t\t\t\t\tb2DestroyBody( m_projectileId );\n\t\t\t\t\t\tm_projectileId = b2_nullBodyId;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ProjectileEvent( context );\n\t}\n\n\tb2BodyId m_projectileId;\n\tb2ShapeId m_projectileShapeId;\n\tb2Vec2 m_point1;\n\tb2Vec2 m_point2;\n\tbool m_dragging;\n};\n\nstatic int sampleProjectileEvent = RegisterSample( \"Events\", \"Projectile Event\", ProjectileEvent::Create );\n\nclass CircleImpulse : public Sample\n{\npublic:\n\tstruct Event\n\t{\n\t\tfloat impulse;\n\t\tfloat totalImpulse;\n\t\tfloat speed;\n\t};\n\n\texplicit CircleImpulse( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif (m_context->restart == false)\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 2.7f };\n\t\t\tm_context->camera.zoom = 3.4f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tm_gravity = 10.0f;\n\t\tm_restitution = 0.25f;\n\t\tm_useGravity = false;\n\t\tm_useRestitution = false;\n\t\tm_mass = 1.0f;\n\t\tm_bodyId = b2_nullBodyId;\n\n\t\tSpawn();\n\t}\n\n\tvoid Spawn()\n\t{\n\t\tif (B2_IS_NON_NULL( m_bodyId ))\n\t\t{\n\t\t\tb2DestroyBody( m_bodyId );\n\t\t\tm_bodyId = b2_nullBodyId;\n\t\t}\n\n\t\tm_events.clear();\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.gravityScale = m_useGravity ? 1.0f : 0.0f;\n\t\tbodyDef.linearVelocity.y = -25.0f;\n\t\tbodyDef.position.y = 5.5f;\n\n\t\tb2Circle circle = {};\n\t\tcircle.radius = 0.25f;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.enableHitEvents = true;\n\t\tshapeDef.material.friction = 0.0f;\n\t\tshapeDef.material.restitution = m_useRestitution ? m_restitution : 0.0f;\n\n\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2CreateCircleShape( m_bodyId, &shapeDef, &circle );\n\n\t\t// Override mass\n\t\tb2MassData massData = b2Body_GetMassData( m_bodyId );\n\t\tfloat ratio = m_mass / massData.mass;\n\t\tmassData.mass = m_mass;\n\t\tmassData.rotationalInertia *= ratio;\n\t\tb2Body_SetMassData( m_bodyId, massData );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 6.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 10.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Circle Impulse\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tif (ImGui::Checkbox( \"gravity\", &m_useGravity ))\n\t\t{\n\t\t\tSpawn();\n\t\t}\n\n\t\tif (ImGui::Checkbox( \"restitution\", &m_useRestitution ))\n\t\t{\n\t\t\tSpawn();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2ContactEvents events = b2World_GetContactEvents( m_worldId );\n\t\tfor (int i = 0; i < events.hitCount; ++i)\n\t\t{\n\t\t\tb2ContactHitEvent* event = events.hitEvents + i;\n\n\t\t\tDrawPoint( m_draw, event->point, 10.0f, b2_colorWhite );\n\n\t\t\tb2ContactData data = b2Contact_GetData( event->contactId );\n\n\t\t\tEvent e = {};\n\t\t\te.speed = event->approachSpeed;\n\t\t\tif (data.manifold.pointCount > 0)\n\t\t\t{\n\t\t\t\te.impulse = data.manifold.points[0].normalImpulse;\n\t\t\t\te.totalImpulse = data.manifold.points[0].totalNormalImpulse;\n\t\t\t}\n\t\t\tm_events.push_back( e );\n\t\t}\n\n\t\tDrawTextLine( \"mass = %g, gravity = %g, restitution = %g\", m_mass, m_useGravity ? 10.0f : 0.0f,\n\t\t\t\t\t  m_useRestitution ? m_restitution : 0.0f );\n\n\t\tint eventCount = (int)m_events.size();\n\t\tfor (int i = 0; i < eventCount; ++i)\n\t\t{\n\t\t\tconst Event& e = m_events[i];\n\t\t\tDrawTextLine( \"hit speed = %g, hit momentum = %g, final impulse = %g, total impulse = %g\", e.speed, m_mass * e.speed, e.impulse,\n\t\t\t\t\t\t  e.totalImpulse );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new CircleImpulse( context );\n\t}\n\n\tfloat m_mass;\n\tstd::vector<Event> m_events;\n\tb2BodyId m_bodyId;\n\tfloat m_gravity;\n\tfloat m_restitution;\n\tbool m_useGravity;\n\tbool m_useRestitution;\n};\n\nstatic int sampleCircleImpulse = RegisterSample( \"Stacking\", \"Circle Impulse\", CircleImpulse::Create );\n"
  },
  {
    "path": "samples/sample_geometry.cpp",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"draw.h\"\n#include \"random.h\"\n#include \"sample.h\"\n\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n\nclass ConvexHull : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_count = B2_MAX_POLYGON_VERTICES\n\t};\n\n\texplicit ConvexHull( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.5f, 0.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.3f;\n\t\t}\n\n\t\tm_generation = 0;\n\t\tm_auto = false;\n\t\tm_bulk = false;\n\t\tGenerate();\n\t}\n\n\tvoid Generate()\n\t{\n#if 0\n\t\tm_points[0] = { 5.65314484f, 0.204832315f };\n\t\tm_points[1] = {-5.65314484f, -0.204832315f };\n\t\tm_points[2] = {2.34463644f, 1.15731204f };\n\t\tm_points[3] = {0.0508846045f, 3.23230696f };\n\t\tm_points[4] = {-5.65314484f, -0.204832315f };\n\t\tm_points[5] = {-5.65314484f, -0.204832315f };\n\t\tm_points[6] = {3.73758054f, -1.11098099f };\n\t\tm_points[7] = {1.33504069f, -4.43795443f };\n\n\t\tm_count = e_count;\n#elif 0\n\t\tm_points[0] = { -0.328125, 0.179688 };\n\t\tm_points[1] = { -0.203125, 0.304688 };\n\t\tm_points[2] = { 0.171875, 0.304688 };\n\t\tm_points[3] = { 0.359375, 0.117188 };\n\t\tm_points[4] = { 0.359375, -0.195313 };\n\t\tm_points[5] = { 0.234375, -0.320313 };\n\t\tm_points[6] = { -0.265625, -0.257813 };\n\t\tm_points[7] = { -0.328125, -0.132813 };\n\n\t\tb2Hull hull = b2ComputeHull( m_points, 8 );\n\t\tbool valid = b2ValidateHull( &hull );\n\t\tif ( valid == false )\n\t\t{\n\t\t\tassert( valid );\n\t\t}\n\n\t\tm_count = e_count;\n#else\n\n\t\tfloat angle = B2_PI * RandomFloat();\n\t\tb2Rot r = b2MakeRot( angle );\n\n\t\tb2Vec2 lowerBound = { -4.0f, -4.0f };\n\t\tb2Vec2 upperBound = { 4.0f, 4.0f };\n\n\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t{\n\t\t\tfloat x = 10.0f * RandomFloat();\n\t\t\tfloat y = 10.0f * RandomFloat();\n\n\t\t\t// Clamp onto a square to help create collinearities.\n\t\t\t// This will stress the convex hull algorithm.\n\t\t\tb2Vec2 v = b2Clamp( { x, y }, lowerBound, upperBound );\n\t\t\tm_points[i] = b2RotateVector( r, v );\n\t\t}\n\n\t\tm_count = e_count;\n#endif\n\n\t\tm_generation += 1;\n\t}\n\n\tvoid Keyboard( int key ) override\n\t{\n\t\tswitch ( key )\n\t\t{\n\t\t\tcase GLFW_KEY_A:\n\t\t\t\tm_auto = !m_auto;\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_B:\n\t\t\t\tm_bulk = !m_bulk;\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_G:\n\t\t\t\tGenerate();\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tDrawTextLine( \"Options: generate(g), auto(a), bulk(b)\" );\n\n\t\tb2Hull hull;\n\t\tbool valid = false;\n\t\tfloat milliseconds = 0.0f;\n\n\t\tif ( m_bulk )\n\t\t{\n#if 1\n\t\t\t// defect hunting\n\t\t\tfor ( int i = 0; i < 10000; ++i )\n\t\t\t{\n\t\t\t\tGenerate();\n\t\t\t\thull = b2ComputeHull( m_points, m_count );\n\t\t\t\tif ( hull.count == 0 )\n\t\t\t\t{\n\t\t\t\t\t// m_bulk = false;\n\t\t\t\t\t// break;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tvalid = b2ValidateHull( &hull );\n\t\t\t\tif ( valid == false || m_bulk == false )\n\t\t\t\t{\n\t\t\t\t\tm_bulk = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n#else\n\t\t\t// performance\n\t\t\tGenerate();\n\t\t\tb2Timer timer;\n\t\t\tfor ( int i = 0; i < 1000000; ++i )\n\t\t\t{\n\t\t\t\thull = b2ComputeHull( m_points, m_count );\n\t\t\t}\n\t\t\tvalid = hull.count > 0;\n\t\t\tmilliseconds = timer.GetMilliseconds();\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ( m_auto )\n\t\t\t{\n\t\t\t\tGenerate();\n\t\t\t}\n\n\t\t\thull = b2ComputeHull( m_points, m_count );\n\t\t\tif ( hull.count > 0 )\n\t\t\t{\n\t\t\t\tvalid = b2ValidateHull( &hull );\n\t\t\t\tif ( valid == false )\n\t\t\t\t{\n\t\t\t\t\tm_auto = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( valid == false )\n\t\t{\n\t\t\tDrawTextLine( \"generation = %d, FAILED\", m_generation );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDrawTextLine( \"generation = %d, count = %d\", m_generation, hull.count );\n\t\t}\n\n\t\tif ( milliseconds > 0.0f )\n\t\t{\n\t\t\tDrawTextLine( \"milliseconds = %g\", milliseconds );\n\t\t}\n\n\t\tDrawPolygon( m_draw, hull.points, hull.count, b2_colorGray );\n\n\t\tfor ( int32_t i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tDrawPoint( m_draw, m_points[i], 5.0f, b2_colorBlue );\n\t\t\tDrawWorldString( m_draw, m_camera, b2Add( m_points[i], { 0.1f, 0.1f } ), b2_colorWhite, \"%d\", i );\n\t\t}\n\n\t\tfor ( int32_t i = 0; i < hull.count; ++i )\n\t\t{\n\t\t\tDrawPoint( m_draw, hull.points[i], 6.0f, b2_colorGreen );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ConvexHull( context );\n\t}\n\n\tb2Vec2 m_points[B2_MAX_POLYGON_VERTICES];\n\tint32_t m_count;\n\tint32_t m_generation;\n\tbool m_auto;\n\tbool m_bulk;\n};\n\nstatic int sampleIndex = RegisterSample( \"Geometry\", \"Convex Hull\", ConvexHull::Create );\n"
  },
  {
    "path": "samples/sample_issues.cpp",
    "content": "#include \"GLFW/glfw3.h\"\n#include \"imgui.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n\nstruct PhysicsHitQueryResult2D\n{\n\tb2ShapeId shapeId = b2_nullShapeId;\n\tb2Vec2 point = b2Vec2_zero;\n\tb2Vec2 normal = b2Vec2_zero;\n\tb2Vec2 endPos = b2Vec2_zero;\n\tfloat fraction = 0.0f;\n\tbool startPenetrating = false;\n\tbool blockingHit = false;\n};\n\nstruct CastContext_Single\n{\n\tb2Vec2 startPos;\n\tb2Vec2 translation;\n\tPhysicsHitQueryResult2D result;\n\tbool hit = false;\n};\n\nstatic float b2CastResult_Closest( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* c )\n{\n\tCastContext_Single* context = static_cast<CastContext_Single*>( c );\n\n\tif ( b2Dot( context->translation, normal ) >= 0.0f )\n\t\treturn -1.0f;\n\n\tcontext->result.shapeId = shapeId;\n\tcontext->result.point = point;\n\tcontext->result.normal = normal;\n\tcontext->result.endPos = context->startPos + context->translation * fraction;\n\tcontext->result.fraction = fraction;\n\tcontext->result.blockingHit = true;\n\tcontext->hit = true;\n\n\treturn fraction;\n}\n\nstatic b2ShapeProxy TransformShapeProxy( const b2Transform& t, const b2ShapeProxy& proxy )\n{\n\tb2ShapeProxy ret;\n\tret.count = proxy.count;\n\tret.radius = proxy.radius;\n\n\tfor ( int i = 0; i < proxy.count; ++i )\n\t{\n\t\tret.points[i] = b2TransformPoint( t, proxy.points[i] );\n\t}\n\n\treturn ret;\n}\n\n// This showed a problem with shape casts hitting the back-side of a chain shape\nclass ShapeCastChain : public Sample\n{\npublic:\n\texplicit ShapeCastChain( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\t// World Body & Shape\n\t\tb2BodyDef worldBodyDef = b2DefaultBodyDef();\n\t\tconst b2BodyId groundId = b2CreateBody( m_worldId, &worldBodyDef );\n\n\t\tb2Vec2 points[4] = { { 1.0f, 0.0f }, { -1.0f, 0.0f }, { -1.0f, -1.0f }, { 1.0f, -1.0f } };\n\n\t\tb2ChainDef worldChainDef = b2DefaultChainDef();\n\t\tworldChainDef.userData = nullptr;\n\t\tworldChainDef.points = points;\n\t\tworldChainDef.count = 4;\n\t\tworldChainDef.filter.categoryBits = 0x1;\n\t\tworldChainDef.filter.maskBits = 0x1;\n\t\tworldChainDef.filter.groupIndex = 0;\n\t\tworldChainDef.isLoop = true;\n\t\tworldChainDef.enableSensorEvents = false;\n\t\tb2CreateChain( groundId, &worldChainDef );\n\n\t\t// \"Character\" Body & Shape\n\t\tb2BodyDef characterBodyDef = b2DefaultBodyDef();\n\t\tcharacterBodyDef.position = { 0.0f, 0.103f };\n\t\tcharacterBodyDef.rotation = b2MakeRot( 0.0f );\n\t\tcharacterBodyDef.linearDamping = 0.0f;\n\t\tcharacterBodyDef.angularDamping = 0.0f;\n\t\tcharacterBodyDef.gravityScale = 1.0f;\n\t\tcharacterBodyDef.enableSleep = true;\n\t\tcharacterBodyDef.isAwake = false;\n\t\tcharacterBodyDef.motionLocks.angularZ = true;\n\t\tcharacterBodyDef.isEnabled = true;\n\t\tcharacterBodyDef.userData = nullptr;\n\t\tcharacterBodyDef.type = b2_kinematicBody;\n\n\t\tcharacterBodyId_ = b2CreateBody( m_worldId, &characterBodyDef );\n\n\t\tb2ShapeDef characterShapeDef = b2DefaultShapeDef();\n\n\t\tcharacterShapeDef.userData = nullptr;\n\t\tcharacterShapeDef.filter.categoryBits = 0x1;\n\t\tcharacterShapeDef.filter.maskBits = 0x1;\n\t\tcharacterShapeDef.filter.groupIndex = 0;\n\t\tcharacterShapeDef.isSensor = false;\n\t\tcharacterShapeDef.enableSensorEvents = false;\n\t\tcharacterShapeDef.enableContactEvents = false;\n\t\tcharacterShapeDef.enableHitEvents = false;\n\t\tcharacterShapeDef.enablePreSolveEvents = false;\n\t\tcharacterShapeDef.invokeContactCreation = false;\n\t\tcharacterShapeDef.updateBodyMass = false;\n\n\t\tcharacterBox_ = b2MakeBox( 0.1f, 0.1f );\n\t\tb2CreatePolygonShape( characterBodyId_, &characterShapeDef, &characterBox_ );\n\n\t\tcontext->camera.center = b2Vec2_zero;\n\t}\n\n\tvoid Step() override\n\t{\n\t\tconst float timeStep = m_context->hertz > 0.0f ? 1.0f / m_context->hertz : 0.0f;\n\n\t\tbool noXInput = true;\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_A ) )\n\t\t{\n\t\t\tcharacterVelocity_.x -= timeStep * 5.0f;\n\t\t\tnoXInput = false;\n\t\t}\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_D ) )\n\t\t{\n\t\t\tcharacterVelocity_.x += timeStep * 5.0f;\n\t\t\tnoXInput = false;\n\t\t}\n\n\t\tbool noYInput = true;\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_S ) )\n\t\t{\n\t\t\tcharacterVelocity_.y -= timeStep * 5.0f;\n\t\t\tnoYInput = false;\n\t\t}\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_W ) )\n\t\t{\n\t\t\tcharacterVelocity_.y += timeStep * 5.0f;\n\t\t\tnoYInput = false;\n\t\t}\n\n\t\tif ( noXInput )\n\t\t{\n\t\t\tif ( b2AbsFloat( characterVelocity_.x ) > 0.01f )\n\t\t\t{\n\t\t\t\tconst float decel = characterVelocity_.x > 0.0f ? 5.0f : -5.0f;\n\t\t\t\tif ( b2AbsFloat( decel ) < characterVelocity_.x )\n\t\t\t\t{\n\t\t\t\t\tcharacterVelocity_.x -= decel;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcharacterVelocity_.x = 0.0f;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcharacterVelocity_.x = 0.0f;\n\t\t\t}\n\t\t}\n\n\t\tif ( noYInput )\n\t\t{\n\t\t\tif ( b2AbsFloat( characterVelocity_.y ) > 0.01f )\n\t\t\t{\n\t\t\t\tconst float decel = characterVelocity_.y > 0.0f ? 5.0f : -5.0f;\n\t\t\t\tif ( b2AbsFloat( decel ) < characterVelocity_.y )\n\t\t\t\t{\n\t\t\t\t\tcharacterVelocity_.y -= decel;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcharacterVelocity_.y = 0.0f;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcharacterVelocity_.y = 0.0f;\n\t\t\t}\n\t\t}\n\n\t\tb2Vec2 characterPos = b2Body_GetPosition( characterBodyId_ );\n\t\tb2Vec2 newCharacterPos = characterPos;\n\t\tnewCharacterPos.x += characterVelocity_.x * timeStep;\n\t\tnewCharacterPos.y += characterVelocity_.y * timeStep;\n\t\tb2Body_SetTransform( characterBodyId_, newCharacterPos, b2Rot_identity );\n\n\t\tPhysicsHitQueryResult2D hitResult;\n\t\tconst b2ShapeProxy shapeProxy = b2MakeProxy( characterBox_.vertices, characterBox_.count, characterBox_.radius );\n\t\tif ( ShapeCastSingle( hitResult, characterPos, newCharacterPos, 0.0f, shapeProxy ) )\n\t\t{\n\t\t\thitPos = hitResult.point;\n\t\t\thitNormal = hitResult.normal;\n\t\t}\n\n\t\tDrawLine( m_draw, hitPos, { hitPos.x + hitNormal.x, hitPos.y + hitNormal.y }, b2_colorRed );\n\n\t\tSample::Step();\n\t}\n\n\tbool ShapeCastSingle( PhysicsHitQueryResult2D& outResult, b2Vec2 start, b2Vec2 end, float rotation,\n\t\t\t\t\t\t  const b2ShapeProxy& shape ) const\n\t{\n\t\tconst b2Transform transform = { start, b2MakeRot( rotation ) };\n\t\tconst b2ShapeProxy transformedShape = TransformShapeProxy( transform, shape );\n\n\t\tconst b2Vec2 translation = { end.x - start.x, end.y - start.y };\n\t\tconst b2QueryFilter filter = { 0x1, 0x1 };\n\t\tCastContext_Single context;\n\t\tcontext.startPos = start;\n\t\tcontext.translation = { translation.x, translation.y };\n\t\tb2World_CastShape( m_worldId, &transformedShape, translation, filter, &b2CastResult_Closest, &context );\n\n\t\tif ( context.hit )\n\t\t{\n\t\t\toutResult = context.result;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ShapeCastChain( context );\n\t}\n\nprivate:\n\tb2BodyId characterBodyId_ = b2_nullBodyId;\n\tb2Polygon characterBox_;\n\tb2Vec2 characterVelocity_ = b2Vec2_zero;\n\tb2Vec2 hitPos = b2Vec2_zero;\n\tb2Vec2 hitNormal = b2Vec2_zero;\n};\n\nstatic int sampleShapeCastChain = RegisterSample( \"Issues\", \"Shape Cast Chain\", ShapeCastChain::Create );\n\nclass BadSteiner : public Sample\n{\npublic:\n\texplicit BadSteiner( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 1.75f };\n\t\t\tm_context->camera.zoom = 2.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -100.0f, 0.0f }, { 100.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -48.0f, 62.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2Vec2 points[3] = {\n\t\t\t\t{ 48.7599983f, -60.5699997f },\n\t\t\t\t{ 48.7400017f, -60.5400009f },\n\t\t\t\t{ 48.6800003f, -60.5600014f },\n\t\t\t};\n\n\t\t\tb2Hull hull = b2ComputeHull( points, 3 );\n\t\t\tb2Polygon poly = b2MakePolygon( &hull, 0.0f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &poly );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BadSteiner( context );\n\t}\n};\n\nstatic int sampleBadSteiner = RegisterSample( \"Issues\", \"Bad Steiner\", BadSteiner::Create );\n\nclass DisableCrash : public Sample\n{\npublic:\n\texplicit DisableCrash( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.8f, 6.4f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t}\n\n\t\tm_isEnabled = true;\n\n\t\t// Define attachment\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -2.0f, 3.0f };\n\t\t\tbodyDef.isEnabled = m_isEnabled;\n\t\t\tm_attachmentId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 2.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_attachmentId, &shapeDef, &box );\n\t\t}\n\n\t\t// Define platform\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { -4.0f, 5.0f };\n\t\t\tm_platformId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f, 4.0f, { 4.0f, 0.0f }, b2MakeRot( 0.5f * B2_PI ) );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_platformId, &shapeDef, &box );\n\n\t\t\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\t\t\tb2Vec2 pivot = { -2.0f, 5.0f };\n\t\t\trevoluteDef.base.bodyIdA = m_attachmentId;\n\t\t\trevoluteDef.base.bodyIdB = m_platformId;\n\t\t\trevoluteDef.base.localFrameA.p = b2Body_GetLocalPoint( m_attachmentId, pivot );\n\t\t\trevoluteDef.base.localFrameB.p = b2Body_GetLocalPoint( m_platformId, pivot );\n\t\t\trevoluteDef.maxMotorTorque = 50.0f;\n\t\t\trevoluteDef.enableMotor = true;\n\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 11.0f * fontSize;\n\t\tfloat winX = 0.5f * fontSize;\n\t\tfloat winY = m_camera->height - height - 2.0f * fontSize;\n\t\tImGui::SetNextWindowPos( { winX, winY }, ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 9.0f * fontSize, height ) );\n\t\tImGui::Begin( \"Disable Crash\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Checkbox( \"Enable\", &m_isEnabled ) )\n\t\t{\n\t\t\tif ( m_isEnabled )\n\t\t\t{\n\t\t\t\tb2Body_Enable( m_attachmentId );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2Body_Disable( m_attachmentId );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new DisableCrash( context );\n\t}\n\n\tb2BodyId m_attachmentId;\n\tb2BodyId m_platformId;\n\tbool m_isEnabled;\n};\n\nstatic int sampleDisableCrash = RegisterSample( \"Issues\", \"Disable\", DisableCrash::Create );\n\nclass Crash01 : public Sample\n{\npublic:\n\texplicit Crash01( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.8f, 6.4f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t}\n\n\t\tm_type = b2_dynamicBody;\n\t\tm_isEnabled = true;\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.name = \"ground\";\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Define attachment\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -2.0f, 3.0f };\n\t\t\tbodyDef.name = \"attach1\";\n\t\t\tm_attachmentId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 2.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 1.0f;\n\t\t\tb2CreatePolygonShape( m_attachmentId, &shapeDef, &box );\n\t\t}\n\n\t\t// Define platform\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = m_type;\n\t\t\tbodyDef.isEnabled = m_isEnabled;\n\t\t\tbodyDef.position = { -4.0f, 5.0f };\n\t\t\tbodyDef.name = \"platform\";\n\t\t\tm_platformId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f, 4.0f, { 4.0f, 0.0f }, b2MakeRot( 0.5f * B2_PI ) );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 2.0f;\n\t\t\tb2CreatePolygonShape( m_platformId, &shapeDef, &box );\n\n\t\t\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\t\t\tb2Vec2 pivot = { -2.0f, 5.0f };\n\t\t\trevoluteDef.base.bodyIdA = m_attachmentId;\n\t\t\trevoluteDef.base.bodyIdB = m_platformId;\n\t\t\trevoluteDef.base.localFrameA.p = b2Body_GetLocalPoint( m_attachmentId, pivot );\n\t\t\trevoluteDef.base.localFrameB.p = b2Body_GetLocalPoint( m_platformId, pivot );\n\t\t\trevoluteDef.maxMotorTorque = 50.0f;\n\t\t\trevoluteDef.enableMotor = true;\n\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\n\t\t\tb2PrismaticJointDef prismaticDef = b2DefaultPrismaticJointDef();\n\t\t\tb2Vec2 anchor = { 0.0f, 5.0f };\n\t\t\tprismaticDef.base.bodyIdA = groundId;\n\t\t\tprismaticDef.base.bodyIdB = m_platformId;\n\t\t\tprismaticDef.base.localFrameA.p = b2Body_GetLocalPoint( groundId, anchor );\n\t\t\tprismaticDef.base.localFrameB.p = b2Body_GetLocalPoint( m_platformId, anchor );\n\t\t\tprismaticDef.maxMotorForce = 1000.0f;\n\t\t\tprismaticDef.motorSpeed = 0.0f;\n\t\t\tprismaticDef.enableMotor = true;\n\t\t\tprismaticDef.lowerTranslation = -10.0f;\n\t\t\tprismaticDef.upperTranslation = 10.0f;\n\t\t\tprismaticDef.enableLimit = true;\n\n\t\t\tb2CreatePrismaticJoint( m_worldId, &prismaticDef );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 11.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 9.0f * fontSize, height ) );\n\t\tImGui::Begin( \"Crash 01\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::RadioButton( \"Static\", m_type == b2_staticBody ) )\n\t\t{\n\t\t\tm_type = b2_staticBody;\n\t\t\tb2Body_SetType( m_platformId, b2_staticBody );\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Kinematic\", m_type == b2_kinematicBody ) )\n\t\t{\n\t\t\tm_type = b2_kinematicBody;\n\t\t\tb2Body_SetType( m_platformId, b2_kinematicBody );\n\t\t\tb2Body_SetLinearVelocity( m_platformId, { -0.1f, 0.0f } );\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Dynamic\", m_type == b2_dynamicBody ) )\n\t\t{\n\t\t\tm_type = b2_dynamicBody;\n\t\t\tb2Body_SetType( m_platformId, b2_dynamicBody );\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Enable\", &m_isEnabled ) )\n\t\t{\n\t\t\tif ( m_isEnabled )\n\t\t\t{\n\t\t\t\tb2Body_Enable( m_attachmentId );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2Body_Disable( m_attachmentId );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Crash01( context );\n\t}\n\n\tb2BodyId m_attachmentId;\n\tb2BodyId m_platformId;\n\tb2BodyType m_type;\n\tbool m_isEnabled;\n};\n\nstatic int sampleBodyType = RegisterSample( \"Issues\", \"Crash01\", Crash01::Create );\n\nclass StaticVsBulletBug : public Sample\n{\npublic:\n\texplicit StaticVsBulletBug( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 48.8525391, 68.1518555 };\n\t\t\tm_context->camera.zoom = 100.0f * 0.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bd = b2DefaultBodyDef();\n\t\t\tbd.type = b2_dynamicBody; // NOTE(bug): Changing this to b2_staticBody fixes the issue\n\t\t\tb2BodyId staticBodyId = b2CreateBody( m_worldId, &bd );\n\n\t\t\tconst b2Vec2 verts[] = {\n\t\t\t\t{ 48.8525391, 68.1518555 }, { 49.1821289, 68.1152344 }, { 68.8476562, 68.1152344 },\n\t\t\t\t{ 68.8476562, 70.2392578 }, { 48.8525391, 70.2392578 },\n\t\t\t};\n\n\t\t\tconst b2Hull hull = b2ComputeHull( verts, ARRAY_COUNT( verts ) );\n\t\t\tconst b2Polygon poly = b2MakePolygon( &hull, 0.0f );\n\n\t\t\tb2ShapeDef sd = b2DefaultShapeDef();\n\t\t\tsd.density = 1.0f;\n\t\t\tsd.material.friction = 0.5f;\n\t\t\tsd.material.restitution = 0.1f;\n\n\t\t\tb2CreatePolygonShape( staticBodyId, &sd, &poly );\n\t\t\tb2Body_SetType( staticBodyId, b2_staticBody );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bd = b2DefaultBodyDef();\n\t\t\tbd.position = { 58.9243050, 77.5401459 };\n\t\t\tbd.type = b2_dynamicBody;\n\t\t\tbd.motionLocks.angularZ = true;\n\t\t\tbd.linearVelocity = { 104.868881, -281.073883 };\n\t\t\tbd.isBullet = true;\n\n\t\t\tb2BodyId ballBodyId = b2CreateBody( m_worldId, &bd );\n\t\t\tconst b2Circle ball = { .center = {}, .radius = 0.3f };\n\n\t\t\tb2ShapeDef ballShape = b2DefaultShapeDef();\n\t\t\tballShape.density = 3.0f;\n\t\t\tballShape.material.friction = 0.2f;\n\t\t\tballShape.material.restitution = 0.9f;\n\n\t\t\tb2CreateCircleShape( ballBodyId, &ballShape, &ball );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new StaticVsBulletBug( context );\n\t}\n};\n\nstatic int staticVsBulletBug = RegisterSample( \"Issues\", \"StaticVsBulletBug\", StaticVsBulletBug::Create );\n\n// This simulations stresses the solver by putting a light mass between two bodies on a prismatic joint with a stiff spring.\n// This can be made stable by increasing the size of the middle circle and/or increasing the number of sub-steps.\nclass UnstablePrismaticJoints : public Sample\n{\npublic:\n\texplicit UnstablePrismaticJoints( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 1.75f };\n\t\t\tm_context->camera.zoom = 32.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -100.0f, 0.0f }, { 100.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2BodyId centerId;\n\t\t{\n\t\t\tb2BodyDef bd = b2DefaultBodyDef();\n\t\t\tbd.type = b2_dynamicBody;\n\t\t\tbd.position = { 0, 3 };\n\t\t\tcenterId = b2CreateBody( m_worldId, &bd );\n\n\t\t\tb2ShapeDef sd = b2DefaultShapeDef();\n\n\t\t\tb2Circle circle;\n\t\t\tcircle.center = { 0, 0 };\n\n\t\t\t// Note: this will crash due to divergence (inf/nan) with a radius of 0.1\n\t\t\t// circle.radius = 0.1f;\n\t\t\tcircle.radius = 0.5f;\n\n\t\t\tb2CreateCircleShape( centerId, &sd, &circle );\n\t\t}\n\n\t\tb2PrismaticJointDef jd = b2DefaultPrismaticJointDef();\n\t\tjd.enableSpring = true;\n\t\tjd.hertz = 10.0f;\n\t\tjd.dampingRatio = 2.0f;\n\n\t\t{\n\t\t\tb2BodyDef bd = b2DefaultBodyDef();\n\t\t\tbd.type = b2_dynamicBody;\n\t\t\tbd.position = { -3.5, 3 };\n\n\t\t\tb2BodyId leftId = b2CreateBody( m_worldId, &bd );\n\n\t\t\tb2ShapeDef sd = b2DefaultShapeDef();\n\n\t\t\tb2Circle circle;\n\t\t\tcircle.center = { 0, 0 };\n\t\t\tcircle.radius = 2.0f;\n\t\t\tb2CreateCircleShape( leftId, &sd, &circle );\n\n\t\t\tjd.base.bodyIdA = centerId;\n\t\t\tjd.base.bodyIdB = leftId;\n\t\t\tjd.targetTranslation = -3.0f;\n\t\t\tb2CreatePrismaticJoint( m_worldId, &jd );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bd = b2DefaultBodyDef();\n\t\t\tbd.type = b2_dynamicBody;\n\t\t\tbd.position = { 3.5, 3 };\n\t\t\tb2BodyId rightId = b2CreateBody( m_worldId, &bd );\n\n\t\t\tb2ShapeDef sd = b2DefaultShapeDef();\n\n\t\t\tb2Circle circle;\n\t\t\tcircle.center = { 0, 0 };\n\t\t\tcircle.radius = 2.0f;\n\n\t\t\tb2CreateCircleShape( rightId, &sd, &circle );\n\n\t\t\tjd.base.bodyIdA = centerId;\n\t\t\tjd.base.bodyIdB = rightId;\n\t\t\tjd.targetTranslation = 3.0f;\n\t\t\tb2CreatePrismaticJoint( m_worldId, &jd );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new UnstablePrismaticJoints( context );\n\t}\n};\n\nstatic int sampleUnstablePrismaticJoints =\n\tRegisterSample( \"Issues\", \"Unstable Prismatic Joints\", UnstablePrismaticJoints::Create );\n\nclass UnstableWindmill : public Sample\n{\npublic:\n\texplicit UnstableWindmill( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 1.75f };\n\t\t\tm_context->camera.zoom = 32.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -100.0f, -10.0f }, { 100.0f, -10.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2BodyDef bdef = b2DefaultBodyDef();\n\t\tbdef.gravityScale = 0.0f;\n\t\tbdef.type = b2_dynamicBody;\n\t\tb2ShapeDef sdef = b2DefaultShapeDef();\n\t\tsdef.material = b2DefaultSurfaceMaterial();\n\t\tsdef.material.friction = 0.1f;\n\n\t\t// center\n\t\tbdef.position = { 10, 10 };\n\t\tb2BodyId center = b2CreateBody( m_worldId, &bdef );\n\t\tb2Circle circle = { .center = { 0, 0 }, .radius = 5 };\n\t\tb2CreateCircleShape( center, &sdef, &circle );\n\n\t\t// rotors\n\t\tb2WeldJointDef wjdef = b2DefaultWeldJointDef();\n\n\t\t// This simulation can be stabilized by using a lower constraint stiffness\n\t\twjdef.base.constraintHertz = 30.0f;\n\t\twjdef.base.bodyIdA = center;\n\n\t\tb2Polygon polygon;\n\n\t\tbdef.position = { 10, 0 };\n\t\tb2BodyId body = b2CreateBody( m_worldId, &bdef );\n\t\tb2CreatePolygonShape( body, &sdef, &( polygon = b2MakeBox( 4, 5 ) ) );\n\t\twjdef.base.localFrameA = { .p = { 0, -5 }, .q = b2Rot_identity };\n\t\twjdef.base.bodyIdB = body;\n\t\twjdef.base.localFrameB = { .p = { 0, 5 }, .q = b2Rot_identity };\n\t\tb2CreateWeldJoint( m_worldId, &wjdef );\n\n\t\tbdef.position = { 20, 10 };\n\t\tbody = b2CreateBody( m_worldId, &bdef );\n\t\tb2CreatePolygonShape( body, &sdef, &( polygon = b2MakeBox( 5, 4 ) ) );\n\t\twjdef.base.localFrameA = { .p = { 5, 0 }, .q = b2Rot_identity };\n\t\twjdef.base.bodyIdB = body;\n\t\twjdef.base.localFrameB = { .p = { -5, 0 }, .q = b2Rot_identity };\n\t\tb2CreateWeldJoint( m_worldId, &wjdef );\n\n\t\tbdef.position = { 10, 20 };\n\t\tbody = b2CreateBody( m_worldId, &bdef );\n\t\tb2CreatePolygonShape( body, &sdef, &( polygon = b2MakeBox( 4, 5 ) ) );\n\t\twjdef.base.localFrameA = { .p = { 0, 5 }, .q = b2Rot_identity };\n\t\twjdef.base.bodyIdB = body;\n\t\twjdef.base.localFrameB = { .p = { 0, -5 }, .q = b2Rot_identity };\n\t\tb2CreateWeldJoint( m_worldId, &wjdef );\n\n\t\tbdef.position = { 0, 10 };\n\t\tbody = b2CreateBody( m_worldId, &bdef );\n\t\tb2CreatePolygonShape( body, &sdef, &( polygon = b2MakeBox( 5, 4 ) ) );\n\t\twjdef.base.localFrameA = { .p = { -5, 0 }, .q = b2Rot_identity };\n\t\twjdef.base.bodyIdB = body;\n\t\twjdef.base.localFrameB = { .p = { 5, 0 }, .q = b2Rot_identity };\n\t\tb2CreateWeldJoint( m_worldId, &wjdef );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new UnstableWindmill( context );\n\t}\n};\n\nstatic int sampleUnstableWindmill = RegisterSample( \"Issues\", \"Unstable Windmill\", UnstableWindmill::Create );\n"
  },
  {
    "path": "samples/sample_joints.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"car.h\"\n#include \"donut.h\"\n#include \"doohickey.h\"\n#include \"draw.h\"\n#include \"human.h\"\n#include \"random.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n#include <imgui.h>\n\n// Test the distance joint and all options\nclass DistanceJoint : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_maxCount = 10\n\t};\n\n\texplicit DistanceJoint( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 12.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.35f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tm_groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\tm_count = 0;\n\t\tm_hertz = 5.0f;\n\t\tm_dampingRatio = 0.5f;\n\t\tm_length = 1.0f;\n\t\tm_minLength = m_length;\n\t\tm_maxLength = m_length;\n\t\tm_tensionForce = 2000.0f;\n\t\tm_compressionForce = 100.0f;\n\t\tm_enableSpring = false;\n\t\tm_enableLimit = false;\n\n\t\tfor ( int i = 0; i < e_maxCount; ++i )\n\t\t{\n\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t\tm_jointIds[i] = b2_nullJointId;\n\t\t}\n\n\t\tCreateScene( 1 );\n\t}\n\n\tvoid CreateScene( int newCount )\n\t{\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tb2DestroyJoint( m_jointIds[i], false );\n\t\t\tm_jointIds[i] = b2_nullJointId;\n\t\t}\n\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t}\n\n\t\tm_count = newCount;\n\n\t\tfloat radius = 0.25f;\n\t\tb2Circle circle = { { 0.0f, 0.0f }, radius };\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 20.0f;\n\n\t\tfloat yOffset = 20.0f;\n\n\t\tb2DistanceJointDef jointDef = b2DefaultDistanceJointDef();\n\t\tjointDef.hertz = m_hertz;\n\t\tjointDef.dampingRatio = m_dampingRatio;\n\t\tjointDef.length = m_length;\n\t\tjointDef.lowerSpringForce = -m_tensionForce;\n\t\tjointDef.upperSpringForce = m_compressionForce;\n\t\tjointDef.minLength = m_minLength;\n\t\tjointDef.maxLength = m_maxLength;\n\t\tjointDef.enableSpring = m_enableSpring;\n\t\tjointDef.enableLimit = m_enableLimit;\n\n\t\tb2BodyId prevBodyId = m_groundId;\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.angularDamping = 1.0f;\n\t\t\tbodyDef.position = { m_length * ( i + 1.0f ), yOffset };\n\t\t\tm_bodyIds[i] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCircleShape( m_bodyIds[i], &shapeDef, &circle );\n\n\t\t\tb2Vec2 pivotA = { m_length * i, yOffset };\n\t\t\tb2Vec2 pivotB = { m_length * ( i + 1.0f ), yOffset };\n\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[i];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivotA );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivotB );\n\t\t\tm_jointIds[i] = b2CreateDistanceJoint( m_worldId, &jointDef );\n\n\t\t\tprevBodyId = m_bodyIds[i];\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 20.0f * fontSize;\n\t\tImGui::SetNextWindowPos( { 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize }, ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( { 18.0f * fontSize, height } );\n\n\t\tImGui::Begin( \"Distance Joint\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 10.0f * fontSize );\n\n\t\tif ( ImGui::SliderFloat( \"Length\", &m_length, 0.1f, 4.0f, \"%3.1f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t{\n\t\t\t\tb2DistanceJoint_SetLength( m_jointIds[i], m_length );\n\t\t\t\tb2Joint_WakeBodies( m_jointIds[i] );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Spring\", &m_enableSpring ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t{\n\t\t\t\tb2DistanceJoint_EnableSpring( m_jointIds[i], m_enableSpring );\n\t\t\t\tb2Joint_WakeBodies( m_jointIds[i] );\n\t\t\t}\n\t\t}\n\n\t\tif ( m_enableSpring )\n\t\t{\n\t\t\tif ( ImGui::SliderFloat( \"Tension\", &m_tensionForce, 0.0f, 4000.0f ) )\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2DistanceJoint_SetSpringForceRange( m_jointIds[i], -m_tensionForce, m_compressionForce );\n\t\t\t\t\tb2Joint_WakeBodies( m_jointIds[i] );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Compression\", &m_compressionForce, 0.0f, 200.0f ) )\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2DistanceJoint_SetSpringForceRange( m_jointIds[i], -m_tensionForce, m_compressionForce );\n\t\t\t\t\tb2Joint_WakeBodies( m_jointIds[i] );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Hertz\", &m_hertz, 0.0f, 15.0f, \"%3.1f\" ) )\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2DistanceJoint_SetSpringHertz( m_jointIds[i], m_hertz );\n\t\t\t\t\tb2Joint_WakeBodies( m_jointIds[i] );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Damping\", &m_dampingRatio, 0.0f, 4.0f, \"%3.1f\" ) )\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2DistanceJoint_SetSpringDampingRatio( m_jointIds[i], m_dampingRatio );\n\t\t\t\t\tb2Joint_WakeBodies( m_jointIds[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Limit\", &m_enableLimit ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t{\n\t\t\t\tb2DistanceJoint_EnableLimit( m_jointIds[i], m_enableLimit );\n\t\t\t\tb2Joint_WakeBodies( m_jointIds[i] );\n\t\t\t}\n\t\t}\n\n\t\tif ( m_enableLimit )\n\t\t{\n\t\t\tif ( ImGui::SliderFloat( \"Min Length\", &m_minLength, 0.1f, 4.0f, \"%3.1f\" ) )\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2DistanceJoint_SetLengthRange( m_jointIds[i], m_minLength, m_maxLength );\n\t\t\t\t\tb2Joint_WakeBodies( m_jointIds[i] );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Max Length\", &m_maxLength, 0.1f, 4.0f, \"%3.1f\" ) )\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2DistanceJoint_SetLengthRange( m_jointIds[i], m_minLength, m_maxLength );\n\t\t\t\t\tb2Joint_WakeBodies( m_jointIds[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tint count = m_count;\n\t\tif ( ImGui::SliderInt( \"Count\", &count, 1, e_maxCount ) )\n\t\t{\n\t\t\tCreateScene( count );\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new DistanceJoint( context );\n\t}\n\n\tb2BodyId m_groundId;\n\tb2BodyId m_bodyIds[e_maxCount];\n\tb2JointId m_jointIds[e_maxCount];\n\tint m_count;\n\tfloat m_hertz;\n\tfloat m_dampingRatio;\n\tfloat m_length;\n\tfloat m_tensionForce;\n\tfloat m_compressionForce;\n\tfloat m_minLength;\n\tfloat m_maxLength;\n\tbool m_enableSpring;\n\tbool m_enableLimit;\n};\n\nstatic int sampleDistanceJoint = RegisterSample( \"Joints\", \"Distance Joint\", DistanceJoint::Create );\n\n/// This test shows how to use a motor joint. A motor joint\n/// can be used to animate a dynamic body. With finite motor forces\n/// the body can be blocked by collision with other bodies.\nclass MotorJoint : public Sample\n{\npublic:\n\texplicit MotorJoint( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 7.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t}\n\n\t\tb2BodyId groundId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tm_transform = { .p = { 0.0f, 8.0f }, .q = b2Rot_identity };\n\n\t\t// Define a target body\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.position = m_transform.p;\n\t\t\tm_targetId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\t// Define motorized body\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = m_transform.p;\n\t\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 2.0f, 0.5f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\n\t\t\tm_maxForce = 5000.0f;\n\t\t\tm_maxTorque = 500.0f;\n\n\t\t\tb2MotorJointDef jointDef = b2DefaultMotorJointDef();\n\t\t\tjointDef.base.bodyIdA = m_targetId;\n\t\t\tjointDef.base.bodyIdB = m_bodyId;\n\t\t\tjointDef.linearHertz = 4.0f;\n\t\t\tjointDef.linearDampingRatio = 0.7f;\n\t\t\tjointDef.angularHertz = 4.0f;\n\t\t\tjointDef.angularDampingRatio = 0.7f;\n\t\t\tjointDef.maxSpringForce = m_maxForce;\n\t\t\tjointDef.maxSpringTorque = m_maxTorque;\n\n\t\t\tm_jointId = b2CreateMotorJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\t// Define spring body\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -2.0f, 2.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeSquare( 0.5f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2MotorJointDef jointDef = b2DefaultMotorJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Add( bodyDef.position, { 0.25f, 0.25f } );\n\t\t\tjointDef.base.localFrameB.p = { 0.25f, 0.25f };\n\t\t\tjointDef.linearHertz = 7.5f;\n\t\t\tjointDef.linearDampingRatio = 0.7f;\n\t\t\tjointDef.angularHertz = 7.5f;\n\t\t\tjointDef.angularDampingRatio = 0.7f;\n\t\t\tjointDef.maxSpringForce = 500.0f;\n\t\t\tjointDef.maxSpringTorque = 10.0f;\n\n\t\t\tb2CreateMotorJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tm_speed = 1.0f;\n\t\tm_time = 0.0f;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 180.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Motor Joint\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::SliderFloat( \"Speed\", &m_speed, -5.0f, 5.0f, \"%.0f\" ) )\n\t\t{\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Max Force\", &m_maxForce, 0.0f, 10000.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tb2MotorJoint_SetMaxSpringForce( m_jointId, m_maxForce );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Max Torque\", &m_maxTorque, 0.0f, 10000.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tb2MotorJoint_SetMaxSpringTorque( m_jointId, m_maxTorque );\n\t\t}\n\n\t\tif ( ImGui::Button( \"Apply Impulse\" ) )\n\t\t{\n\t\t\tb2Body_ApplyLinearImpulseToCenter( m_bodyId, { 100.0f, 0.0f }, true );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tfloat timeStep = m_context->hertz > 0.0f ? 1.0f / m_context->hertz : 0.0f;\n\n\t\tif ( m_context->pause )\n\t\t{\n\t\t\tif ( m_context->singleStep == false )\n\t\t\t{\n\t\t\t\ttimeStep = 0.0f;\n\t\t\t}\n\t\t}\n\n\t\tif ( timeStep > 0.0f )\n\t\t{\n\t\t\tm_time += m_speed * timeStep;\n\n\t\t\tb2Vec2 linearOffset;\n\t\t\tlinearOffset.x = 6.0f * sinf( 2.0f * m_time );\n\t\t\tlinearOffset.y = 8.0f + 4.0f * sinf( 1.0f * m_time );\n\n\t\t\tfloat angularOffset = 2.0f * m_time;\n\t\t\tm_transform = { linearOffset, b2MakeRot( angularOffset ) };\n\n\t\t\tbool wake = true;\n\t\t\tb2Body_SetTargetTransform( m_targetId, m_transform, timeStep, wake );\n\t\t}\n\n\t\tDrawTransform( m_draw, m_transform, 1.0f );\n\n\t\tSample::Step();\n\n\t\tb2Vec2 force = b2Joint_GetConstraintForce( m_jointId );\n\t\tfloat torque = b2Joint_GetConstraintTorque( m_jointId );\n\n\t\tDrawTextLine( \"force = {%3.f, %3.f}, torque = %3.f\", force.x, force.y, torque );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new MotorJoint( context );\n\t}\n\n\tb2BodyId m_targetId;\n\tb2BodyId m_bodyId;\n\tb2JointId m_jointId;\n\tb2Transform m_transform;\n\tfloat m_time;\n\tfloat m_speed;\n\tfloat m_maxForce;\n\tfloat m_maxTorque;\n};\n\nstatic int sampleMotorJoint = RegisterSample( \"Joints\", \"Motor Joint\", MotorJoint::Create );\n\nclass TopDownFriction : public Sample\n{\npublic:\n\texplicit TopDownFriction( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 7.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t}\n\n\t\tb2BodyId groundId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tsegment = { { -10.0f, 0.0f }, { -10.0f, 20.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tsegment = { { 10.0f, 0.0f }, { 10.0f, 20.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tsegment = { { -10.0f, 20.0f }, { 10.0f, 20.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2MotorJointDef jointDef = b2DefaultMotorJointDef();\n\t\tjointDef.base.bodyIdA = groundId;\n\t\tjointDef.base.collideConnected = true;\n\t\tjointDef.maxVelocityForce = 10.0f;\n\t\tjointDef.maxVelocityTorque = 10.0f;\n\n\t\tb2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f };\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.35f };\n\t\tb2Polygon square = b2MakeSquare( 0.35f );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.gravityScale = 0.0f;\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.restitution = 0.8f;\n\n\t\tint n = 10;\n\t\tfloat x = -5.0f, y = 15.0f;\n\t\tfor ( int i = 0; i < n; ++i )\n\t\t{\n\t\t\tfor ( int j = 0; j < n; ++j )\n\t\t\t{\n\t\t\t\tbodyDef.position = { x, y };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\tint remainder = ( n * i + j ) % 4;\n\t\t\t\tif ( remainder == 0 )\n\t\t\t\t{\n\t\t\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\t\t\t\t}\n\t\t\t\telse if ( remainder == 1 )\n\t\t\t\t{\n\t\t\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t\t\t}\n\t\t\t\telse if ( remainder == 2 )\n\t\t\t\t{\n\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &square );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tb2Polygon poly = RandomPolygon( 0.75f );\n\t\t\t\t\tpoly.radius = 0.1f;\n\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &poly );\n\t\t\t\t}\n\n\t\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\t\tb2CreateMotorJoint( m_worldId, &jointDef );\n\n\t\t\t\tx += 1.0f;\n\t\t\t}\n\n\t\t\tx = -5.0f;\n\t\t\ty -= 1.0f;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 180.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Top Down Friction\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Button( \"Explode\" ) )\n\t\t{\n\t\t\tb2ExplosionDef def = b2DefaultExplosionDef();\n\t\t\tdef.position = { 0.0f, 10.0f };\n\t\t\tdef.radius = 10.0f;\n\t\t\tdef.falloff = 5.0f;\n\t\t\tdef.impulsePerLength = 10.0f;\n\t\t\tb2World_Explode( m_worldId, &def );\n\n\t\t\tDrawCircle( m_draw, def.position, 10.0f, b2_colorWhite );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new TopDownFriction( context );\n\t}\n};\n\nstatic int sampleTopDownFriction = RegisterSample( \"Joints\", \"Top Down Friction\", TopDownFriction::Create );\n\n// This sample shows how to use a filter joint to prevent collision between two bodies.\n// This is more specific than shape filters. It also shows that sleeping is coupled by the filter joint.\nclass FilterJoint : public Sample\n{\npublic:\n\texplicit FilterJoint( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 7.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyId groundId;\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -4.0f, 2.0f };\n\t\t\tb2BodyId bodyId1 = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeSquare( 2.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( bodyId1, &shapeDef, &box );\n\n\t\t\tbodyDef.position = { 4.0f, 2.0f };\n\t\t\tb2BodyId bodyId2 = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId2, &shapeDef, &box );\n\n\t\t\tb2FilterJointDef jointDef = b2DefaultFilterJointDef();\n\t\t\tjointDef.base.bodyIdA = bodyId1;\n\t\t\tjointDef.base.bodyIdB = bodyId2;\n\n\t\t\tb2CreateFilterJoint( m_worldId, &jointDef );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new FilterJoint( context );\n\t}\n};\n\nstatic int sampleFilterJoint = RegisterSample( \"Joints\", \"Filter Joint\", FilterJoint::Create );\n\nclass RevoluteJoint : public Sample\n{\npublic:\n\texplicit RevoluteJoint( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 15.5f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.7f;\n\t\t}\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, -1.0f };\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 40.0f, 1.0f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tm_enableSpring = false;\n\t\tm_enableLimit = false;\n\t\tm_enableMotor = false;\n\t\tm_hertz = 2.0f;\n\t\tm_dampingRatio = 0.5f;\n\t\tm_targetDegrees = 45.0f;\n\t\tm_motorSpeed = 1.0f;\n\t\tm_motorTorque = 1000.0f;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -10.0f, 20.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 1.0f;\n\n\t\t\tb2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 6.0f }, 0.5f };\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\n\t\t\tb2Vec2 pivot = { -10.0f, 20.5f };\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.q = b2MakeRot( 0.5f * B2_PI );\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.targetAngle = B2_PI * m_targetDegrees / 180.0f;\n\t\t\tjointDef.enableSpring = m_enableSpring;\n\t\t\tjointDef.hertz = m_hertz;\n\t\t\tjointDef.dampingRatio = m_dampingRatio;\n\t\t\tjointDef.motorSpeed = m_motorSpeed;\n\t\t\tjointDef.maxMotorTorque = m_motorTorque;\n\t\t\tjointDef.enableMotor = m_enableMotor;\n\t\t\tjointDef.lowerAngle = -0.5f * B2_PI;\n\t\t\tjointDef.upperAngle = 0.05f * B2_PI;\n\t\t\tjointDef.enableLimit = m_enableLimit;\n\n\t\t\tm_jointId1 = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\tb2Joint_SetConstraintTuning( m_jointId1, 60.0f, 20.0f );\n\t\t}\n\n\t\t{\n\t\t\tb2Circle circle = {};\n\t\t\tcircle.radius = 2.0f;\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 5.0f, 30.0f };\n\t\t\tm_ball = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 1.0f;\n\n\t\t\tb2CreateCircleShape( m_ball, &shapeDef, &circle );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 20.0f, 10.0f };\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2BodyId body = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 10.0f, 0.5f, { -10.0f, 0.0f }, b2Rot_identity );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 1.0f;\n\t\t\tb2CreatePolygonShape( body, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { 19.0f, 10.0f };\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = body;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.lowerAngle = -0.25f * B2_PI;\n\t\t\tjointDef.upperAngle = 0.0f * B2_PI;\n\t\t\tjointDef.enableLimit = true;\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.motorSpeed = 0.0f;\n\t\t\tjointDef.maxMotorTorque = m_motorTorque;\n\n\t\t\tm_jointId2 = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 8.0f * fontSize;\n\t\tImGui::SetNextWindowPos( { 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize }, ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( { 8.0f * fontSize, height } );\n\n\t\tImGui::Begin( \"Revolute Joint\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Checkbox( \"Limit\", &m_enableLimit ) )\n\t\t{\n\t\t\tb2RevoluteJoint_EnableLimit( m_jointId1, m_enableLimit );\n\t\t\tb2Joint_WakeBodies( m_jointId1 );\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Motor\", &m_enableMotor ) )\n\t\t{\n\t\t\tb2RevoluteJoint_EnableMotor( m_jointId1, m_enableMotor );\n\t\t\tb2Joint_WakeBodies( m_jointId1 );\n\t\t}\n\n\t\tif ( m_enableMotor )\n\t\t{\n\t\t\tif ( ImGui::SliderFloat( \"Max Torque\", &m_motorTorque, 0.0f, 5000.0f, \"%.0f\" ) )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetMaxMotorTorque( m_jointId1, m_motorTorque );\n\t\t\t\tb2Joint_WakeBodies( m_jointId1 );\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Speed\", &m_motorSpeed, -20.0f, 20.0f, \"%.0f\" ) )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetMotorSpeed( m_jointId1, m_motorSpeed );\n\t\t\t\tb2Joint_WakeBodies( m_jointId1 );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Spring\", &m_enableSpring ) )\n\t\t{\n\t\t\tb2RevoluteJoint_EnableSpring( m_jointId1, m_enableSpring );\n\t\t\tb2Joint_WakeBodies( m_jointId1 );\n\t\t}\n\n\t\tif ( m_enableSpring )\n\t\t{\n\t\t\tif ( ImGui::SliderFloat( \"Hertz\", &m_hertz, 0.0f, 30.0f, \"%.1f\" ) )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetSpringHertz( m_jointId1, m_hertz );\n\t\t\t\tb2Joint_WakeBodies( m_jointId1 );\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Damping\", &m_dampingRatio, 0.0f, 2.0f, \"%.1f\" ) )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetSpringDampingRatio( m_jointId1, m_dampingRatio );\n\t\t\t\tb2Joint_WakeBodies( m_jointId1 );\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Degrees\", &m_targetDegrees, -180.0f, 180.0f, \"%.0f\" ) )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetTargetAngle( m_jointId1, B2_PI * m_targetDegrees / 180.0f );\n\t\t\t\tb2Joint_WakeBodies( m_jointId1 );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tfloat angle1 = b2RevoluteJoint_GetAngle( m_jointId1 );\n\t\tDrawTextLine( \"Angle (Deg) 1 = %2.1f\", angle1 );\n\n\t\tfloat torque1 = b2RevoluteJoint_GetMotorTorque( m_jointId1 );\n\t\tDrawTextLine( \"Motor Torque 1 = %4.1f\", torque1 );\n\n\t\tfloat torque2 = b2RevoluteJoint_GetMotorTorque( m_jointId2 );\n\t\tDrawTextLine( \"Motor Torque 2 = %4.1f\", torque2 );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new RevoluteJoint( context );\n\t}\n\n\tb2BodyId m_ball;\n\tb2JointId m_jointId1;\n\tb2JointId m_jointId2;\n\tfloat m_motorSpeed;\n\tfloat m_motorTorque;\n\tfloat m_hertz;\n\tfloat m_dampingRatio;\n\tfloat m_targetDegrees;\n\tbool m_enableSpring;\n\tbool m_enableMotor;\n\tbool m_enableLimit;\n};\n\nstatic int sampleRevolute = RegisterSample( \"Joints\", \"Revolute\", RevoluteJoint::Create );\n\nclass PrismaticJoint : public Sample\n{\npublic:\n\texplicit PrismaticJoint( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 8.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t}\n\n\t\tb2BodyId groundId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\tm_enableSpring = false;\n\t\tm_enableLimit = true;\n\t\tm_enableMotor = false;\n\t\tm_motorSpeed = 2.0f;\n\t\tm_motorForce = 25.0f;\n\t\tm_hertz = 1.0f;\n\t\tm_dampingRatio = 0.5f;\n\t\tm_translation = 0.0f;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, 10.0f };\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 2.0f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { 0.0f, 9.0f };\n\t\t\t// b2Vec2 axis = b2Normalize({1.0f, 0.0f});\n\t\t\tb2Vec2 axis = b2Normalize( { 1.0f, 1.0f } );\n\t\t\tb2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameA.q = b2MakeRotFromUnitVector( axis );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.base.localFrameB.q = b2MakeRotFromUnitVector( axis );\n\t\t\tjointDef.base.drawScale = 2.0f;\n\t\t\tjointDef.motorSpeed = m_motorSpeed;\n\t\t\tjointDef.maxMotorForce = m_motorForce;\n\t\t\tjointDef.enableMotor = m_enableMotor;\n\t\t\tjointDef.lowerTranslation = -10.0f;\n\t\t\tjointDef.upperTranslation = 10.0f;\n\t\t\tjointDef.enableLimit = m_enableLimit;\n\t\t\tjointDef.enableSpring = m_enableSpring;\n\t\t\tjointDef.hertz = m_hertz;\n\t\t\tjointDef.dampingRatio = m_dampingRatio;\n\n\t\t\tm_jointId = b2CreatePrismaticJoint( m_worldId, &jointDef );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 240.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Prismatic Joint\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Checkbox( \"Limit\", &m_enableLimit ) )\n\t\t{\n\t\t\tb2PrismaticJoint_EnableLimit( m_jointId, m_enableLimit );\n\t\t\tb2Joint_WakeBodies( m_jointId );\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Motor\", &m_enableMotor ) )\n\t\t{\n\t\t\tb2PrismaticJoint_EnableMotor( m_jointId, m_enableMotor );\n\t\t\tb2Joint_WakeBodies( m_jointId );\n\t\t}\n\n\t\tif ( m_enableMotor )\n\t\t{\n\t\t\tif ( ImGui::SliderFloat( \"Max Force\", &m_motorForce, 0.0f, 200.0f, \"%.0f\" ) )\n\t\t\t{\n\t\t\t\tb2PrismaticJoint_SetMaxMotorForce( m_jointId, m_motorForce );\n\t\t\t\tb2Joint_WakeBodies( m_jointId );\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Speed\", &m_motorSpeed, -40.0f, 40.0f, \"%.0f\" ) )\n\t\t\t{\n\t\t\t\tb2PrismaticJoint_SetMotorSpeed( m_jointId, m_motorSpeed );\n\t\t\t\tb2Joint_WakeBodies( m_jointId );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Spring\", &m_enableSpring ) )\n\t\t{\n\t\t\tb2PrismaticJoint_EnableSpring( m_jointId, m_enableSpring );\n\t\t\tb2Joint_WakeBodies( m_jointId );\n\t\t}\n\n\t\tif ( m_enableSpring )\n\t\t{\n\t\t\tif ( ImGui::SliderFloat( \"Hertz\", &m_hertz, 0.0f, 10.0f, \"%.1f\" ) )\n\t\t\t{\n\t\t\t\tb2PrismaticJoint_SetSpringHertz( m_jointId, m_hertz );\n\t\t\t\tb2Joint_WakeBodies( m_jointId );\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Damping\", &m_dampingRatio, 0.0f, 2.0f, \"%.1f\" ) )\n\t\t\t{\n\t\t\t\tb2PrismaticJoint_SetSpringDampingRatio( m_jointId, m_dampingRatio );\n\t\t\t\tb2Joint_WakeBodies( m_jointId );\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Translation\", &m_translation, -15.0f, 15.0f, \"%.1f\" ) )\n\t\t\t{\n\t\t\t\tb2PrismaticJoint_SetTargetTranslation( m_jointId, m_translation );\n\t\t\t\tb2Joint_WakeBodies( m_jointId );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tfloat force = b2PrismaticJoint_GetMotorForce( m_jointId );\n\t\tDrawTextLine( \"Motor Force = %4.1f\", force );\n\n\t\tfloat translation = b2PrismaticJoint_GetTranslation( m_jointId );\n\t\tDrawTextLine( \"Translation = %4.1f\", translation );\n\n\t\tfloat speed = b2PrismaticJoint_GetSpeed( m_jointId );\n\t\tDrawTextLine( \"Speed = %4.8f\", speed );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new PrismaticJoint( context );\n\t}\n\n\tb2JointId m_jointId;\n\tfloat m_motorSpeed;\n\tfloat m_motorForce;\n\tfloat m_hertz;\n\tfloat m_dampingRatio;\n\tfloat m_translation;\n\tbool m_enableSpring;\n\tbool m_enableMotor;\n\tbool m_enableLimit;\n};\n\nstatic int samplePrismatic = RegisterSample( \"Joints\", \"Prismatic\", PrismaticJoint::Create );\n\nclass WheelJoint : public Sample\n{\npublic:\n\texplicit WheelJoint( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 10.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.15f;\n\t\t}\n\n\t\tb2BodyId groundId;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\tm_enableSpring = true;\n\t\tm_enableLimit = true;\n\t\tm_enableMotor = true;\n\t\tm_motorSpeed = 2.0f;\n\t\tm_motorTorque = 5.0f;\n\t\tm_hertz = 1.0f;\n\t\tm_dampingRatio = 0.7f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.position = { 0.0f, 10.25f };\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Capsule capsule = { { 0.0f, -0.5f }, { 0.0f, 0.5f }, 0.5f };\n\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\n\t\tb2Vec2 pivot = { 0.0f, 10.0f };\n\t\tb2Vec2 axis = b2Normalize( { 1.0f, 1.0f } );\n\t\tb2WheelJointDef jointDef = b2DefaultWheelJointDef();\n\t\tjointDef.base.bodyIdA = groundId;\n\t\tjointDef.base.bodyIdB = bodyId;\n\t\tjointDef.base.localFrameA.q = b2MakeRotFromUnitVector( axis );\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.motorSpeed = m_motorSpeed;\n\t\tjointDef.maxMotorTorque = m_motorTorque;\n\t\tjointDef.enableMotor = m_enableMotor;\n\t\tjointDef.lowerTranslation = -3.0f;\n\t\tjointDef.upperTranslation = 3.0f;\n\t\tjointDef.enableLimit = m_enableLimit;\n\t\tjointDef.hertz = m_hertz;\n\t\tjointDef.dampingRatio = m_dampingRatio;\n\n\t\tm_jointId = b2CreateWheelJoint( m_worldId, &jointDef );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 15.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 18.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Wheel Joint\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Checkbox( \"Limit\", &m_enableLimit ) )\n\t\t{\n\t\t\tb2WheelJoint_EnableLimit( m_jointId, m_enableLimit );\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Motor\", &m_enableMotor ) )\n\t\t{\n\t\t\tb2WheelJoint_EnableMotor( m_jointId, m_enableMotor );\n\t\t}\n\n\t\tif ( m_enableMotor )\n\t\t{\n\t\t\tif ( ImGui::SliderFloat( \"Torque\", &m_motorTorque, 0.0f, 20.0f, \"%.0f\" ) )\n\t\t\t{\n\t\t\t\tb2WheelJoint_SetMaxMotorTorque( m_jointId, m_motorTorque );\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Speed\", &m_motorSpeed, -20.0f, 20.0f, \"%.0f\" ) )\n\t\t\t{\n\t\t\t\tb2WheelJoint_SetMotorSpeed( m_jointId, m_motorSpeed );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Spring\", &m_enableSpring ) )\n\t\t{\n\t\t\tb2WheelJoint_EnableSpring( m_jointId, m_enableSpring );\n\t\t}\n\n\t\tif ( m_enableSpring )\n\t\t{\n\t\t\tif ( ImGui::SliderFloat( \"Hertz\", &m_hertz, 0.0f, 10.0f, \"%.1f\" ) )\n\t\t\t{\n\t\t\t\tb2WheelJoint_SetSpringHertz( m_jointId, m_hertz );\n\t\t\t}\n\n\t\t\tif ( ImGui::SliderFloat( \"Damping\", &m_dampingRatio, 0.0f, 2.0f, \"%.1f\" ) )\n\t\t\t{\n\t\t\t\tb2WheelJoint_SetSpringDampingRatio( m_jointId, m_dampingRatio );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tfloat torque = b2WheelJoint_GetMotorTorque( m_jointId );\n\t\tDrawTextLine( \"Motor Torque = %4.1f\", torque );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new WheelJoint( context );\n\t}\n\n\tb2JointId m_jointId;\n\tfloat m_hertz;\n\tfloat m_dampingRatio;\n\tfloat m_motorSpeed;\n\tfloat m_motorTorque;\n\tbool m_enableSpring;\n\tbool m_enableMotor;\n\tbool m_enableLimit;\n};\n\nstatic int sampleWheel = RegisterSample( \"Joints\", \"Wheel\", WheelJoint::Create );\n\n// A suspension bridge\nclass Bridge : public Sample\n{\npublic:\n\texplicit Bridge( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.zoom = 25.0f * 2.5f;\n\t\t}\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\t{\n\t\t\tm_constraintHertz = 60.0f;\n\t\t\tm_constraintDampingRatio = 0.0f;\n\t\t\tm_springHertz = 2.0f;\n\t\t\tm_springDampingRatio = 0.7f;\n\t\t\tm_frictionTorque = 200.0f;\n\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 0.125f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 20.0f;\n\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorTorque = m_frictionTorque;\n\t\t\tjointDef.enableSpring = true;\n\t\t\tjointDef.hertz = m_springHertz;\n\t\t\tjointDef.dampingRatio = m_springDampingRatio;\n\n\t\t\tint jointIndex = 0;\n\n\t\t\tfloat xbase = -80.0f;\n\n\t\t\tb2BodyId prevBodyId = groundId;\n\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t{\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t\tbodyDef.position = { xbase + 0.5f + 1.0f * i, 20.0f };\n\t\t\t\tbodyDef.linearDamping = 0.1f;\n\t\t\t\tbodyDef.angularDamping = 0.1f;\n\n\t\t\t\tm_bodyIds[i] = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\tb2CreatePolygonShape( m_bodyIds[i], &shapeDef, &box );\n\n\t\t\t\tb2Vec2 pivot = { xbase + 1.0f * i, 20.0f };\n\t\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\t\tjointDef.base.bodyIdB = m_bodyIds[i];\n\t\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\t\tm_jointIds[jointIndex++] = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\t\tprevBodyId = m_bodyIds[i];\n\t\t\t}\n\n\t\t\tb2Vec2 pivot = { xbase + 1.0f * m_count, 20.0f };\n\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\tjointDef.base.bodyIdB = groundId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tm_jointIds[jointIndex++] = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\tassert( jointIndex == m_count + 1 );\n\t\t}\n\n\t\tfor ( int i = 0; i < 2; ++i )\n\t\t{\n\t\t\tb2Vec2 vertices[3] = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 1.5f } };\n\n\t\t\tb2Hull hull = b2ComputeHull( vertices, 3 );\n\t\t\tb2Polygon triangle = b2MakePolygon( &hull, 0.0f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 20.0f;\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -8.0f + 8.0f * i, 22.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &triangle );\n\t\t}\n\n\t\tfor ( int i = 0; i < 3; ++i )\n\t\t{\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 20.0f;\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -6.0f + 6.0f * i, 25.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 180.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 320.0f, height ) );\n\n\t\tImGui::Begin( \"Bridge\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::PushItemWidth( ImGui::GetWindowWidth() * 0.6f );\n\n\t\tbool updateFriction = ImGui::SliderFloat( \"Joint Friction\", &m_frictionTorque, 0.0f, 10000.0f, \"%2.f\" );\n\t\tif ( updateFriction )\n\t\t{\n\t\t\tfor ( int i = 0; i <= m_count; ++i )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetMaxMotorTorque( m_jointIds[i], m_frictionTorque );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Spring hertz\", &m_springHertz, 0.0f, 30.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i <= m_count; ++i )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetSpringHertz( m_jointIds[i], m_springHertz );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Spring damping\", &m_springDampingRatio, 0.0f, 2.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i <= m_count; ++i )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetSpringDampingRatio( m_jointIds[i], m_springDampingRatio );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Constraint hertz\", &m_constraintHertz, 15.0f, 240.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i <= m_count; ++i )\n\t\t\t{\n\t\t\t\tb2Joint_SetConstraintTuning( m_jointIds[i], m_constraintHertz, m_constraintDampingRatio );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Constraint damping\", &m_constraintDampingRatio, 0.0f, 10.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i <= m_count; ++i )\n\t\t\t{\n\t\t\t\tb2Joint_SetConstraintTuning( m_jointIds[i], m_constraintHertz, m_constraintDampingRatio );\n\t\t\t}\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Bridge( context );\n\t}\n\n\tstatic constexpr int m_count = 160;\n\tb2BodyId m_bodyIds[m_count];\n\tb2JointId m_jointIds[m_count + 1];\n\tfloat m_frictionTorque;\n\tfloat m_constraintHertz;\n\tfloat m_constraintDampingRatio;\n\tfloat m_springHertz;\n\tfloat m_springDampingRatio;\n};\n\nstatic int sampleBridgeIndex = RegisterSample( \"Joints\", \"Bridge\", Bridge::Create );\n\nclass BallAndChain : public Sample\n{\npublic:\n\texplicit BallAndChain( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, -8.0f };\n\t\t\tm_context->camera.zoom = 27.5f;\n\t\t}\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\tm_frictionTorque = 100.0f;\n\n\t\t{\n\t\t\tfloat hx = 0.5f;\n\t\t\tb2Capsule capsule = { { -hx, 0.0f }, { hx, 0.0f }, 0.125f };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 20.0f;\n\t\t\tshapeDef.filter.categoryBits = 0x1;\n\t\t\tshapeDef.filter.maskBits = 0x2;\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\n\t\t\tint jointIndex = 0;\n\n\t\t\tb2BodyId prevBodyId = groundId;\n\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t{\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t\tbodyDef.position = { ( 1.0f + 2.0f * i ) * hx, m_count * hx };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\n\t\t\t\tb2Vec2 pivot = { ( 2.0f * i ) * hx, m_count * hx };\n\t\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\t\tjointDef.enableMotor = true;\n\t\t\t\tjointDef.maxMotorTorque = m_frictionTorque;\n\t\t\t\tjointDef.enableSpring = i > 0;\n\t\t\t\tjointDef.hertz = 4.0f;\n\t\t\t\tm_jointIds[jointIndex++] = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\t\tprevBodyId = bodyId;\n\t\t\t}\n\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 4.0f };\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { ( 1.0f + 2.0f * m_count ) * hx + circle.radius - hx, m_count * hx };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tshapeDef.filter.categoryBits = 0x2;\n\t\t\tshapeDef.filter.maskBits = 0x1;\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\n\t\t\tb2Vec2 pivot = { ( 2.0f * m_count ) * hx, m_count * hx };\n\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorTorque = m_frictionTorque;\n\t\t\tjointDef.enableSpring = true;\n\t\t\tjointDef.hertz = 4.0f;\n\t\t\tm_jointIds[jointIndex++] = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t\tassert( jointIndex == m_count + 1 );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 60.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Ball and Chain\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tbool updateFriction = ImGui::SliderFloat( \"Joint Friction\", &m_frictionTorque, 0.0f, 1000.0f, \"%2.f\" );\n\t\tif ( updateFriction )\n\t\t{\n\t\t\tfor ( int i = 0; i <= m_count; ++i )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetMaxMotorTorque( m_jointIds[i], m_frictionTorque );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BallAndChain( context );\n\t}\n\n\tstatic constexpr int m_count = 30;\n\tb2JointId m_jointIds[m_count + 1];\n\tfloat m_frictionTorque;\n};\n\nstatic int sampleBallAndChainIndex = RegisterSample( \"Joints\", \"Ball & Chain\", BallAndChain::Create );\n\n// This sample shows the limitations of an iterative solver. The cantilever sags even though the weld\n// joint is stiff as possible.\nclass Cantilever : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_count = 8\n\t};\n\n\texplicit Cantilever( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.35f;\n\t\t}\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\t{\n\t\t\tm_linearHertz = 15.0f;\n\t\t\tm_linearDampingRatio = 0.5f;\n\t\t\tm_angularHertz = 5.0f;\n\t\t\tm_angularDampingRatio = 0.5f;\n\t\t\tm_gravityScale = 1.0f;\n\t\t\tm_collideConnected = false;\n\n\t\t\tfloat hx = 0.5f;\n\t\t\tb2Capsule capsule = { { -hx, 0.0f }, { hx, 0.0f }, 0.125f };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 20.0f;\n\n\t\t\tb2WeldJointDef jointDef = b2DefaultWeldJointDef();\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.isAwake = false;\n\n\t\t\tb2BodyId prevBodyId = groundId;\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tbodyDef.position = { ( 1.0f + 2.0f * i ) * hx, 0.0f };\n\t\t\t\tm_bodyIds[i] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreateCapsuleShape( m_bodyIds[i], &shapeDef, &capsule );\n\n\t\t\t\tb2Vec2 pivot = { ( 2.0f * i ) * hx, 0.0f };\n\t\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\t\tjointDef.base.bodyIdB = m_bodyIds[i];\n\t\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\t\tjointDef.linearHertz = m_linearHertz;\n\t\t\t\tjointDef.linearDampingRatio = m_linearDampingRatio;\n\t\t\t\tjointDef.angularHertz = m_angularHertz;\n\t\t\t\tjointDef.angularDampingRatio = m_angularDampingRatio;\n\t\t\t\tjointDef.base.collideConnected = m_collideConnected;\n\t\t\t\tm_jointIds[i] = b2CreateWeldJoint( m_worldId, &jointDef );\n\n\t\t\t\t// Experimental tuning\n\t\t\t\tb2Joint_SetConstraintTuning( m_jointIds[i], 120.0f, 10.0f );\n\n\t\t\t\tprevBodyId = m_bodyIds[i];\n\t\t\t}\n\n\t\t\tm_tipId = prevBodyId;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 14.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 19.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Cantilever\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 8.0f * fontSize );\n\n\t\tif ( ImGui::SliderFloat( \"Linear Hertz\", &m_linearHertz, 0.0f, 20.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2WeldJoint_SetLinearHertz( m_jointIds[i], m_linearHertz );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Linear Damping Ratio\", &m_linearDampingRatio, 0.0f, 10.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2WeldJoint_SetLinearDampingRatio( m_jointIds[i], m_linearDampingRatio );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Angular Hertz\", &m_angularHertz, 0.0f, 20.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2WeldJoint_SetAngularHertz( m_jointIds[i], m_angularHertz );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Angular Damping Ratio\", &m_angularDampingRatio, 0.0f, 10.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2WeldJoint_SetAngularDampingRatio( m_jointIds[i], m_angularDampingRatio );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Collide Connected\", &m_collideConnected ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2Joint_SetCollideConnected( m_jointIds[i], m_collideConnected );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Gravity Scale\", &m_gravityScale, -1.0f, 1.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2Body_SetGravityScale( m_bodyIds[i], m_gravityScale );\n\t\t\t}\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2Vec2 tipPosition = b2Body_GetPosition( m_tipId );\n\t\tDrawTextLine( \"tip-y = %.2f\", tipPosition.y );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Cantilever( context );\n\t}\n\n\tfloat m_linearHertz;\n\tfloat m_linearDampingRatio;\n\tfloat m_angularHertz;\n\tfloat m_angularDampingRatio;\n\tfloat m_gravityScale;\n\tb2BodyId m_tipId;\n\tb2BodyId m_bodyIds[e_count];\n\tb2JointId m_jointIds[e_count];\n\tbool m_collideConnected;\n};\n\nstatic int sampleCantileverIndex = RegisterSample( \"Joints\", \"Cantilever\", Cantilever::Create );\n\n// This test ensures joints work correctly with bodies that have motion locks\nclass MotionLocks : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_count = 6\n\t};\n\n\texplicit MotionLocks( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 8.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.7f;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tm_motionLocks = { false, false, true };\n\n\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t{\n\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t}\n\n\t\tb2Vec2 position = { -12.5f, 10.0f };\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.motionLocks = m_motionLocks;\n\n\t\tb2Polygon box = b2MakeBox( 1.0f, 1.0f );\n\n\t\tint index = 0;\n\n\t\t// distance joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tfloat length = 2.0f;\n\t\t\tb2Vec2 pivot1 = { position.x, position.y + 1.0f + length };\n\t\t\tb2Vec2 pivot2 = { position.x, position.y + 1.0f };\n\t\t\tb2DistanceJointDef jointDef = b2DefaultDistanceJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot1 );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot2 );\n\t\t\tjointDef.length = length;\n\t\t\tb2CreateDistanceJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// motor joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tb2MotorJointDef jointDef = b2DefaultMotorJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = position;\n\t\t\tjointDef.maxVelocityForce = 200.0f;\n\t\t\tjointDef.maxVelocityTorque = 200.0f;\n\t\t\tb2CreateMotorJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// prismatic joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tb2CreatePrismaticJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// revolute joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// weld joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2WeldJointDef jointDef = b2DefaultWeldJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.angularHertz = 1.0f;\n\t\t\tjointDef.angularDampingRatio = 0.5f;\n\t\t\tjointDef.linearHertz = 1.0f;\n\t\t\tjointDef.linearDampingRatio = 0.5f;\n\t\t\tb2CreateWeldJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// wheel joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2WheelJointDef jointDef = b2DefaultWheelJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.hertz = 1.0f;\n\t\t\tjointDef.dampingRatio = 0.7f;\n\t\t\tjointDef.lowerTranslation = -1.0f;\n\t\t\tjointDef.upperTranslation = 1.0f;\n\t\t\tjointDef.enableLimit = true;\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorTorque = 10.0f;\n\t\t\tjointDef.motorSpeed = 1.0f;\n\t\t\tb2CreateWheelJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 8.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 14.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Motion Locks\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Checkbox( \"Lock Linear X\", &m_motionLocks.linearX ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2Body_SetMotionLocks( m_bodyIds[i], m_motionLocks );\n\t\t\t\tb2Body_SetAwake( m_bodyIds[i], true );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Lock Linear Y\", &m_motionLocks.linearY ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2Body_SetMotionLocks( m_bodyIds[i], m_motionLocks );\n\t\t\t\tb2Body_SetAwake( m_bodyIds[i], true );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::Checkbox( \"Lock Angular Z\", &m_motionLocks.angularZ ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2Body_SetMotionLocks( m_bodyIds[i], m_motionLocks );\n\t\t\t\tb2Body_SetAwake( m_bodyIds[i], true );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_L ) == GLFW_PRESS )\n\t\t{\n\t\t\tb2Body_ApplyLinearImpulseToCenter( m_bodyIds[0], { 100.0f, 0.0f }, true );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new MotionLocks( context );\n\t}\n\n\tb2BodyId m_bodyIds[e_count];\n\tb2MotionLocks m_motionLocks;\n};\n\nstatic int sampleMotionLocks = RegisterSample( \"Joints\", \"Motion Locks\", MotionLocks::Create );\n\n// This sample shows how to break joints when the internal reaction force becomes large.\nclass BreakableJoint : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_count = 6\n\t};\n\n\texplicit BreakableJoint( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 8.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.7f;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } };\n\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t{\n\t\t\tm_jointIds[i] = b2_nullJointId;\n\t\t}\n\n\t\tb2Vec2 position = { -12.5f, 10.0f };\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.enableSleep = false;\n\n\t\tb2Polygon box = b2MakeBox( 1.0f, 1.0f );\n\n\t\tint index = 0;\n\n\t\t// distance joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tfloat length = 2.0f;\n\t\t\tb2Vec2 pivot1 = { position.x, position.y + 1.0f + length };\n\t\t\tb2Vec2 pivot2 = { position.x, position.y + 1.0f };\n\t\t\tb2DistanceJointDef jointDef = b2DefaultDistanceJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot1 );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot2 );\n\t\t\tjointDef.length = length;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreateDistanceJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// motor joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2MotorJointDef jointDef = b2DefaultMotorJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = position;\n\t\t\tjointDef.maxVelocityForce = 1000.0f;\n\t\t\tjointDef.maxVelocityTorque = 20.0f;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreateMotorJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// prismatic joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreatePrismaticJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// revolute joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// weld joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2WeldJointDef jointDef = b2DefaultWeldJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreateWeldJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\t// wheel joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2WheelJointDef jointDef = b2DefaultWheelJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.hertz = 1.0f;\n\t\t\tjointDef.dampingRatio = 0.7f;\n\t\t\tjointDef.lowerTranslation = -1.0f;\n\t\t\tjointDef.upperTranslation = 1.0f;\n\t\t\tjointDef.enableLimit = true;\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorTorque = 10.0f;\n\t\t\tjointDef.motorSpeed = 1.0f;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreateWheelJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 5.0f;\n\t\t++index;\n\n\t\tm_breakForce = 1000.0f;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 100.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Breakable Joint\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::SliderFloat( \"break force\", &m_breakForce, 0.0f, 10000.0f, \"%.1f\" );\n\n\t\tb2Vec2 gravity = b2World_GetGravity( m_worldId );\n\t\tif ( ImGui::SliderFloat( \"gravity\", &gravity.y, -50.0f, 50.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tb2World_SetGravity( m_worldId, gravity );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NULL( m_jointIds[i] ) )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2Vec2 force = b2Joint_GetConstraintForce( m_jointIds[i] );\n\t\t\tif ( b2LengthSquared( force ) > m_breakForce * m_breakForce )\n\t\t\t{\n\t\t\t\tb2DestroyJoint( m_jointIds[i], true );\n\t\t\t\tm_jointIds[i] = b2_nullJointId;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2Transform localFrame = b2Joint_GetLocalFrameA( m_jointIds[i] );\n\t\t\t\tDrawWorldString( m_draw, m_camera, localFrame.p, b2_colorWhite, \"(%.1f, %.1f)\", force.x, force.y );\n\t\t\t}\n\t\t}\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BreakableJoint( context );\n\t}\n\n\tb2JointId m_jointIds[e_count];\n\tfloat m_breakForce;\n};\n\nstatic int sampleBreakableJoint = RegisterSample( \"Joints\", \"Breakable\", BreakableJoint::Create );\n\n// This sample shows how to measure joint separation. This is the unresolved constraint error.\nclass JointSeparation : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_count = 5\n\t};\n\n\texplicit JointSeparation( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 8.0f };\n\t\t\tm_context->camera.zoom = 25.0f;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } };\n\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\tb2Vec2 position = { -20.0f, 10.0f };\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.enableSleep = false;\n\n\t\tb2Polygon box = b2MakeBox( 1.0f, 1.0f );\n\n\t\tint index = 0;\n\n\t\t// distance joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tfloat length = 2.0f;\n\t\t\tb2Vec2 pivot1 = { position.x, position.y + 1.0f + length };\n\t\t\tb2Vec2 pivot2 = { position.x, position.y + 1.0f };\n\t\t\tb2DistanceJointDef jointDef = b2DefaultDistanceJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot1 );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot2 );\n\t\t\tjointDef.length = length;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreateDistanceJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 10.0f;\n\t\t++index;\n\n\t\t// prismatic joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreatePrismaticJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 10.0f;\n\t\t++index;\n\n\t\t// revolute joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 10.0f;\n\t\t++index;\n\n\t\t// weld joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2WeldJointDef jointDef = b2DefaultWeldJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreateWeldJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tposition.x += 10.0f;\n\t\t++index;\n\n\t\t// wheel joint\n\t\t{\n\t\t\tassert( index < e_count );\n\n\t\t\tbodyDef.position = position;\n\t\t\tm_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = { position.x - 1.0f, position.y };\n\t\t\tb2WheelJointDef jointDef = b2DefaultWheelJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[index];\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.hertz = 1.0f;\n\t\t\tjointDef.dampingRatio = 0.7f;\n\t\t\tjointDef.lowerTranslation = -1.0f;\n\t\t\tjointDef.upperTranslation = 1.0f;\n\t\t\tjointDef.enableLimit = true;\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorTorque = 10.0f;\n\t\t\tjointDef.motorSpeed = 1.0f;\n\t\t\tjointDef.base.collideConnected = true;\n\t\t\tm_jointIds[index] = b2CreateWheelJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\tm_impulse = 500.0f;\n\t\tm_jointHertz = 60.0f;\n\t\tm_jointDampingRatio = 2.0f;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 14.0f * fontSize;\n\t\tImGui::SetNextWindowPos( { 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize }, ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( { 20.0f * fontSize, height } );\n\n\t\tImGui::Begin( \"Joint Separation\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tb2Vec2 gravity = b2World_GetGravity( m_worldId );\n\t\tif ( ImGui::SliderFloat( \"gravity\", &gravity.y, -500.0f, 500.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tb2World_SetGravity( m_worldId, gravity );\n\t\t}\n\n\t\tif ( ImGui::Button( \"impulse\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 p = b2Body_GetWorldPoint( m_bodyIds[i], { 1.0f, 1.0f } );\n\t\t\t\tb2Body_ApplyLinearImpulse( m_bodyIds[i], { m_impulse, -m_impulse }, p, true );\n\t\t\t}\n\t\t}\n\n\t\tImGui::SliderFloat( \"magnitude\", &m_impulse, 0.0f, 1000.0f, \"%.0f\" );\n\n\t\tif ( ImGui::SliderFloat( \"hertz\", &m_jointHertz, 15.0f, 120.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2Joint_SetConstraintTuning( m_jointIds[i], m_jointHertz, m_jointDampingRatio );\n\t\t\t}\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"damping\", &m_jointDampingRatio, 0.0f, 10.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t\t{\n\t\t\t\tb2Joint_SetConstraintTuning( m_jointIds[i], m_jointHertz, m_jointDampingRatio );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NULL( m_jointIds[i] ) )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfloat linear = b2Joint_GetLinearSeparation( m_jointIds[i] );\n\t\t\tfloat angular = b2Joint_GetAngularSeparation( m_jointIds[i] );\n\t\t\tb2Transform localFrame = b2Joint_GetLocalFrameA( m_jointIds[i] );\n\t\t\tDrawWorldString( m_draw, m_camera, localFrame.p, b2_colorWhite, \"%.2f m, %.1f deg\", linear,\n\t\t\t\t\t\t\t 180.0f * angular / B2_PI );\n\t\t}\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new JointSeparation( context );\n\t}\n\n\tb2BodyId m_bodyIds[e_count];\n\tb2JointId m_jointIds[e_count];\n\tfloat m_impulse;\n\tfloat m_jointHertz;\n\tfloat m_jointDampingRatio;\n};\n\nstatic int sampleJointSeparation = RegisterSample( \"Joints\", \"Separation\", JointSeparation::Create );\n\n// This shows how you can implement a constraint outside of Box2D\nclass UserConstraint : public Sample\n{\npublic:\n\texplicit UserConstraint( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 3.0f, -1.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.15f;\n\t\t}\n\n\t\tb2Polygon box = b2MakeBox( 1.0f, 0.5f );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 20.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.gravityScale = 1.0f;\n\t\tbodyDef.angularDamping = 0.5f;\n\t\tbodyDef.linearDamping = 0.2f;\n\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\tb2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\n\t\tm_impulses[0] = 0.0f;\n\t\tm_impulses[1] = 0.0f;\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2Transform axes = b2Transform_identity;\n\t\tDrawTransform( m_draw, axes, 1.0f );\n\n\t\tif ( m_context->pause )\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tfloat timeStep = m_context->hertz > 0.0f ? 1.0f / m_context->hertz : 0.0f;\n\t\tif ( timeStep == 0.0f )\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tfloat invTimeStep = m_context->hertz;\n\n\t\tstatic float hertz = 3.0f;\n\t\tstatic float zeta = 0.7f;\n\t\tstatic float maxForce = 1000.0f;\n\t\tfloat omega = 2.0f * B2_PI * hertz;\n\t\tfloat sigma = 2.0f * zeta + timeStep * omega;\n\t\tfloat s = timeStep * omega * sigma;\n\t\tfloat impulseCoefficient = 1.0f / ( 1.0f + s );\n\t\tfloat massCoefficient = s * impulseCoefficient;\n\t\tfloat biasCoefficient = omega / sigma;\n\n\t\tb2Vec2 localAnchors[2] = { { 1.0f, -0.5f }, { 1.0f, 0.5f } };\n\t\tfloat mass = b2Body_GetMass( m_bodyId );\n\t\tfloat invMass = mass < 0.0001f ? 0.0f : 1.0f / mass;\n\t\tfloat inertiaTensor = b2Body_GetRotationalInertia( m_bodyId );\n\t\tfloat invI = inertiaTensor < 0.0001f ? 0.0f : 1.0f / inertiaTensor;\n\n\t\tb2Vec2 vB = b2Body_GetLinearVelocity( m_bodyId );\n\t\tfloat omegaB = b2Body_GetAngularVelocity( m_bodyId );\n\t\tb2Vec2 pB = b2Body_GetWorldCenterOfMass( m_bodyId );\n\n\t\tfor ( int i = 0; i < 2; ++i )\n\t\t{\n\t\t\tb2Vec2 anchorA = { 3.0f, 0.0f };\n\t\t\tb2Vec2 anchorB = b2Body_GetWorldPoint( m_bodyId, localAnchors[i] );\n\n\t\t\tb2Vec2 deltaAnchor = b2Sub( anchorB, anchorA );\n\n\t\t\tfloat slackLength = 1.0f;\n\t\t\tfloat length = b2Length( deltaAnchor );\n\t\t\tfloat C = length - slackLength;\n\t\t\tif ( C < 0.0f || length < 0.001f )\n\t\t\t{\n\t\t\t\tDrawLine( m_draw, anchorA, anchorB, b2_colorLightCyan );\n\t\t\t\tm_impulses[i] = 0.0f;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tDrawLine( m_draw, anchorA, anchorB, b2_colorViolet );\n\t\t\tb2Vec2 axis = b2Normalize( deltaAnchor );\n\n\t\t\tb2Vec2 rB = b2Sub( anchorB, pB );\n\t\t\tfloat Jb = b2Cross( rB, axis );\n\t\t\tfloat K = invMass + Jb * invI * Jb;\n\t\t\tfloat invK = K < 0.0001f ? 0.0f : 1.0f / K;\n\n\t\t\tfloat Cdot = b2Dot( vB, axis ) + Jb * omegaB;\n\t\t\tfloat impulse = -massCoefficient * invK * ( Cdot + biasCoefficient * C );\n\t\t\tfloat appliedImpulse = b2ClampFloat( impulse, -maxForce * timeStep, 0.0f );\n\n\t\t\tvB = b2MulAdd( vB, invMass * appliedImpulse, axis );\n\t\t\tomegaB += appliedImpulse * invI * Jb;\n\n\t\t\tm_impulses[i] = appliedImpulse;\n\t\t}\n\n\t\tb2Body_SetLinearVelocity( m_bodyId, vB );\n\t\tb2Body_SetAngularVelocity( m_bodyId, omegaB );\n\n\t\tDrawTextLine( \"forces = %g, %g\", m_impulses[0] * invTimeStep, m_impulses[1] * invTimeStep );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new UserConstraint( context );\n\t}\n\n\tb2BodyId m_bodyId;\n\tfloat m_impulses[2];\n};\n\nstatic int sampleUserConstraintIndex = RegisterSample( \"Joints\", \"User Constraint\", UserConstraint::Create );\n\n// This is a fun demo that shows off the wheel joint\nclass Driving : public Sample\n{\npublic:\n\texplicit Driving( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center.y = 5.0f;\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t\tm_context->debugDraw.drawJoints = false;\n\t\t}\n\n\t\tb2BodyId groundId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Vec2 points[25];\n\t\t\tint count = 24;\n\n\t\t\t// fill in reverse to match line list convention\n\t\t\tpoints[count--] = { -20.0f, -20.0f };\n\t\t\tpoints[count--] = { -20.0f, 0.0f };\n\t\t\tpoints[count--] = { 20.0f, 0.0f };\n\n\t\t\tfloat hs[10] = { 0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f };\n\t\t\tfloat x = 20.0f, dx = 5.0f;\n\n\t\t\tfor ( int j = 0; j < 2; ++j )\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i < 10; ++i )\n\t\t\t\t{\n\t\t\t\t\tfloat y2 = hs[i];\n\t\t\t\t\tpoints[count--] = { x + dx, y2 };\n\t\t\t\t\tx += dx;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// flat before bridge\n\t\t\tpoints[count--] = { x + 40.0f, 0.0f };\n\t\t\tpoints[count--] = { x + 40.0f, -20.0f };\n\n\t\t\tassert( count == -1 );\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = 25;\n\t\t\tchainDef.isLoop = true;\n\t\t\tb2CreateChain( groundId, &chainDef );\n\n\t\t\t// flat after bridge\n\t\t\tx += 80.0f;\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { x, 0.0f }, { x + 40.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\t// jump ramp\n\t\t\tx += 40.0f;\n\t\t\tsegment = { { x, 0.0f }, { x + 10.0f, 5.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\t// final corner\n\t\t\tx += 20.0f;\n\t\t\tsegment = { { x, 0.0f }, { x + 40.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tx += 40.0f;\n\t\t\tsegment = { { x, 0.0f }, { x, 20.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Teeter\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 140.0f, 1.0f };\n\t\t\tbodyDef.angularVelocity = 1.0f;\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeBox( 10.0f, 0.25f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tb2Vec2 pivot = bodyDef.position;\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.lowerAngle = -8.0f * B2_PI / 180.0f;\n\t\t\tjointDef.upperAngle = 8.0f * B2_PI / 180.0f;\n\t\t\tjointDef.enableLimit = true;\n\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\t// Bridge\n\t\t{\n\t\t\tint N = 20;\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Capsule capsule = { { -1.0f, 0.0f }, { 1.0f, 0.0f }, 0.125f };\n\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\n\t\t\tb2BodyId prevBodyId = groundId;\n\t\t\tfor ( int i = 0; i < N; ++i )\n\t\t\t{\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t\tbodyDef.position = { 161.0f + 2.0f * i, -0.125f };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\n\t\t\t\tb2Vec2 pivot = { 160.0f + 2.0f * i, -0.125f };\n\t\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\t\tprevBodyId = bodyId;\n\t\t\t}\n\n\t\t\tb2Vec2 pivot = { 160.0f + 2.0f * N, -0.125f };\n\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\tjointDef.base.bodyIdB = groundId;\n\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\tjointDef.enableMotor = true;\n\t\t\tjointDef.maxMotorTorque = 50.0f;\n\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\n\t\t// Boxes\n\t\t{\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 0.5f );\n\n\t\t\tb2BodyId bodyId;\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.25f;\n\t\t\tshapeDef.material.restitution = 0.25f;\n\t\t\tshapeDef.density = 0.25f;\n\n\t\t\tbodyDef.position = { 230.0f, 0.5f };\n\t\t\tbodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tbodyDef.position = { 230.0f, 1.5f };\n\t\t\tbodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tbodyDef.position = { 230.0f, 2.5f };\n\t\t\tbodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tbodyDef.position = { 230.0f, 3.5f };\n\t\t\tbodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tbodyDef.position = { 230.0f, 4.5f };\n\t\t\tbodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t// Car\n\n\t\tm_throttle = 0.0f;\n\t\tm_speed = 35.0f;\n\t\tm_torque = 5.0f;\n\t\tm_hertz = 5.0f;\n\t\tm_dampingRatio = 0.7f;\n\n\t\tm_car.Spawn( m_worldId, { 0.0f, 0.0f }, 1.0f, m_hertz, m_dampingRatio, m_torque, nullptr );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 10.0f * fontSize;\n\t\tImGui::SetNextWindowPos( { 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize }, ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( { 16.0f * fontSize, height } );\n\n\t\tImGui::Begin( \"Driving\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::PushItemWidth( 8.0f * fontSize );\n\t\tif ( ImGui::SliderFloat( \"Spring Hertz\", &m_hertz, 0.0f, 20.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tm_car.SetHertz( m_hertz );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Damping Ratio\", &m_dampingRatio, 0.0f, 10.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tm_car.SetDampingRadio( m_dampingRatio );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Speed\", &m_speed, 0.0f, 50.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tm_car.SetSpeed( m_throttle * m_speed );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Torque\", &m_torque, 0.0f, 10.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tm_car.SetTorque( m_torque );\n\t\t}\n\t\tImGui::PopItemWidth();\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_A ) == GLFW_PRESS )\n\t\t{\n\t\t\tm_throttle = 1.0f;\n\t\t\tm_car.SetSpeed( m_speed );\n\t\t}\n\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_S ) == GLFW_PRESS )\n\t\t{\n\t\t\tm_throttle = 0.0f;\n\t\t\tm_car.SetSpeed( 0.0f );\n\t\t}\n\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_D ) == GLFW_PRESS )\n\t\t{\n\t\t\tm_throttle = -1.0f;\n\t\t\tm_car.SetSpeed( -m_speed );\n\t\t}\n\n\t\tDrawTextLine( \"Keys: left = a, brake = s, right = d\" );\n\n\t\tb2Vec2 linearVelocity = b2Body_GetLinearVelocity( m_car.m_chassisId );\n\t\tfloat kph = linearVelocity.x * 3.6f;\n\t\tDrawTextLine( \"speed in kph: %.2g\", kph );\n\n\t\tb2Vec2 carPosition = b2Body_GetPosition( m_car.m_chassisId );\n\t\tm_context->camera.center.x = carPosition.x;\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Driving( context );\n\t}\n\n\tCar m_car;\n\n\tfloat m_throttle;\n\tfloat m_hertz;\n\tfloat m_dampingRatio;\n\tfloat m_torque;\n\tfloat m_speed;\n};\n\nstatic int sampleDriving = RegisterSample( \"Joints\", \"Driving\", Driving::Create );\n\nclass Ragdoll : public Sample\n{\npublic:\n\texplicit Ragdoll( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 12.0f };\n\t\t\tm_context->camera.zoom = 16.0f;\n\n\t\t\t// m_context->camera.m_center = { 0.0f, 26.0f };\n\t\t\t// m_context->camera.m_zoom = 1.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tm_jointFrictionTorque = 0.03f;\n\t\tm_jointHertz = 5.0f;\n\t\tm_jointDampingRatio = 0.5f;\n\n\t\tm_human = {};\n\n\t\tSpawn();\n\n\t\tb2World_SetContactTuning( m_worldId, 240.0f, 0.0f, 2.0f );\n\t}\n\n\tvoid Spawn()\n\t{\n\t\tCreateHuman( &m_human, m_worldId, { 0.0f, 25.0f }, 1.0f, m_jointFrictionTorque, m_jointHertz, m_jointDampingRatio, 1,\n\t\t\t\t\t nullptr, false );\n\t\t// Human_ApplyRandomAngularImpulse( &m_human, 10.0f );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 10.0f * fontSize;\n\t\tImGui::SetNextWindowPos( { 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize }, ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( { 14.0f * fontSize, height } );\n\n\t\tImGui::Begin( \"Ragdoll\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 8.0f * fontSize );\n\n\t\tif ( ImGui::SliderFloat( \"Friction\", &m_jointFrictionTorque, 0.0f, 1.0f, \"%3.2f\" ) )\n\t\t{\n\t\t\tHuman_SetJointFrictionTorque( &m_human, m_jointFrictionTorque );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Hertz\", &m_jointHertz, 0.0f, 10.0f, \"%3.1f\" ) )\n\t\t{\n\t\t\tHuman_SetJointSpringHertz( &m_human, m_jointHertz );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Damping\", &m_jointDampingRatio, 0.0f, 4.0f, \"%3.1f\" ) )\n\t\t{\n\t\t\tHuman_SetJointDampingRatio( &m_human, m_jointDampingRatio );\n\t\t}\n\n\t\tif ( ImGui::Button( \"Respawn\" ) )\n\t\t{\n\t\t\tDestroyHuman( &m_human );\n\t\t\tSpawn();\n\t\t}\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Ragdoll( context );\n\t}\n\n\tHuman m_human;\n\tfloat m_jointFrictionTorque;\n\tfloat m_jointHertz;\n\tfloat m_jointDampingRatio;\n};\n\nstatic int sampleRagdoll = RegisterSample( \"Joints\", \"Ragdoll\", Ragdoll::Create );\n\nclass SoftBody : public Sample\n{\npublic:\n\texplicit SoftBody( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.25f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tm_donut.Create( m_worldId, { 0.0f, 10.0f }, 2.0f, 0, false, nullptr );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SoftBody( context );\n\t}\n\n\tDonut m_donut;\n};\n\nstatic int sampleDonut = RegisterSample( \"Joints\", \"Soft Body\", SoftBody::Create );\n\nclass DoohickeyFarm : public Sample\n{\npublic:\n\texplicit DoohickeyFarm( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.35f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 1.0f, 1.0f, { 0.0f, 1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tfloat y = 4.0f;\n\t\tfor ( int i = 0; i < 4; ++i )\n\t\t{\n\t\t\tDoohickey doohickey;\n\t\t\tdoohickey.Spawn( m_worldId, { 0.0f, y }, 0.5f );\n\t\t\ty += 2.0f;\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new DoohickeyFarm( context );\n\t}\n};\n\nstatic int sampleDoohickey = RegisterSample( \"Joints\", \"Doohickey\", DoohickeyFarm::Create );\n\nclass ScissorLift : public Sample\n{\npublic:\n\texplicit ScissorLift( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 9.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.4f;\n\t\t}\n\n\t\t// Need 8 sub-steps for smoother operation\n\t\tm_context->subStepCount = 8;\n\n\t\tb2BodyId groundId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.sleepThreshold = 0.01f;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Capsule capsule = { { -2.5f, 0.0f }, { 2.5f, 0.0f }, 0.15f };\n\n\t\tb2BodyId baseId1 = groundId;\n\t\tb2BodyId baseId2 = groundId;\n\t\tb2Vec2 baseAnchor1 = { -2.5f, 0.2f };\n\t\tb2Vec2 baseAnchor2 = { 2.5f, 0.2f };\n\t\tfloat y = 0.5f;\n\n\t\tb2BodyId linkId1;\n\t\tint N = 3;\n\n\t\tfloat constraintDampingRatio = 20.0f;\n\t\tfloat constraintHertz = 240.0f;\n\n\t\tfor ( int i = 0; i < N; ++i )\n\t\t{\n\t\t\tbodyDef.position = { 0.0f, y };\n\t\t\tbodyDef.rotation = b2MakeRot( 0.15f );\n\t\t\tb2BodyId bodyId1 = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCapsuleShape( bodyId1, &shapeDef, &capsule );\n\n\t\t\tbodyDef.position = { 0.0f, y };\n\t\t\tbodyDef.rotation = b2MakeRot( -0.15f );\n\n\t\t\tb2BodyId bodyId2 = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCapsuleShape( bodyId2, &shapeDef, &capsule );\n\n\t\t\tif ( i == 1 )\n\t\t\t{\n\t\t\t\tlinkId1 = bodyId2;\n\t\t\t}\n\n\t\t\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\n\t\t\t// left pin\n\t\t\trevoluteDef.base.bodyIdA = baseId1;\n\t\t\trevoluteDef.base.bodyIdB = bodyId1;\n\t\t\trevoluteDef.base.localFrameA.p = baseAnchor1;\n\t\t\trevoluteDef.base.localFrameB.p = { -2.5f, 0.0f };\n\t\t\trevoluteDef.base.collideConnected = ( i == 0 ) ? true : false;\n\t\t\trevoluteDef.base.constraintDampingRatio = constraintDampingRatio;\n\t\t\trevoluteDef.base.constraintHertz = constraintHertz;\n\n\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\n\t\t\t// right pin\n\t\t\tif ( i == 0 )\n\t\t\t{\n\t\t\t\tb2WheelJointDef wheelDef = b2DefaultWheelJointDef();\n\t\t\t\twheelDef.base.bodyIdA = baseId2;\n\t\t\t\twheelDef.base.bodyIdB = bodyId2;\n\t\t\t\twheelDef.base.localFrameA.p = baseAnchor2;\n\t\t\t\twheelDef.base.localFrameB.p = { 2.5f, 0.0f };\n\t\t\t\twheelDef.enableSpring = false;\n\t\t\t\twheelDef.base.collideConnected = true;\n\t\t\t\twheelDef.base.constraintDampingRatio = constraintDampingRatio;\n\t\t\t\twheelDef.base.constraintHertz = constraintHertz;\n\n\t\t\t\tb2CreateWheelJoint( m_worldId, &wheelDef );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trevoluteDef.base.bodyIdA = baseId2;\n\t\t\t\trevoluteDef.base.bodyIdB = bodyId2;\n\t\t\t\trevoluteDef.base.localFrameA.p = baseAnchor2;\n\t\t\t\trevoluteDef.base.localFrameB.p = { 2.5f, 0.0f };\n\t\t\t\trevoluteDef.base.collideConnected = false;\n\t\t\t\trevoluteDef.base.constraintDampingRatio = constraintDampingRatio;\n\t\t\t\trevoluteDef.base.constraintHertz = constraintHertz;\n\n\t\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\t\t\t}\n\n\t\t\t// middle pin\n\t\t\trevoluteDef.base.bodyIdA = bodyId1;\n\t\t\trevoluteDef.base.bodyIdB = bodyId2;\n\t\t\trevoluteDef.base.localFrameA.p = { 0.0f, 0.0f };\n\t\t\trevoluteDef.base.localFrameB.p = { 0.0f, 0.0f };\n\t\t\trevoluteDef.base.collideConnected = false;\n\t\t\trevoluteDef.base.constraintDampingRatio = constraintDampingRatio;\n\t\t\trevoluteDef.base.constraintHertz = constraintHertz;\n\n\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\n\t\t\tbaseId1 = bodyId2;\n\t\t\tbaseId2 = bodyId1;\n\t\t\tbaseAnchor1 = { -2.5f, 0.0f };\n\t\t\tbaseAnchor2 = { 2.5f, 0.0f };\n\t\t\ty += 1.0f;\n\t\t}\n\n\t\tbodyDef.position = { 0.0f, y };\n\t\tbodyDef.rotation = b2Rot_identity;\n\t\tb2BodyId platformId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2Polygon box = b2MakeBox( 3.0f, 0.2f );\n\t\tb2CreatePolygonShape( platformId, &shapeDef, &box );\n\n\t\t// left pin\n\t\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\t\trevoluteDef.base.bodyIdA = platformId;\n\t\trevoluteDef.base.bodyIdB = baseId1;\n\t\trevoluteDef.base.localFrameA.p = { -2.5f, -0.4f };\n\t\trevoluteDef.base.localFrameB.p = baseAnchor1;\n\t\trevoluteDef.base.collideConnected = true;\n\t\trevoluteDef.base.constraintDampingRatio = constraintDampingRatio;\n\t\trevoluteDef.base.constraintHertz = constraintHertz;\n\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\n\t\t// right pin\n\t\tb2WheelJointDef wheelDef = b2DefaultWheelJointDef();\n\t\twheelDef.base.bodyIdA = platformId;\n\t\twheelDef.base.bodyIdB = baseId2;\n\t\twheelDef.base.localFrameA.p = { 2.5f, -0.4f };\n\t\twheelDef.base.localFrameB.p = baseAnchor2;\n\t\twheelDef.enableSpring = false;\n\t\twheelDef.base.collideConnected = true;\n\t\twheelDef.base.constraintDampingRatio = constraintDampingRatio;\n\t\twheelDef.base.constraintHertz = constraintHertz;\n\t\tb2CreateWheelJoint( m_worldId, &wheelDef );\n\n\t\tm_enableMotor = false;\n\t\tm_motorSpeed = 0.25f;\n\t\tm_motorForce = 2000.0f;\n\n\t\tb2DistanceJointDef distanceDef = b2DefaultDistanceJointDef();\n\t\tdistanceDef.base.bodyIdA = groundId;\n\t\tdistanceDef.base.bodyIdB = linkId1;\n\t\tdistanceDef.base.localFrameA.p = { -2.5f, 0.2f };\n\t\tdistanceDef.base.localFrameB.p = { 0.5f, 0.0f };\n\t\tdistanceDef.enableSpring = true;\n\t\tdistanceDef.minLength = 0.2f;\n\t\tdistanceDef.maxLength = 5.5f;\n\t\tdistanceDef.enableLimit = true;\n\t\tdistanceDef.enableMotor = m_enableMotor;\n\t\tdistanceDef.motorSpeed = m_motorSpeed;\n\t\tdistanceDef.maxMotorForce = m_motorForce;\n\t\tm_liftJointId = b2CreateDistanceJoint( m_worldId, &distanceDef );\n\n\t\tCar car;\n\t\tcar.Spawn( m_worldId, { 0.0f, y + 2.0f }, 1.0f, 3.0f, 0.7f, 0.0f, nullptr );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 140.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Scissor Lift\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Checkbox( \"Motor\", &m_enableMotor ) )\n\t\t{\n\t\t\tb2DistanceJoint_EnableMotor( m_liftJointId, m_enableMotor );\n\t\t\tb2Joint_WakeBodies( m_liftJointId );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Max Force\", &m_motorForce, 0.0f, 3000.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tb2DistanceJoint_SetMaxMotorForce( m_liftJointId, m_motorForce );\n\t\t\tb2Joint_WakeBodies( m_liftJointId );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Speed\", &m_motorSpeed, -0.3f, 0.3f, \"%.2f\" ) )\n\t\t{\n\t\t\tb2DistanceJoint_SetMotorSpeed( m_liftJointId, m_motorSpeed );\n\t\t\tb2Joint_WakeBodies( m_liftJointId );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ScissorLift( context );\n\t}\n\n\tb2JointId m_liftJointId;\n\tfloat m_motorForce;\n\tfloat m_motorSpeed;\n\tbool m_enableMotor;\n};\n\nstatic int sampleScissorLift = RegisterSample( \"Joints\", \"Scissor Lift\", ScissorLift::Create );\n\nclass GearLift : public Sample\n{\npublic:\n\texplicit GearLift( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 6.0f };\n\t\t\tm_context->camera.zoom = 7.0f;\n\t\t\tm_context->debugDraw.drawJoints = false;\n\t\t}\n\n\t\tb2BodyId groundId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tconst char* path =\n\t\t\t\t\"m 63.500002,201.08333 103.187498,0 1e-5,-37.04166 h -2.64584 l 0,34.39583 h -42.33333 v -2.64583 l \"\n\t\t\t\t\"-2.64584,-1e-5 v -2.64583 h -2.64583 v -2.64584 h -2.64584 v -2.64583 H 111.125 v -2.64583 h -2.64583 v \"\n\t\t\t\t\"-2.64583 h -2.64583 v -2.64584 l -2.64584,1e-5 v -2.64583 l -2.64583,-1e-5 V 174.625 h -2.645834 v -2.64584 l \"\n\t\t\t\t\"-2.645833,1e-5 v -2.64584 H 92.60417 v -2.64583 h -2.645834 v -2.64583 l -26.458334,0 0,37.04166\";\n\n\t\t\tb2Vec2 points[128];\n\n\t\t\tb2Vec2 offset = { -120.0f, -200.0f };\n\t\t\tfloat scale = 0.2f;\n\t\t\tint count = ParsePath( path, offset, points, 64, scale, false );\n\n\t\t\tb2SurfaceMaterial material = b2DefaultSurfaceMaterial();\n\t\t\tmaterial.customColor = b2_colorDarkSeaGreen;\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = count;\n\t\t\tchainDef.isLoop = true;\n\t\t\tchainDef.materials = &material;\n\t\t\tchainDef.materialCount = 1;\n\n\t\t\tb2CreateChain( groundId, &chainDef );\n\t\t}\n\n\t\tfloat gearRadius = 1.0f;\n\t\tfloat toothHalfWidth = 0.09f;\n\t\tfloat toothHalfHeight = 0.06f;\n\t\tfloat toothRadius = 0.03f;\n\t\tfloat linkHalfLength = 0.07f;\n\t\tfloat linkRadius = 0.05f;\n\t\tfloat linkCount = 40;\n\t\tfloat doorHalfHeight = 1.5f;\n\n\t\tb2Vec2 gearPosition1 = { -4.25f, 9.75f };\n\t\tb2Vec2 gearPosition2 = gearPosition1 + b2Vec2{ 2.0f, 1.0f };\n\t\tb2Vec2 linkAttachPosition = gearPosition2 + b2Vec2{ gearRadius + 2.0f * toothHalfWidth + toothRadius, 0.0f };\n\t\tb2Vec2 doorPosition = linkAttachPosition - b2Vec2{ 0.0f, 2.0f * linkCount * linkHalfLength + doorHalfHeight };\n\n\t\t{\n\t\t\tb2Vec2 position = gearPosition1;\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = position;\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.1f;\n\t\t\tshapeDef.material.customColor = b2_colorSaddleBrown;\n\t\t\tb2Circle circle = { b2Vec2_zero, gearRadius };\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\n\t\t\tint count = 16;\n\t\t\tfloat deltaAngle = 2.0f * B2_PI / 16;\n\t\t\tb2Rot dq = b2MakeRot( deltaAngle );\n\t\t\tb2Vec2 center = { gearRadius + toothHalfHeight, 0.0f };\n\t\t\tb2Rot rotation = b2Rot_identity;\n\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tb2Polygon tooth = b2MakeOffsetRoundedBox( toothHalfWidth, toothHalfHeight, center, rotation, toothRadius );\n\t\t\t\tshapeDef.material.customColor = b2_colorGray;\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &tooth );\n\n\t\t\t\trotation = b2MulRot( dq, rotation );\n\t\t\t\tcenter = b2RotateVector( rotation, { gearRadius + toothHalfHeight, 0.0f } );\n\t\t\t}\n\n\t\t\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\n\t\t\tm_motorTorque = 80.0f;\n\t\t\tm_motorSpeed = 0.0f;\n\t\t\tm_enableMotor = true;\n\n\t\t\trevoluteDef.base.bodyIdA = groundId;\n\t\t\trevoluteDef.base.bodyIdB = bodyId;\n\t\t\trevoluteDef.base.localFrameA.p = b2Body_GetLocalPoint( groundId, position );\n\t\t\trevoluteDef.base.localFrameB.p = b2Vec2_zero;\n\t\t\trevoluteDef.enableMotor = m_enableMotor;\n\t\t\trevoluteDef.maxMotorTorque = m_motorTorque;\n\t\t\trevoluteDef.motorSpeed = m_motorSpeed;\n\t\t\tm_driverId = b2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\t\t}\n\n\t\tb2BodyId followerId;\n\n\t\t{\n\t\t\tb2Vec2 position = gearPosition2;\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = position;\n\n\t\t\tfollowerId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.1f;\n\t\t\tshapeDef.material.customColor = b2_colorSaddleBrown;\n\t\t\tb2Circle circle = { b2Vec2_zero, gearRadius };\n\t\t\tb2CreateCircleShape( followerId, &shapeDef, &circle );\n\n\t\t\tint count = 16;\n\t\t\tfloat deltaAngle = 2.0f * B2_PI / 16;\n\t\t\tb2Rot dq = b2MakeRot( deltaAngle );\n\t\t\tb2Vec2 center = { gearRadius + toothHalfWidth, 0.0f };\n\t\t\tb2Rot rotation = b2Rot_identity;\n\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tb2Polygon tooth = b2MakeOffsetRoundedBox( toothHalfWidth, toothHalfHeight, center, rotation, toothRadius );\n\t\t\t\tshapeDef.material.customColor = b2_colorGray;\n\t\t\t\tb2CreatePolygonShape( followerId, &shapeDef, &tooth );\n\n\t\t\t\trotation = b2MulRot( dq, rotation );\n\t\t\t\tcenter = b2RotateVector( rotation, { gearRadius + toothHalfWidth, 0.0f } );\n\t\t\t}\n\n\t\t\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\n\t\t\trevoluteDef.base.bodyIdA = groundId;\n\t\t\trevoluteDef.base.bodyIdB = followerId;\n\t\t\trevoluteDef.base.localFrameA.p = b2Body_GetLocalPoint( groundId, position );\n\t\t\trevoluteDef.base.localFrameA.q = b2MakeRot( 0.25f * B2_PI );\n\t\t\trevoluteDef.base.localFrameB.p = b2Vec2_zero;\n\t\t\trevoluteDef.enableMotor = true;\n\t\t\trevoluteDef.maxMotorTorque = 0.5f;\n\t\t\trevoluteDef.lowerAngle = -0.3f * B2_PI;\n\t\t\trevoluteDef.upperAngle = 0.8f * B2_PI;\n\t\t\trevoluteDef.enableLimit = true;\n\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\t\t}\n\n\t\tb2BodyId lastLinkId;\n\t\t{\n\t\t\tb2Capsule capsule = { { 0.0f, -linkHalfLength }, { 0.0f, linkHalfLength }, linkRadius };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 2.0f;\n\t\t\tshapeDef.material.customColor = b2_colorLightSteelBlue;\n\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.maxMotorTorque = 0.05f;\n\t\t\tjointDef.enableMotor = true;\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2Vec2 position = linkAttachPosition + b2Vec2{ 0.0f, -linkHalfLength };\n\n\t\t\tint count = 40;\n\t\t\tb2BodyId prevBodyId = followerId;\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tbodyDef.position = position;\n\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\n\t\t\t\tb2Vec2 pivot = { position.x, position.y + linkHalfLength };\n\t\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\t\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\t\t\tjointDef.base.drawScale = 0.2f;\n\t\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\t\tposition.y -= 2.0f * linkHalfLength;\n\t\t\t\tprevBodyId = bodyId;\n\t\t\t}\n\n\t\t\tlastLinkId = prevBodyId;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = doorPosition;\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 0.15f, doorHalfHeight );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.1f;\n\t\t\tshapeDef.material.customColor = b2_colorDarkCyan;\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\t{\n\t\t\t\tb2Vec2 pivot = doorPosition + b2Vec2{ 0.0f, doorHalfHeight };\n\t\t\t\tb2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();\n\t\t\t\trevoluteDef.base.bodyIdA = lastLinkId;\n\t\t\t\trevoluteDef.base.bodyIdB = bodyId;\n\t\t\t\trevoluteDef.base.localFrameA.p = b2Body_GetLocalPoint( lastLinkId, pivot );\n\t\t\t\trevoluteDef.base.localFrameB.p = { 0.0f, doorHalfHeight };\n\t\t\t\trevoluteDef.enableMotor = true;\n\t\t\t\trevoluteDef.maxMotorTorque = 0.05f;\n\t\t\t\tb2CreateRevoluteJoint( m_worldId, &revoluteDef );\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tb2Vec2 localAxis = { 0.0f, 1.0f };\n\t\t\t\tb2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();\n\t\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( groundId, doorPosition );\n\t\t\t\tjointDef.base.localFrameA.q = b2MakeRotFromUnitVector( localAxis );\n\t\t\t\tjointDef.base.localFrameB.p = b2Vec2_zero;\n\t\t\t\tjointDef.base.localFrameB.q = b2MakeRotFromUnitVector( localAxis );\n\t\t\t\tjointDef.maxMotorForce = 0.2f;\n\t\t\t\tjointDef.enableMotor = true;\n\t\t\t\tjointDef.base.collideConnected = true;\n\t\t\t\tb2CreatePrismaticJoint( m_worldId, &jointDef );\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.rollingResistance = 0.3f;\n\n\t\t\tb2HexColor colors[5] = {\n\t\t\t\tb2_colorGray, b2_colorGainsboro, b2_colorLightGray, b2_colorLightSlateGray, b2_colorDarkGray,\n\t\t\t};\n\n\t\t\tfloat y = 4.25f;\n\t\t\tint xCount = 10, yCount = 20;\n\t\t\tfor ( int i = 0; i < yCount; ++i )\n\t\t\t{\n\t\t\t\tfloat x = -3.15f;\n\t\t\t\tfor ( int j = 0; j < xCount; ++j )\n\t\t\t\t{\n\t\t\t\t\tbodyDef.position = { x, y };\n\t\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\t\tb2Polygon poly = RandomPolygon( 0.1f );\n\t\t\t\t\tpoly.radius = RandomFloatRange( 0.01f, 0.02f );\n\n\t\t\t\t\tint colorIndex = RandomIntRange( 0, 4 );\n\t\t\t\t\tshapeDef.material.customColor = colors[colorIndex];\n\n\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &poly );\n\t\t\t\t\tx += 0.2f;\n\t\t\t\t}\n\n\t\t\t\ty += 0.2f;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 120.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 25.0f ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Gear Lift\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Checkbox( \"Motor\", &m_enableMotor ) )\n\t\t{\n\t\t\tb2RevoluteJoint_EnableMotor( m_driverId, m_enableMotor );\n\t\t\tb2Joint_WakeBodies( m_driverId );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Max Torque\", &m_motorTorque, 0.0f, 100.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tb2RevoluteJoint_SetMaxMotorTorque( m_driverId, m_motorTorque );\n\t\t\tb2Joint_WakeBodies( m_driverId );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Speed\", &m_motorSpeed, -0.3f, 0.3f, \"%.2f\" ) )\n\t\t{\n\t\t\tb2RevoluteJoint_SetMotorSpeed( m_driverId, m_motorSpeed );\n\t\t\tb2Joint_WakeBodies( m_driverId );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_A ) )\n\t\t{\n\t\t\tm_motorSpeed = b2MaxFloat( -0.3f, m_motorSpeed - 0.01f );\n\t\t\tb2RevoluteJoint_SetMotorSpeed( m_driverId, m_motorSpeed );\n\t\t\tb2Joint_WakeBodies( m_driverId );\n\t\t}\n\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_D ) )\n\t\t{\n\t\t\tm_motorSpeed = b2MinFloat( 0.3f, m_motorSpeed + 0.01f );\n\t\t\tb2RevoluteJoint_SetMotorSpeed( m_driverId, m_motorSpeed );\n\t\t\tb2Joint_WakeBodies( m_driverId );\n\t\t}\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new GearLift( context );\n\t}\n\n\tb2JointId m_driverId;\n\tfloat m_motorTorque;\n\tfloat m_motorSpeed;\n\tbool m_enableMotor;\n};\n\nstatic int sampleGearLift = RegisterSample( \"Joints\", \"Gear Lift\", GearLift::Create );\n\n// A top down door\nclass Door : public Sample\n{\npublic:\n\texplicit Door( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 4.0f;\n\t\t}\n\n\t\tb2BodyId groundId = b2_nullBodyId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, 0.0f };\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\tm_enableLimit = true;\n\t\tm_impulse = 50000.0f;\n\t\tm_translationError = 0.0f;\n\t\tm_jointHertz = 240.0f;\n\t\tm_jointDampingRatio = 1.0f;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 0.0f, 1.5f };\n\t\t\tbodyDef.gravityScale = 0.0f;\n\n\t\t\tm_doorId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 1000.0f;\n\n\t\t\tb2Polygon box = b2MakeBox( 0.1f, 1.5f );\n\t\t\tb2CreatePolygonShape( m_doorId, &shapeDef, &box );\n\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = m_doorId;\n\t\t\tjointDef.base.localFrameA.p = { 0.0f, 0.0f };\n\t\t\tjointDef.base.localFrameB.p = { 0.0f, -1.5f };\n\t\t\tjointDef.base.constraintHertz = m_jointHertz;\n\t\t\tjointDef.base.constraintDampingRatio = m_jointDampingRatio;\n\t\t\tjointDef.targetAngle = 0.0f;\n\t\t\tjointDef.enableSpring = true;\n\t\t\tjointDef.hertz = 1.0f;\n\t\t\tjointDef.dampingRatio = 0.5f;\n\t\t\tjointDef.motorSpeed = 0.0f;\n\t\t\tjointDef.maxMotorTorque = 0.0f;\n\t\t\tjointDef.enableMotor = false;\n\t\t\tjointDef.lowerAngle = -0.5f * B2_PI;\n\t\t\tjointDef.upperAngle = 0.5f * B2_PI;\n\t\t\tjointDef.enableLimit = m_enableLimit;\n\n\t\t\tm_jointId = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 220.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Door\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Button( \"impulse\" ) )\n\t\t{\n\t\t\tb2Vec2 p = b2Body_GetWorldPoint( m_doorId, { 0.0f, 1.5f } );\n\t\t\tb2Body_ApplyLinearImpulse( m_doorId, { m_impulse, 0.0f }, p, true );\n\t\t\tm_translationError = 0.0f;\n\t\t}\n\n\t\tImGui::SliderFloat( \"magnitude\", &m_impulse, 1000.0f, 100000.0f, \"%.0f\" );\n\n\t\tif ( ImGui::Checkbox( \"limit\", &m_enableLimit ) )\n\t\t{\n\t\t\tb2RevoluteJoint_EnableLimit( m_jointId, m_enableLimit );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"hertz\", &m_jointHertz, 15.0f, 480.0f, \"%.0f\" ) )\n\t\t{\n\t\t\tb2Joint_SetConstraintTuning( m_jointId, m_jointHertz, m_jointDampingRatio );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"damping\", &m_jointDampingRatio, 0.0f, 10.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tb2Joint_SetConstraintTuning( m_jointId, m_jointHertz, m_jointDampingRatio );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2Vec2 p = b2Body_GetWorldPoint( m_doorId, { 0.0f, 1.5f } );\n\t\tDrawPoint( m_draw, p, 5.0f, b2_colorDarkKhaki );\n\n\t\tfloat translationError = b2Joint_GetLinearSeparation( m_jointId );\n\t\tm_translationError = b2MaxFloat( m_translationError, translationError );\n\n\t\tDrawTextLine( \"translation error = %g\", m_translationError );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Door( context );\n\t}\n\n\tb2BodyId m_doorId;\n\tb2JointId m_jointId;\n\tfloat m_impulse;\n\tfloat m_translationError;\n\tfloat m_jointHertz;\n\tfloat m_jointDampingRatio;\n\tbool m_enableLimit;\n};\n\nstatic int sampleDoor = RegisterSample( \"Joints\", \"Door\", Door::Create );\n\nclass ScaleRagdoll : public Sample\n{\npublic:\n\texplicit ScaleRagdoll( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 4.5f };\n\t\t\tm_context->camera.zoom = 6.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 20.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tm_scale = 1.0f;\n\n\t\tm_human = {};\n\n\t\tSpawn();\n\t}\n\n\tvoid Spawn()\n\t{\n\t\tfloat jointFrictionTorque = 0.03f;\n\t\tfloat jointHertz = 1.0f;\n\t\tfloat jointDampingRatio = 0.5f;\n\t\tCreateHuman( &m_human, m_worldId, { 0.0f, 5.0f }, m_scale, jointFrictionTorque, jointHertz, jointDampingRatio, 1, nullptr,\n\t\t\t\t\t false );\n\t\tHuman_ApplyRandomAngularImpulse( &m_human, 10.0f );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 4.0f * fontSize;\n\t\tImGui::SetNextWindowPos( { 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize }, ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( { 20.0f * fontSize, height } );\n\n\t\tImGui::Begin( \"Scale Ragdoll\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 15.0f * fontSize );\n\n\t\tif ( ImGui::SliderFloat( \"Scale\", &m_scale, 0.1f, 10.0f, \"%3.2f\", ImGuiSliderFlags_ClampOnInput ) )\n\t\t{\n\t\t\tHuman_SetScale( &m_human, m_scale );\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ScaleRagdoll( context );\n\t}\n\n\tHuman m_human;\n\tfloat m_scale;\n};\n\nstatic int sampleScaleRagdoll = RegisterSample( \"Joints\", \"Scale Ragdoll\", ScaleRagdoll::Create );\n"
  },
  {
    "path": "samples/sample_robustness.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"draw.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n\n#include <imgui.h>\n#include <stdlib.h>\n\n// Pyramid with heavy box on top\nclass HighMassRatio1 : public Sample\n{\npublic:\n\texplicit HighMassRatio1( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 3.0f, 14.0f };\n\t\t\tm_context->camera.zoom = 25.0f;\n\t\t}\n\n\t\tfloat extent = 1.0f;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 50.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2Polygon box = b2MakeBox( extent, extent );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tfor ( int j = 0; j < 3; ++j )\n\t\t\t{\n\t\t\t\tint count = 10;\n\t\t\t\tfloat offset = -20.0f * extent + 2.0f * ( count + 1.0f ) * extent * j;\n\t\t\t\tfloat y = extent;\n\t\t\t\twhile ( count > 0 )\n\t\t\t\t{\n\t\t\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat coeff = i - 0.5f * count;\n\n\t\t\t\t\t\tfloat yy = count == 1 ? y + 2.0f : y;\n\t\t\t\t\t\tbodyDef.position = { 2.0f * coeff * extent + offset, yy };\n\t\t\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\t\t\tshapeDef.density = count == 1 ? ( j + 1.0f ) * 100.0f : 1.0f;\n\t\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\t\t\t}\n\n\t\t\t\t\t--count;\n\t\t\t\t\ty += 2.0f * extent;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new HighMassRatio1( context );\n\t}\n};\n\nstatic int sampleHighMassRatio1 = RegisterSample( \"Robustness\", \"HighMassRatio1\", HighMassRatio1::Create );\n\n// Big box on small boxes\nclass HighMassRatio2 : public Sample\n{\npublic:\n\texplicit HighMassRatio2( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 16.5f };\n\t\t\tm_context->camera.zoom = 25.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 50.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tfloat extent = 1.0f;\n\t\t\tb2Polygon smallBox = b2MakeBox( 0.5f * extent, 0.5f * extent );\n\t\t\tb2Polygon bigBox = b2MakeBox( 10.0f * extent, 10.0f * extent );\n\n\t\t\t{\n\t\t\t\tbodyDef.position = { -9.0f * extent, 0.5f * extent };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &smallBox );\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tbodyDef.position = { 9.0f * extent, 0.5f * extent };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &smallBox );\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tbodyDef.position = { 0.0f, ( 10.0f + 16.0f ) * extent };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &bigBox );\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new HighMassRatio2( context );\n\t}\n};\n\nstatic int sampleHighMassRatio2 = RegisterSample( \"Robustness\", \"HighMassRatio2\", HighMassRatio2::Create );\n\n// Big box on small triangles\nclass HighMassRatio3 : public Sample\n{\npublic:\n\texplicit HighMassRatio3( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 16.5f };\n\t\t\tm_context->camera.zoom = 25.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 50.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tfloat extent = 1.0f;\n\t\t\tb2Vec2 points[3] = { { -0.5f * extent, 0.0f }, { 0.5f * extent, 0.0f }, { 0.0f, 1.0f * extent } };\n\t\t\tb2Hull hull = b2ComputeHull( points, 3 );\n\t\t\tb2Polygon smallTriangle = b2MakePolygon( &hull, 0.0f );\n\t\t\tb2Polygon bigBox = b2MakeBox( 10.0f * extent, 10.0f * extent );\n\n\t\t\t{\n\t\t\t\tbodyDef.position = { -9.0f * extent, 0.5f * extent };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &smallTriangle );\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tbodyDef.position = { 9.0f * extent, 0.5f * extent };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &smallTriangle );\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tbodyDef.position = { 0.0f, ( 10.0f + 4.0f ) * extent };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &bigBox );\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new HighMassRatio3( context );\n\t}\n};\n\nstatic int sampleHighMassRatio3 = RegisterSample( \"Robustness\", \"HighMassRatio3\", HighMassRatio3::Create );\n\nclass OverlapRecovery : public Sample\n{\npublic:\n\texplicit OverlapRecovery( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 2.5f };\n\t\t\tm_context->camera.zoom = 3.75f;\n\t\t}\n\n\t\tm_bodyIds = nullptr;\n\t\tm_bodyCount = 0;\n\t\tm_baseCount = 4;\n\t\tm_overlap = 0.25f;\n\t\tm_extent = 0.5f;\n\t\tm_speed = 3.0f;\n\t\tm_hertz = 30.0f;\n\t\tm_dampingRatio = 10.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } };\n\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\tCreateScene();\n\t}\n\n\t~OverlapRecovery() override\n\t{\n\t\tfree( m_bodyIds );\n\t}\n\n\tvoid CreateScene()\n\t{\n\t\tfor ( int i = 0; i < m_bodyCount; ++i )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t}\n\n\t\tb2World_SetContactTuning( m_worldId, m_hertz, m_dampingRatio, m_speed );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tb2Polygon box = b2MakeBox( m_extent, m_extent );\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\n\t\tm_bodyCount = m_baseCount * ( m_baseCount + 1 ) / 2;\n\t\tm_bodyIds = (b2BodyId*)realloc( m_bodyIds, m_bodyCount * sizeof( b2BodyId ) );\n\n\t\tint bodyIndex = 0;\n\t\tfloat fraction = 1.0f - m_overlap;\n\t\tfloat y = m_extent;\n\t\tfor ( int i = 0; i < m_baseCount; ++i )\n\t\t{\n\t\t\tfloat x = fraction * m_extent * ( i - m_baseCount );\n\t\t\tfor ( int j = i; j < m_baseCount; ++j )\n\t\t\t{\n\t\t\t\tbodyDef.position = { x, y };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\t\tm_bodyIds[bodyIndex++] = bodyId;\n\n\t\t\t\tx += 2.0f * fraction * m_extent;\n\t\t\t}\n\n\t\t\ty += 2.0f * fraction * m_extent;\n\t\t}\n\n\t\tassert( bodyIndex == m_bodyCount );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 210.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 220.0f, height ) );\n\n\t\tImGui::Begin( \"Overlap Recovery\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 100.0f );\n\n\t\tbool changed = false;\n\t\tchanged = changed || ImGui::SliderFloat( \"Extent\", &m_extent, 0.1f, 1.0f, \"%.1f\" );\n\t\tchanged = changed || ImGui::SliderInt( \"Base Count\", &m_baseCount, 1, 10 );\n\t\tchanged = changed || ImGui::SliderFloat( \"Overlap\", &m_overlap, 0.0f, 1.0f, \"%.2f\" );\n\t\tchanged = changed || ImGui::SliderFloat( \"Speed\", &m_speed, 0.0f, 10.0f, \"%.1f\" );\n\t\tchanged = changed || ImGui::SliderFloat( \"Hertz\", &m_hertz, 0.0f, 240.0f, \"%.f\" );\n\t\tchanged = changed || ImGui::SliderFloat( \"Damping Ratio\", &m_dampingRatio, 0.0f, 20.0f, \"%.1f\" );\n\t\tchanged = changed || ImGui::Button( \"Reset Scene\" );\n\n\t\tif ( changed )\n\t\t{\n\t\t\tCreateScene();\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new OverlapRecovery( context );\n\t}\n\n\tb2BodyId* m_bodyIds;\n\tint m_bodyCount;\n\tint m_baseCount;\n\tfloat m_overlap;\n\tfloat m_extent;\n\tfloat m_speed;\n\tfloat m_hertz;\n\tfloat m_dampingRatio;\n};\n\nstatic int sampleOverlapRecovery = RegisterSample( \"Robustness\", \"Overlap Recovery\", OverlapRecovery::Create );\n\nclass TinyPyramid : public Sample\n{\npublic:\n\texplicit TinyPyramid( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.8f };\n\t\t\tm_context->camera.zoom = 1.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 5.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tm_extent = 0.025f;\n\t\t\tint baseCount = 30;\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2Polygon box = b2MakeSquare( m_extent );\n\n\t\t\tfor ( int i = 0; i < baseCount; ++i )\n\t\t\t{\n\t\t\t\tfloat y = ( 2.0f * i + 1.0f ) * m_extent;\n\n\t\t\t\tfor ( int j = i; j < baseCount; ++j )\n\t\t\t\t{\n\t\t\t\t\tfloat x = ( i + 1.0f ) * m_extent + 2.0f * ( j - i ) * m_extent - baseCount * m_extent;\n\t\t\t\t\tbodyDef.position = { x, y };\n\n\t\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tDrawTextLine( \"%.1fcm squares\", 200.0f * m_extent );\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new TinyPyramid( context );\n\t}\n\n\tfloat m_extent;\n};\n\nstatic int sampleTinyPyramid = RegisterSample( \"Robustness\", \"Tiny Pyramid\", TinyPyramid::Create );\n\n// High gravity and high mass ratio. This shows how to tune contact and joint stiffness values to\n// achieve an improved result at a high sub-step count.\n// There is still a fair bit of bounce with some settings.\nclass Cart : public Sample\n{\npublic:\n\texplicit Cart( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 1.0f };\n\t\t\tm_context->camera.zoom = 1.5f;\n\t\t\tm_context->subStepCount = 12;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, -1.0f };\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon groundBox = b2MakeBox( 20.0f, 1.0f );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &groundBox );\n\t\t}\n\n\t\tb2World_SetGravity( m_worldId, { 0, -22.0f } );\n\n\t\tm_contactHertz = 240.0f;\n\t\tm_contactDampingRatio = 10.0f;\n\t\tm_contactSpeed = 0.5f;\n\t\tb2World_SetContactTuning( m_worldId, m_contactHertz, m_contactDampingRatio, m_contactSpeed );\n\n\t\tm_constraintHertz = 240.0f;\n\t\tm_constraintDampingRatio = 0.0f;\n\n\t\tm_chassisId = {};\n\t\tm_wheelId1 = {};\n\t\tm_wheelId2 = {};\n\t\tm_jointId1 = {};\n\t\tm_jointId2 = {};\n\n\t\tCreateScene();\n\t}\n\n\tvoid CreateScene()\n\t{\n\t\tif ( B2_IS_NON_NULL( m_chassisId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_chassisId );\n\t\t}\n\n\t\tif ( B2_IS_NON_NULL( m_wheelId1 ) )\n\t\t{\n\t\t\tb2DestroyBody( m_wheelId1 );\n\t\t}\n\n\t\tif ( B2_IS_NON_NULL( m_wheelId2 ) )\n\t\t{\n\t\t\tb2DestroyBody( m_wheelId2 );\n\t\t}\n\n\t\tfloat yBase = 2.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { 0.0f, yBase };\n\t\tm_chassisId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1000.0f;\n\n\t\tb2Polygon box = b2MakeOffsetBox( 1.0f, 0.25f, { 0.0f, 0.25f }, b2Rot_identity );\n\t\tb2CreatePolygonShape( m_chassisId, &shapeDef, &box );\n\n\t\tshapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.rollingResistance = 0.02f;\n\t\tshapeDef.density = 50.0f;\n\n\t\tb2Circle circle = { b2Vec2_zero, 0.1f };\n\t\tbodyDef.position = { -0.9f, yBase - 0.15f };\n\t\tm_wheelId1 = b2CreateBody( m_worldId, &bodyDef );\n\t\tb2CreateCircleShape( m_wheelId1, &shapeDef, &circle );\n\n\t\tbodyDef.position = { 0.9f, yBase - 0.15f };\n\t\tm_wheelId2 = b2CreateBody( m_worldId, &bodyDef );\n\t\tb2CreateCircleShape( m_wheelId2, &shapeDef, &circle );\n\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.constraintHertz = 120.0f;\n\t\tjointDef.base.constraintDampingRatio = 0.0f;\n\n\t\tjointDef.base.bodyIdA = m_chassisId;\n\t\tjointDef.base.bodyIdB = m_wheelId1;\n\t\tjointDef.base.localFrameA.p = { -0.9f, -0.15f };\n\t\tjointDef.base.localFrameB.p = { 0.0f, 0.0f };\n\n\t\tm_jointId1 = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\tb2Joint_SetConstraintTuning( m_jointId1, m_constraintHertz, m_constraintDampingRatio );\n\n\t\tjointDef.base.bodyIdA = m_chassisId;\n\t\tjointDef.base.bodyIdB = m_wheelId2;\n\t\tjointDef.base.localFrameA.p = { 0.9f, -0.15f };\n\t\tjointDef.base.localFrameB.p = { 0.0f, 0.0f };\n\n\t\tm_jointId2 = b2CreateRevoluteJoint( m_worldId, &jointDef );\n\t\tb2Joint_SetConstraintTuning( m_jointId2, m_constraintHertz, m_constraintDampingRatio );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 240.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 320.0f, height ) );\n\n\t\tImGui::Begin( \"Cart\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 200.0f );\n\n\t\tbool changed = false;\n\t\tImGui::Text( \"Contact\" );\n\t\tchanged = changed || ImGui::SliderFloat( \"Hertz##contact\", &m_contactHertz, 0.0f, 240.0f, \"%.f\" );\n\t\tchanged = changed || ImGui::SliderFloat( \"Damping Ratio##contact\", &m_contactDampingRatio, 0.0f, 100.0f, \"%.f\" );\n\t\tchanged = changed || ImGui::SliderFloat( \"Speed\", &m_contactSpeed, 0.0f, 5.0f, \"%.1f\" );\n\n\t\tif ( changed )\n\t\t{\n\t\t\tb2World_SetContactTuning( m_worldId, m_contactHertz, m_contactDampingRatio, m_contactSpeed );\n\t\t\tCreateScene();\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tchanged = false;\n\t\tImGui::Text( \"Joint\" );\n\t\tchanged = changed || ImGui::SliderFloat( \"Hertz##joint\", &m_constraintHertz, 0.0f, 240.0f, \"%.f\" );\n\t\tchanged = changed || ImGui::SliderFloat( \"Damping Ratio##joint\", &m_constraintDampingRatio, 0.0f, 20.0f, \"%.f\" );\n\n\t\tImGui::Separator();\n\n\t\tchanged = changed || ImGui::Button( \"Reset Scene\" );\n\n\t\tif ( changed )\n\t\t{\n\t\t\tb2Joint_SetConstraintTuning( m_jointId1, m_constraintHertz, m_constraintDampingRatio );\n\t\t\tb2Joint_SetConstraintTuning( m_jointId2, m_constraintHertz, m_constraintDampingRatio );\n\t\t\tCreateScene();\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Cart( context );\n\t}\n\n\tb2BodyId m_chassisId;\n\tb2BodyId m_wheelId1;\n\tb2BodyId m_wheelId2;\n\tb2JointId m_jointId1;\n\tb2JointId m_jointId2;\n\n\tfloat m_contactHertz;\n\tfloat m_contactDampingRatio;\n\tfloat m_contactSpeed;\n\tfloat m_constraintHertz;\n\tfloat m_constraintDampingRatio;\n};\n\nstatic int sampleCart = RegisterSample( \"Robustness\", \"Cart\", Cart::Create );\n\n// Ensure prismatic joint stability when highly distorted\nclass MultiplePrismatic : public Sample\n{\npublic:\n\texplicit MultiplePrismatic( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 8.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t}\n\n\t\tb2BodyId groundId;\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Polygon box = b2MakeBox( 0.5f, 0.5f );\n\t\tb2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef();\n\t\tjointDef.base.bodyIdA = groundId;\n\t\tjointDef.base.localFrameA.p = { 0.0f, 0.0f };\n\t\tjointDef.base.localFrameB.p = { 0.0f, -0.6f };\n\t\tjointDef.base.drawScale = 1.0f;\n\t\tjointDef.base.constraintHertz = 240.0f;\n\t\tjointDef.lowerTranslation = -6.0f;\n\t\tjointDef.upperTranslation = 6.0f;\n\t\tjointDef.enableLimit = true;\n\n\t\tfor ( int i = 0; i < 6; ++i )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, 0.6f + 1.2f * i };\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tb2CreatePrismaticJoint( m_worldId, &jointDef );\n\n\t\t\tjointDef.base.bodyIdA = bodyId;\n\t\t\tjointDef.base.localFrameA.p = { 0.0f, 0.6f };\n\t\t}\n\n\t\t// Increase the mouse force\n\t\tm_mouseForceScale = 100000.0f;\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new MultiplePrismatic( context );\n\t}\n};\n\nstatic int sampleMultiplePrismatic = RegisterSample( \"Robustness\", \"Multiple Prismatic\", MultiplePrismatic::Create );\n"
  },
  {
    "path": "samples/sample_shapes.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"draw.h\"\n#include \"random.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n#include <imgui.h>\n#include <stdio.h>\n#include <vector>\n\n// extern \"C\" int b2_toiCalls;\n// extern \"C\" int b2_toiHitCount;\n\nclass ChainShape : public Sample\n{\npublic:\n\tenum ShapeType\n\t{\n\t\te_circleShape = 0,\n\t\te_capsuleShape,\n\t\te_boxShape\n\t};\n\n\texplicit ChainShape( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 1.75f;\n\t\t}\n\n\t\tm_groundId = b2_nullBodyId;\n\t\tm_bodyId = b2_nullBodyId;\n\t\tm_chainId = b2_nullChainId;\n\t\tm_shapeId = b2_nullShapeId;\n\t\tm_shapeType = e_circleShape;\n\n\t\tm_material = b2DefaultSurfaceMaterial();\n\t\tm_material.friction = 0.2f;\n\t\tm_material.customColor = b2_colorSteelBlue;\n\t\tm_material.userMaterialId = 42;\n\n\t\tCreateScene();\n\t\tLaunch();\n\t}\n\n\tvoid CreateScene()\n\t{\n\t\tif ( B2_IS_NON_NULL( m_groundId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_groundId );\n\t\t}\n\n\t\t// https://betravis.github.io/shape-tools/path-to-polygon/\n\t\t// b2Vec2 points[] = {{-20.58325, 14.54175}, {-21.90625, 15.8645},\t\t {-24.552, 17.1875},\n\t\t//\t\t\t\t   {-27.198, 11.89575},\t  {-29.84375, 15.8645},\t\t {-29.84375, 21.15625},\n\t\t//\t\t\t\t   {-25.875, 23.802},\t  {-20.58325, 25.125},\t\t {-25.875, 29.09375},\n\t\t//\t\t\t\t   {-20.58325, 31.7395},  {-11.0089998, 23.2290001}, {-8.67700005, 21.15625},\n\t\t//\t\t\t\t   {-6.03125, 21.15625},  {-7.35424995, 29.09375},\t {-3.38549995, 29.09375},\n\t\t//\t\t\t\t   {1.90625, 30.41675},\t  {5.875, 17.1875},\t\t\t {11.16675, 25.125},\n\t\t//\t\t\t\t   {9.84375, 29.09375},\t  {13.8125, 31.7395},\t\t {21.75, 30.41675},\n\t\t//\t\t\t\t   {28.3644981, 26.448},  {25.71875, 18.5105},\t\t {24.3957481, 13.21875},\n\t\t//\t\t\t\t   {17.78125, 11.89575},  {15.1355, 7.92700005},\t {5.875, 9.25},\n\t\t//\t\t\t\t   {1.90625, 11.89575},\t  {-3.25, 11.89575},\t\t {-3.25, 9.9375},\n\t\t//\t\t\t\t   {-4.70825005, 9.25},\t  {-8.67700005, 9.25},\t\t {-11.323, 11.89575},\n\t\t//\t\t\t\t   {-13.96875, 11.89575}, {-15.29175, 14.54175},\t {-19.2605, 14.54175}};\n\n\t\tb2Vec2 points[] = {\n\t\t\t{ -56.885498, 12.8985004 },\t  { -56.885498, 16.2057495 },\t{ 56.885498, 16.2057495 },\t { 56.885498, -16.2057514 },\n\t\t\t{ 51.5935059, -16.2057514 },  { 43.6559982, -10.9139996 },\t{ 35.7184982, -10.9139996 }, { 27.7809982, -10.9139996 },\n\t\t\t{ 21.1664963, -14.2212505 },  { 11.9059982, -16.2057514 },\t{ 0, -16.2057514 },\t\t\t { -10.5835037, -14.8827496 },\n\t\t\t{ -17.1980019, -13.5597477 }, { -21.1665001, -12.2370014 }, { -25.1355019, -9.5909977 }, { -31.75, -3.63799858 },\n\t\t\t{ -38.3644981, 6.2840004 },\t  { -42.3334999, 9.59125137 },\t{ -47.625, 11.5755005 },\t { -56.885498, 12.8985004 },\n\t\t};\n\n\t\tint count = sizeof( points ) / sizeof( points[0] );\n\n\t\t// float scale = 0.25f;\n\t\t// b2Vec2 lower = {FLT_MAX, FLT_MAX};\n\t\t// b2Vec2 upper = {-FLT_MAX, -FLT_MAX};\n\t\t// for (int i = 0; i < count; ++i)\n\t\t//{\n\t\t//\tpoints[i].x = 2.0f * scale * points[i].x;\n\t\t//\tpoints[i].y = -scale * points[i].y;\n\n\t\t//\tlower = b2Min(lower, points[i]);\n\t\t//\tupper = b2Max(upper, points[i]);\n\t\t//}\n\n\t\t// b2Vec2 center = b2MulSV(0.5f, b2Add(lower, upper));\n\t\t// for (int i = 0; i < count; ++i)\n\t\t//{\n\t\t//\tpoints[i] = b2Sub(points[i], center);\n\t\t// }\n\n\t\t// for (int i = 0; i < count / 2; ++i)\n\t\t//{\n\t\t//\tb2Vec2 temp = points[i];\n\t\t//\tpoints[i] = points[count - 1 - i];\n\t\t//\tpoints[count - 1 - i] = temp;\n\t\t// }\n\n\t\t// printf(\"{\");\n\t\t// for (int i = 0; i < count; ++i)\n\t\t//{\n\t\t//\tprintf(\"{%.9g, %.9g},\", points[i].x, points[i].y);\n\t\t// }\n\t\t// printf(\"};\\n\");\n\n\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\tchainDef.points = points;\n\t\tchainDef.count = count;\n\t\tchainDef.materials = &m_material;\n\t\tchainDef.materialCount = 1;\n\t\tchainDef.isLoop = true;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tm_groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tm_chainId = b2CreateChain( m_groundId, &chainDef );\n\t}\n\n\tvoid Launch()\n\t{\n\t\tif ( B2_IS_NON_NULL( m_bodyId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyId );\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { -55.0f, 13.5f };\n\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material = m_material;\n\n\t\tif ( m_shapeType == e_circleShape )\n\t\t{\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tm_shapeId = b2CreateCircleShape( m_bodyId, &shapeDef, &circle );\n\t\t}\n\t\telse if ( m_shapeType == e_capsuleShape )\n\t\t{\n\t\t\tb2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\t\t\tm_shapeId = b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfloat h = 0.5f;\n\t\t\tb2Polygon box = b2MakeBox( h, h );\n\t\t\tm_shapeId = b2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t// b2_toiCalls = 0;\n\t\t// b2_toiHitCount = 0;\n\n\t\tm_stepCount = 0;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 155.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Chain Shape\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tconst char* shapeTypes[] = { \"Circle\", \"Capsule\", \"Box\" };\n\t\tint shapeType = int( m_shapeType );\n\t\tif ( ImGui::Combo( \"Shape\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) )\n\t\t{\n\t\t\tm_shapeType = ShapeType( shapeType );\n\t\t\tLaunch();\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Friction\", &m_material.friction, 0.0f, 1.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tb2Shape_SetSurfaceMaterial( m_shapeId, &m_material );\n\t\t\tb2Chain_SetSurfaceMaterial( m_chainId, &m_material, 1 );\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Restitution\", &m_material.restitution, 0.0f, 2.0f, \"%.1f\" ) )\n\t\t{\n\t\t\tb2Shape_SetSurfaceMaterial( m_shapeId, &m_material );\n\t\t}\n\n\t\tif ( ImGui::Button( \"Launch\" ) )\n\t\t{\n\t\t\tLaunch();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tDrawLine( m_draw, b2Vec2_zero, { 0.5f, 0.0f }, b2_colorRed );\n\t\tDrawLine( m_draw, b2Vec2_zero, { 0.0f, 0.5f }, b2_colorGreen );\n\n\t\t// DrawTextLine( \"toi calls, hits = %d, %d\", b2_toiCalls, b2_toiHitCount );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ChainShape( context );\n\t}\n\n\tb2BodyId m_groundId;\n\tb2BodyId m_bodyId;\n\tb2ChainId m_chainId;\n\tShapeType m_shapeType;\n\tb2ShapeId m_shapeId;\n\tb2SurfaceMaterial m_material;\n};\n\nstatic int sampleChainShape = RegisterSample( \"Shapes\", \"Chain Shape\", ChainShape::Create );\n\n// This sample shows how careful creation of compound shapes leads to better simulation and avoids\n// objects getting stuck.\n// This also shows how to get the combined AABB for the body.\nclass CompoundShapes : public Sample\n{\npublic:\n\texplicit CompoundShapes( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 6.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { 50.0f, 0.0f }, { -50.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Table 1\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -15.0f, 1.0f };\n\t\t\tm_table1Id = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon top = b2MakeOffsetBox( 3.0f, 0.5f, { 0.0f, 3.5f }, b2Rot_identity );\n\t\t\tb2Polygon leftLeg = b2MakeOffsetBox( 0.5f, 1.5f, { -2.5f, 1.5f }, b2Rot_identity );\n\t\t\tb2Polygon rightLeg = b2MakeOffsetBox( 0.5f, 1.5f, { 2.5f, 1.5f }, b2Rot_identity );\n\n\t\t\tb2CreatePolygonShape( m_table1Id, &shapeDef, &top );\n\t\t\tb2CreatePolygonShape( m_table1Id, &shapeDef, &leftLeg );\n\t\t\tb2CreatePolygonShape( m_table1Id, &shapeDef, &rightLeg );\n\t\t}\n\n\t\t// Table 2\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -5.0f, 1.0f };\n\t\t\tm_table2Id = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon top = b2MakeOffsetBox( 3.0f, 0.5f, { 0.0f, 3.5f }, b2Rot_identity );\n\t\t\tb2Polygon leftLeg = b2MakeOffsetBox( 0.5f, 2.0f, { -2.5f, 2.0f }, b2Rot_identity );\n\t\t\tb2Polygon rightLeg = b2MakeOffsetBox( 0.5f, 2.0f, { 2.5f, 2.0f }, b2Rot_identity );\n\n\t\t\tb2CreatePolygonShape( m_table2Id, &shapeDef, &top );\n\t\t\tb2CreatePolygonShape( m_table2Id, &shapeDef, &leftLeg );\n\t\t\tb2CreatePolygonShape( m_table2Id, &shapeDef, &rightLeg );\n\t\t}\n\n\t\t// Spaceship 1\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 5.0f, 1.0f };\n\t\t\tm_ship1Id = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Vec2 vertices[3];\n\n\t\t\tvertices[0] = { -2.0f, 0.0f };\n\t\t\tvertices[1] = { 0.0f, 4.0f / 3.0f };\n\t\t\tvertices[2] = { 0.0f, 4.0f };\n\t\t\tb2Hull hull = b2ComputeHull( vertices, 3 );\n\t\t\tb2Polygon left = b2MakePolygon( &hull, 0.0f );\n\n\t\t\tvertices[0] = { 2.0f, 0.0f };\n\t\t\tvertices[1] = { 0.0f, 4.0f / 3.0f };\n\t\t\tvertices[2] = { 0.0f, 4.0f };\n\t\t\thull = b2ComputeHull( vertices, 3 );\n\t\t\tb2Polygon right = b2MakePolygon( &hull, 0.0f );\n\n\t\t\tb2CreatePolygonShape( m_ship1Id, &shapeDef, &left );\n\t\t\tb2CreatePolygonShape( m_ship1Id, &shapeDef, &right );\n\t\t}\n\n\t\t// Spaceship 2\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 15.0f, 1.0f };\n\t\t\tm_ship2Id = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Vec2 vertices[3];\n\n\t\t\tvertices[0] = { -2.0f, 0.0f };\n\t\t\tvertices[1] = { 1.0f, 2.0f };\n\t\t\tvertices[2] = { 0.0f, 4.0f };\n\t\t\tb2Hull hull = b2ComputeHull( vertices, 3 );\n\t\t\tb2Polygon left = b2MakePolygon( &hull, 0.0f );\n\n\t\t\tvertices[0] = { 2.0f, 0.0f };\n\t\t\tvertices[1] = { -1.0f, 2.0f };\n\t\t\tvertices[2] = { 0.0f, 4.0f };\n\t\t\thull = b2ComputeHull( vertices, 3 );\n\t\t\tb2Polygon right = b2MakePolygon( &hull, 0.0f );\n\n\t\t\tb2CreatePolygonShape( m_ship2Id, &shapeDef, &left );\n\t\t\tb2CreatePolygonShape( m_ship2Id, &shapeDef, &right );\n\t\t}\n\n\t\tm_drawBodyAABBs = false;\n\t}\n\n\tvoid Spawn()\n\t{\n\t\t// Table 1 obstruction\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = b2Body_GetPosition( m_table1Id );\n\t\t\tbodyDef.rotation = b2Body_GetRotation( m_table1Id );\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 4.0f, 0.1f, { 0.0f, 3.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t// Table 2 obstruction\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = b2Body_GetPosition( m_table2Id );\n\t\t\tbodyDef.rotation = b2Body_GetRotation( m_table2Id );\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 4.0f, 0.1f, { 0.0f, 3.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t// Ship 1 obstruction\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = b2Body_GetPosition( m_ship1Id );\n\t\t\tbodyDef.rotation = b2Body_GetRotation( m_ship1Id );\n\t\t\t// bodyDef.gravityScale = 0.0f;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Circle circle = { { 0.0f, 2.0f }, 0.5f };\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\n\t\t// Ship 2 obstruction\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = b2Body_GetPosition( m_ship2Id );\n\t\t\tbodyDef.rotation = b2Body_GetRotation( m_ship2Id );\n\t\t\t// bodyDef.gravityScale = 0.0f;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Circle circle = { { 0.0f, 2.0f }, 0.5f };\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 100.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 180.0f, height ) );\n\n\t\tImGui::Begin( \"Compound Shapes\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Button( \"Intrude\" ) )\n\t\t{\n\t\t\tSpawn();\n\t\t}\n\n\t\tImGui::Checkbox( \"Body AABBs\", &m_drawBodyAABBs );\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tif ( m_drawBodyAABBs )\n\t\t{\n\t\t\tb2AABB aabb = b2Body_ComputeAABB( m_table1Id );\n\t\t\tDrawBounds( m_draw, aabb, b2_colorYellow );\n\n\t\t\taabb = b2Body_ComputeAABB( m_table2Id );\n\t\t\tDrawBounds( m_draw, aabb, b2_colorYellow );\n\n\t\t\taabb = b2Body_ComputeAABB( m_ship1Id );\n\t\t\tDrawBounds( m_draw, aabb, b2_colorYellow );\n\n\t\t\taabb = b2Body_ComputeAABB( m_ship2Id );\n\t\t\tDrawBounds( m_draw, aabb, b2_colorYellow );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new CompoundShapes( context );\n\t}\n\n\tb2BodyId m_table1Id;\n\tb2BodyId m_table2Id;\n\tb2BodyId m_ship1Id;\n\tb2BodyId m_ship2Id;\n\tbool m_drawBodyAABBs;\n};\n\nstatic int sampleCompoundShape = RegisterSample( \"Shapes\", \"Compound Shapes\", CompoundShapes::Create );\n\nclass ShapeFilter : public Sample\n{\npublic:\n\tenum CollisionBits\n\t{\n\t\tGROUND = 0x00000001,\n\t\tTEAM1 = 0x00000002,\n\t\tTEAM2 = 0x00000004,\n\t\tTEAM3 = 0x00000008,\n\n\t\tALL_BITS = ( ~0u )\n\t};\n\n\texplicit ShapeFilter( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.filter.categoryBits = GROUND;\n\t\t\tshapeDef.filter.maskBits = ALL_BITS;\n\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\tbodyDef.position = { 0.0f, 2.0f };\n\t\t\tm_player1Id = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tbodyDef.position = { 0.0f, 5.0f };\n\t\t\tm_player2Id = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tbodyDef.position = { 0.0f, 8.0f };\n\t\t\tm_player3Id = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 2.0f, 1.0f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tshapeDef.filter.categoryBits = TEAM1;\n\t\t\tshapeDef.filter.maskBits = GROUND | TEAM2 | TEAM3;\n\t\t\tm_shape1Id = b2CreatePolygonShape( m_player1Id, &shapeDef, &box );\n\n\t\t\tshapeDef.filter.categoryBits = TEAM2;\n\t\t\tshapeDef.filter.maskBits = GROUND | TEAM1 | TEAM3;\n\t\t\tm_shape2Id = b2CreatePolygonShape( m_player2Id, &shapeDef, &box );\n\n\t\t\tshapeDef.filter.categoryBits = TEAM3;\n\t\t\tshapeDef.filter.maskBits = GROUND | TEAM1 | TEAM2;\n\t\t\tm_shape3Id = b2CreatePolygonShape( m_player3Id, &shapeDef, &box );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 240.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Shape Filter\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::Text( \"Player 1 Collides With\" );\n\t\t{\n\t\t\tb2Filter filter1 = b2Shape_GetFilter( m_shape1Id );\n\t\t\tbool team2 = ( filter1.maskBits & TEAM2 ) == TEAM2;\n\t\t\tif ( ImGui::Checkbox( \"Team 2##1\", &team2 ) )\n\t\t\t{\n\t\t\t\tif ( team2 )\n\t\t\t\t{\n\t\t\t\t\tfilter1.maskBits |= TEAM2;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfilter1.maskBits &= ~TEAM2;\n\t\t\t\t}\n\n\t\t\t\tb2Shape_SetFilter( m_shape1Id, filter1 );\n\t\t\t}\n\n\t\t\tbool team3 = ( filter1.maskBits & TEAM3 ) == TEAM3;\n\t\t\tif ( ImGui::Checkbox( \"Team 3##1\", &team3 ) )\n\t\t\t{\n\t\t\t\tif ( team3 )\n\t\t\t\t{\n\t\t\t\t\tfilter1.maskBits |= TEAM3;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfilter1.maskBits &= ~TEAM3;\n\t\t\t\t}\n\n\t\t\t\tb2Shape_SetFilter( m_shape1Id, filter1 );\n\t\t\t}\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::Text( \"Player 2 Collides With\" );\n\t\t{\n\t\t\tb2Filter filter2 = b2Shape_GetFilter( m_shape2Id );\n\t\t\tbool team1 = ( filter2.maskBits & TEAM1 ) == TEAM1;\n\t\t\tif ( ImGui::Checkbox( \"Team 1##2\", &team1 ) )\n\t\t\t{\n\t\t\t\tif ( team1 )\n\t\t\t\t{\n\t\t\t\t\tfilter2.maskBits |= TEAM1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfilter2.maskBits &= ~TEAM1;\n\t\t\t\t}\n\n\t\t\t\tb2Shape_SetFilter( m_shape2Id, filter2 );\n\t\t\t}\n\n\t\t\tbool team3 = ( filter2.maskBits & TEAM3 ) == TEAM3;\n\t\t\tif ( ImGui::Checkbox( \"Team 3##2\", &team3 ) )\n\t\t\t{\n\t\t\t\tif ( team3 )\n\t\t\t\t{\n\t\t\t\t\tfilter2.maskBits |= TEAM3;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfilter2.maskBits &= ~TEAM3;\n\t\t\t\t}\n\n\t\t\t\tb2Shape_SetFilter( m_shape2Id, filter2 );\n\t\t\t}\n\t\t}\n\n\t\tImGui::Separator();\n\n\t\tImGui::Text( \"Player 3 Collides With\" );\n\t\t{\n\t\t\tb2Filter filter3 = b2Shape_GetFilter( m_shape3Id );\n\t\t\tbool team1 = ( filter3.maskBits & TEAM1 ) == TEAM1;\n\t\t\tif ( ImGui::Checkbox( \"Team 1##3\", &team1 ) )\n\t\t\t{\n\t\t\t\tif ( team1 )\n\t\t\t\t{\n\t\t\t\t\tfilter3.maskBits |= TEAM1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfilter3.maskBits &= ~TEAM1;\n\t\t\t\t}\n\n\t\t\t\tb2Shape_SetFilter( m_shape3Id, filter3 );\n\t\t\t}\n\n\t\t\tbool team2 = ( filter3.maskBits & TEAM2 ) == TEAM2;\n\t\t\tif ( ImGui::Checkbox( \"Team 2##3\", &team2 ) )\n\t\t\t{\n\t\t\t\tif ( team2 )\n\t\t\t\t{\n\t\t\t\t\tfilter3.maskBits |= TEAM2;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfilter3.maskBits &= ~TEAM2;\n\t\t\t\t}\n\n\t\t\t\tb2Shape_SetFilter( m_shape3Id, filter3 );\n\t\t\t}\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2Vec2 p1 = b2Body_GetPosition( m_player1Id );\n\t\tDrawWorldString( m_draw, m_camera, { p1.x - 0.5f, p1.y }, b2_colorWhite, \"player 1\" );\n\n\t\tb2Vec2 p2 = b2Body_GetPosition( m_player2Id );\n\t\tDrawWorldString( m_draw, m_camera, { p2.x - 0.5f, p2.y }, b2_colorWhite, \"player 2\" );\n\n\t\tb2Vec2 p3 = b2Body_GetPosition( m_player3Id );\n\t\tDrawWorldString( m_draw, m_camera, { p3.x - 0.5f, p3.y }, b2_colorWhite, \"player 3\" );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ShapeFilter( context );\n\t}\n\n\tb2BodyId m_player1Id;\n\tb2BodyId m_player2Id;\n\tb2BodyId m_player3Id;\n\n\tb2ShapeId m_shape1Id;\n\tb2ShapeId m_shape2Id;\n\tb2ShapeId m_shape3Id;\n};\n\nstatic int sampleShapeFilter = RegisterSample( \"Shapes\", \"Filter\", ShapeFilter::Create );\n\n// This shows how to use custom filtering\nclass CustomFilter : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_count = 10\n\t};\n\n\texplicit CustomFilter( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 10.0f;\n\t\t}\n\n\t\t// Register custom filter\n\t\tb2World_SetCustomFilterCallback( m_worldId, CustomFilterStatic, this );\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } };\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.enableCustomFiltering = true;\n\t\tb2Polygon box = b2MakeSquare( 1.0f );\n\t\tfloat x = -e_count;\n\n\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t{\n\t\t\tbodyDef.position = { x, 5.0f };\n\t\t\tm_bodyIds[i] = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tshapeDef.userData = reinterpret_cast<void*>( intptr_t( i + 1 ) );\n\t\t\tm_shapeIds[i] = b2CreatePolygonShape( m_bodyIds[i], &shapeDef, &box );\n\t\t\tx += 2.0f;\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tDrawTextLine( \"Custom filter disables collision between odd and even shapes\" );\n\n\t\tSample::Step();\n\n\t\tfor ( int i = 0; i < e_count; ++i )\n\t\t{\n\t\t\tb2Vec2 p = b2Body_GetPosition( m_bodyIds[i] );\n\t\t\tDrawWorldString( m_draw, m_camera, { p.x, p.y }, b2_colorWhite, \"%d\", i );\n\t\t}\n\t}\n\n\tbool ShouldCollide( b2ShapeId shapeIdA, b2ShapeId shapeIdB )\n\t{\n\t\tvoid* userDataA = b2Shape_GetUserData( shapeIdA );\n\t\tvoid* userDataB = b2Shape_GetUserData( shapeIdB );\n\n\t\tif ( userDataA == nullptr || userDataB == nullptr )\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tint indexA = static_cast<int>( reinterpret_cast<intptr_t>( userDataA ) );\n\t\tint indexB = static_cast<int>( reinterpret_cast<intptr_t>( userDataB ) );\n\n\t\treturn ( ( indexA & 1 ) + ( indexB & 1 ) ) != 1;\n\t}\n\n\tstatic bool CustomFilterStatic( b2ShapeId shapeIdA, b2ShapeId shapeIdB, void* context )\n\t{\n\t\tCustomFilter* customFilter = static_cast<CustomFilter*>( context );\n\t\treturn customFilter->ShouldCollide( shapeIdA, shapeIdB );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new CustomFilter( context );\n\t}\n\n\tb2BodyId m_bodyIds[e_count];\n\tb2ShapeId m_shapeIds[e_count];\n};\n\nstatic int sampleCustomFilter = RegisterSample( \"Shapes\", \"Custom Filter\", CustomFilter::Create );\n\n// Restitution is approximate since Box2D uses speculative collision\nclass Restitution : public Sample\n{\npublic:\n\tenum ShapeType\n\t{\n\t\te_circleShape = 0,\n\t\te_boxShape\n\t};\n\n\texplicit Restitution( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 4.0f, 17.0f };\n\t\t\tm_context->camera.zoom = 27.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tfloat h = 1.0f * m_count;\n\t\t\tb2Segment segment = { { -h, 0.0f }, { h, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tm_shapeType = e_circleShape;\n\n\t\tCreateBodies();\n\t}\n\n\tvoid CreateBodies()\n\t{\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_bodyIds[i] ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t\t}\n\t\t}\n\n\t\tb2Circle circle = {};\n\t\tcircle.radius = 0.5f;\n\n\t\tb2Polygon box = b2MakeBox( 0.5f, 0.5f );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.restitution = 0.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tfloat dr = 1.0f / ( m_count > 1 ? m_count - 1 : 1 );\n\t\tfloat x = -1.0f * ( m_count - 1 );\n\t\tfloat dx = 2.0f;\n\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tbodyDef.position = { x, 40.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tm_bodyIds[i] = bodyId;\n\n\t\t\tif ( m_shapeType == e_circleShape )\n\t\t\t{\n\t\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\t}\n\n\t\t\tshapeDef.material.restitution += dr;\n\t\t\tx += dx;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 100.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Restitution\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tbool changed = false;\n\t\tconst char* shapeTypes[] = { \"Circle\", \"Box\" };\n\n\t\tint shapeType = int( m_shapeType );\n\t\tchanged = changed || ImGui::Combo( \"Shape\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) );\n\t\tm_shapeType = ShapeType( shapeType );\n\n\t\tchanged = changed || ImGui::Button( \"Reset\" );\n\n\t\tif ( changed )\n\t\t{\n\t\t\tCreateBodies();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Restitution( context );\n\t}\n\n\tstatic constexpr int m_count = 40;\n\n\tb2BodyId m_bodyIds[m_count] = {};\n\tShapeType m_shapeType;\n};\n\nstatic int sampleIndex = RegisterSample( \"Shapes\", \"Restitution\", Restitution::Create );\n\nclass Friction : public Sample\n{\npublic:\n\texplicit Friction( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 14.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.6f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.2f;\n\n\t\t\tb2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tb2Polygon box = b2MakeOffsetBox( 13.0f, 0.25f, { -4.0f, 22.0f }, b2MakeRot( -0.25f ) );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 0.25f, 1.0f, { 10.5f, 19.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 13.0f, 0.25f, { 4.0f, 14.0f }, b2MakeRot( 0.25f ) );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 0.25f, 1.0f, { -10.5f, 11.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 13.0f, 0.25f, { -4.0f, 6.0f }, b2MakeRot( -0.25f ) );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2Polygon box = b2MakeBox( 0.5f, 0.5f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.density = 25.0f;\n\n\t\t\tfloat friction[5] = { 0.75f, 0.5f, 0.35f, 0.1f, 0.0f };\n\n\t\t\tfor ( int i = 0; i < 5; ++i )\n\t\t\t{\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t\tbodyDef.position = { -15.0f + 4.0f * i, 28.0f };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\tshapeDef.material.friction = friction[i];\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Friction( context );\n\t}\n};\n\nstatic int sampleFriction = RegisterSample( \"Shapes\", \"Friction\", Friction::Create );\n\nclass RollingResistance : public Sample\n{\npublic:\n\texplicit RollingResistance( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 5.0f, 20.0f };\n\t\t\tm_context->camera.zoom = 27.5f;\n\t\t}\n\n\t\tm_lift = 0.0f;\n\t\tm_resistScale = 0.02f;\n\t\tCreateScene();\n\t}\n\n\tvoid CreateScene()\n\t{\n\t\tb2Circle circle = { b2Vec2_zero, 0.5f };\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tfor ( int i = 0; i < 20; ++i )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Segment segment = { { -40.0f, 2.0f * i }, { 40.0f, 2.0f * i + m_lift } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -39.5f, 2.0f * i + 0.75f };\n\t\t\tbodyDef.angularVelocity = -10.0f;\n\t\t\tbodyDef.linearVelocity = { 5.0f, 0.0f };\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tshapeDef.material.rollingResistance = m_resistScale * i;\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid Keyboard( int key ) override\n\t{\n\t\tswitch ( key )\n\t\t{\n\t\t\tcase GLFW_KEY_1:\n\t\t\t\tm_lift = 0.0f;\n\t\t\t\tCreateWorld();\n\t\t\t\tCreateScene();\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_2:\n\t\t\t\tm_lift = 5.0f;\n\t\t\t\tCreateWorld();\n\t\t\t\tCreateScene();\n\t\t\t\tbreak;\n\n\t\t\tcase GLFW_KEY_3:\n\t\t\t\tm_lift = -5.0f;\n\t\t\t\tCreateWorld();\n\t\t\t\tCreateScene();\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tSample::Keyboard( key );\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tfor ( int i = 0; i < 20; ++i )\n\t\t{\n\t\t\tDrawWorldString( m_draw, m_camera, { -41.5f, 2.0f * i + 1.0f }, b2_colorWhite, \"%.2f\", m_resistScale * i );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new RollingResistance( context );\n\t}\n\n\tfloat m_resistScale;\n\tfloat m_lift;\n};\n\nstatic int sampleRollingResistance = RegisterSample( \"Shapes\", \"Rolling Resistance\", RollingResistance::Create );\n\nclass ConveyorBelt : public Sample\n{\npublic:\n\texplicit ConveyorBelt( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 2.0f, 7.5f };\n\t\t\tm_context->camera.zoom = 12.0f;\n\t\t}\n\n\t\t// Ground\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\t// Platform\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { -5.0f, 5.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeRoundedBox( 10.0f, 0.25f, 0.25f );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.8f;\n\t\t\tshapeDef.material.tangentSpeed = 2.0f;\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t// Boxes\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Polygon cube = b2MakeSquare( 0.5f );\n\t\tfor ( int i = 0; i < 5; ++i )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -10.0f + 2.0f * i, 7.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &cube );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ConveyorBelt( context );\n\t}\n};\n\nstatic int sampleConveyorBelt = RegisterSample( \"Shapes\", \"Conveyor Belt\", ConveyorBelt::Create );\n\nclass TangentSpeed : public Sample\n{\npublic:\n\texplicit TangentSpeed( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 60.0f, -15.0f };\n\t\t\tm_context->camera.zoom = 38.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t// const char* path = \"M 613.8334,185.20833 H 500.06255 L 470.95838,182.5625 444.50004,174.625 418.04171,161.39583 \"\n\t\t\t//\t\t\t\t   \"394.2292,140.22917 h \"\n\t\t\t//\t\t\t\t   \"-13.22916 v 44.97916 H 68.791712 V 0 h -21.16671 v 206.375 l 566.208398,-1e-5 z\";\n\n\t\t\tconst char* path = \"m 613.8334,185.20833 -42.33338,0 h -37.04166 l -34.39581,0 -29.10417,-2.64583 -26.45834,-7.9375 \"\n\t\t\t\t\t\t\t   \"-26.45833,-13.22917 -23.81251,-21.16666 h -13.22916 v 44.97916 H 68.791712 V 0 h -21.16671 v \"\n\t\t\t\t\t\t\t   \"206.375 l 566.208398,-1e-5 z\";\n\n\t\t\tb2Vec2 offset = { -47.375002f, 0.25f };\n\n\t\t\tfloat scale = 0.2f;\n\t\t\tb2Vec2 points[20] = {};\n\t\t\tint count = ParsePath( path, offset, points, 20, scale, true );\n\n\t\t\tb2SurfaceMaterial materials[20] = {};\n\t\t\tfor ( int i = 0; i < 20; ++i )\n\t\t\t{\n\t\t\t\tmaterials[i].friction = 0.6f;\n\t\t\t}\n\n\t\t\tmaterials[0].tangentSpeed = -10.0;\n\t\t\tmaterials[0].customColor = b2_colorDarkBlue;\n\t\t\tmaterials[1].tangentSpeed = -20.0;\n\t\t\tmaterials[1].customColor = b2_colorDarkCyan;\n\t\t\tmaterials[2].tangentSpeed = -30.0;\n\t\t\tmaterials[2].customColor = b2_colorDarkGoldenRod;\n\t\t\tmaterials[3].tangentSpeed = -40.0;\n\t\t\tmaterials[3].customColor = b2_colorDarkGray;\n\t\t\tmaterials[4].tangentSpeed = -50.0;\n\t\t\tmaterials[4].customColor = b2_colorDarkGreen;\n\t\t\tmaterials[5].tangentSpeed = -60.0;\n\t\t\tmaterials[5].customColor = b2_colorDarkKhaki;\n\t\t\tmaterials[6].tangentSpeed = -70.0;\n\t\t\tmaterials[6].customColor = b2_colorDarkMagenta;\n\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points;\n\t\t\tchainDef.count = count;\n\t\t\tchainDef.isLoop = true;\n\t\t\tchainDef.materials = materials;\n\t\t\tchainDef.materialCount = count;\n\n\t\t\tb2CreateChain( groundId, &chainDef );\n\n\t\t\tm_friction = 0.6f;\n\t\t\tm_rollingResistance = 0.3f;\n\t\t}\n\t}\n\n\tb2BodyId DropBall()\n\t{\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { 110.0f, -30.0f };\n\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.friction = m_friction;\n\t\tshapeDef.material.rollingResistance = m_rollingResistance;\n\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\treturn bodyId;\n\t}\n\n\tvoid Reset()\n\t{\n\t\tint count = int( m_bodyIds.size() );\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t}\n\n\t\tm_bodyIds.clear();\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 80.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 260.0f, height ) );\n\n\t\tImGui::Begin( \"Ball Parameters\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 140.0f );\n\n\t\tif ( ImGui::SliderFloat( \"Friction\", &m_friction, 0.0f, 2.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tReset();\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Rolling Resistance\", &m_rollingResistance, 0.0f, 1.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tReset();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( m_stepCount % 25 == 0 && m_bodyIds.size() < m_totalCount && m_context->pause == false )\n\t\t{\n\t\t\tb2BodyId id = DropBall();\n\t\t\tm_bodyIds.push_back( id );\n\t\t}\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new TangentSpeed( context );\n\t}\n\n\tstatic constexpr int m_totalCount = 200;\n\tstd::vector<b2BodyId> m_bodyIds;\n\tfloat m_friction;\n\tfloat m_rollingResistance;\n};\n\nstatic int sampleTangentSpeed = RegisterSample( \"Shapes\", \"Tangent Speed\", TangentSpeed::Create );\n\n// This sample shows how to modify the geometry on an existing shape. This is only supported on\n// dynamic and kinematic shapes because static shapes don't look for new collisions.\nclass ModifyGeometry : public Sample\n{\npublic:\n\texplicit ModifyGeometry( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.zoom = 25.0f * 0.25f;\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 10.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { 0.0f, 4.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeBox( 1.0f, 1.0f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tm_shapeType = b2_circleShape;\n\t\t\tm_scale = 1.0f;\n\t\t\tm_circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.position = { 0.0f, 1.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tm_shapeId = b2CreateCircleShape( bodyId, &shapeDef, &m_circle );\n\t\t}\n\t}\n\n\tvoid UpdateShape()\n\t{\n\t\tswitch ( m_shapeType )\n\t\t{\n\t\t\tcase b2_circleShape:\n\t\t\t\tm_circle = { { 0.0f, 0.0f }, 0.5f * m_scale };\n\t\t\t\tb2Shape_SetCircle( m_shapeId, &m_circle );\n\t\t\t\tbreak;\n\n\t\t\tcase b2_capsuleShape:\n\t\t\t\tm_capsule = { { -0.5f * m_scale, 0.0f }, { 0.0f, 0.5f * m_scale }, 0.5f * m_scale };\n\t\t\t\tb2Shape_SetCapsule( m_shapeId, &m_capsule );\n\t\t\t\tbreak;\n\n\t\t\tcase b2_segmentShape:\n\t\t\t\tm_segment = { { -0.5f * m_scale, 0.0f }, { 0.75f * m_scale, 0.0f } };\n\t\t\t\tb2Shape_SetSegment( m_shapeId, &m_segment );\n\t\t\t\tbreak;\n\n\t\t\tcase b2_polygonShape:\n\t\t\t\tm_polygon = b2MakeBox( 0.5f * m_scale, 0.75f * m_scale );\n\t\t\t\tb2Shape_SetPolygon( m_shapeId, &m_polygon );\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tassert( false );\n\t\t\t\tbreak;\n\t\t}\n\n\t\tb2BodyId bodyId = b2Shape_GetBody( m_shapeId );\n\t\tb2Body_ApplyMassFromShapes( bodyId );\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 230.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );\n\n\t\tImGui::Begin( \"Modify Geometry\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::RadioButton( \"Circle\", m_shapeType == b2_circleShape ) )\n\t\t{\n\t\t\tm_shapeType = b2_circleShape;\n\t\t\tUpdateShape();\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Capsule\", m_shapeType == b2_capsuleShape ) )\n\t\t{\n\t\t\tm_shapeType = b2_capsuleShape;\n\t\t\tUpdateShape();\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Segment\", m_shapeType == b2_segmentShape ) )\n\t\t{\n\t\t\tm_shapeType = b2_segmentShape;\n\t\t\tUpdateShape();\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Polygon\", m_shapeType == b2_polygonShape ) )\n\t\t{\n\t\t\tm_shapeType = b2_polygonShape;\n\t\t\tUpdateShape();\n\t\t}\n\n\t\tif ( ImGui::SliderFloat( \"Scale\", &m_scale, 0.1f, 10.0f, \"%.2f\" ) )\n\t\t{\n\t\t\tUpdateShape();\n\t\t}\n\n\t\tb2BodyId bodyId = b2Shape_GetBody( m_shapeId );\n\t\tb2BodyType bodyType = b2Body_GetType( bodyId );\n\n\t\tif ( ImGui::RadioButton( \"Static\", bodyType == b2_staticBody ) )\n\t\t{\n\t\t\tb2Body_SetType( bodyId, b2_staticBody );\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Kinematic\", bodyType == b2_kinematicBody ) )\n\t\t{\n\t\t\tb2Body_SetType( bodyId, b2_kinematicBody );\n\t\t}\n\n\t\tif ( ImGui::RadioButton( \"Dynamic\", bodyType == b2_dynamicBody ) )\n\t\t{\n\t\t\tb2Body_SetType( bodyId, b2_dynamicBody );\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ModifyGeometry( context );\n\t}\n\n\tb2ShapeId m_shapeId;\n\tb2ShapeType m_shapeType;\n\tfloat m_scale;\n\n\tunion\n\t{\n\t\tb2Circle m_circle;\n\t\tb2Capsule m_capsule;\n\t\tb2Segment m_segment;\n\t\tb2Polygon m_polygon;\n\t};\n};\n\nstatic int sampleModifyGeometry = RegisterSample( \"Shapes\", \"Modify Geometry\", ModifyGeometry::Create );\n\n// Shows how to link to chain shapes together. This is a useful technique for building large game levels with smooth collision.\nclass ChainLink : public Sample\n{\npublic:\n\texplicit ChainLink( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t}\n\n\t\tb2Vec2 points1[] = { { 40.0f, 1.0f },\t{ 0.0f, 0.0f },\t { -40.0f, 0.0f },\n\t\t\t\t\t\t\t { -40.0f, -1.0f }, { 0.0f, -1.0f }, { 40.0f, -1.0f } };\n\t\tb2Vec2 points2[] = { { -40.0f, -1.0f }, { 0.0f, -1.0f }, { 40.0f, -1.0f },\n\t\t\t\t\t\t\t { 40.0f, 0.0f },\t{ 0.0f, 0.0f },\t { -40.0f, 0.0f } };\n\n\t\tint count1 = std::size( points1 );\n\t\tint count2 = std::size( points2 );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t{\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points1;\n\t\t\tchainDef.count = count1;\n\t\t\tchainDef.isLoop = false;\n\t\t\tb2CreateChain( groundId, &chainDef );\n\t\t}\n\n\t\t{\n\t\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\t\tchainDef.points = points2;\n\t\t\tchainDef.count = count2;\n\t\t\tchainDef.isLoop = false;\n\t\t\tb2CreateChain( groundId, &chainDef );\n\t\t}\n\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t{\n\t\t\tbodyDef.position = { -5.0f, 2.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\n\t\t{\n\t\t\tbodyDef.position = { 0.0f, 2.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f };\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\t\t}\n\n\t\t{\n\t\t\tbodyDef.position = { 5.0f, 2.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tfloat h = 0.5f;\n\t\t\tb2Polygon box = b2MakeBox( h, h );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tDrawTextLine( \"This shows how to link together two chain shapes\" );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new ChainLink( context );\n\t}\n};\n\nstatic int sampleChainLink = RegisterSample( \"Shapes\", \"Chain Link\", ChainLink::Create );\n\nclass RoundedShapes : public Sample\n{\npublic:\n\texplicit RoundedShapes( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.zoom = 25.0f * 0.55f;\n\t\t\tm_context->camera.center = { 2.0f, 8.0f };\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 20.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 1.0f, 5.0f, { 19.0f, 5.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 1.0f, 5.0f, { -19.0f, 5.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t// b2Capsule capsule = {{-0.25f, 0.0f}, {0.25f, 0.0f}, 0.25f};\n\t\t// b2Circle circle = {{0.0f, 0.0f}, 0.35f};\n\t\t// b2Polygon square = b2MakeSquare(0.35f);\n\n\t\t// b2Vec2 points[3] = {{-0.1f, -0.5f}, {0.1f, -0.5f}, {0.0f, 0.5f}};\n\t\t// b2Hull wedgeHull = b2ComputeHull(points, 3);\n\t\t// b2Polygon wedge = b2MakePolygon(&wedgeHull, 0.0f);\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.rollingResistance = 0.3f;\n\n\t\tfloat y = 2.0f;\n\t\tint xcount = 10, ycount = 10;\n\n\t\tfor ( int i = 0; i < ycount; ++i )\n\t\t{\n\t\t\tfloat x = -5.0f;\n\t\t\tfor ( int j = 0; j < xcount; ++j )\n\t\t\t{\n\t\t\t\tbodyDef.position = { x, y };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\tb2Polygon poly = RandomPolygon( 0.5f );\n\t\t\t\tpoly.radius = RandomFloatRange( 0.05f, 0.25f );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &poly );\n\n\t\t\t\tx += 1.0f;\n\t\t\t}\n\n\t\t\ty += 1.0f;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new RoundedShapes( context );\n\t}\n};\n\nstatic int sampleRoundedShapes = RegisterSample( \"Shapes\", \"Rounded\", RoundedShapes::Create );\n\nclass EllipseShape : public Sample\n{\npublic:\n\texplicit EllipseShape( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.zoom = 25.0f * 0.55f;\n\t\t\tm_context->camera.center = { 2.0f, 8.0f };\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 20.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 1.0f, 5.0f, { 19.0f, 5.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tbox = b2MakeOffsetBox( 1.0f, 5.0f, { -19.0f, 5.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tb2Vec2 points[6] = {\n\t\t\t{ 0.0f, -0.25f }, { 0.0f, 0.25f }, { 0.05f, 0.075f }, { -0.05f, 0.075f }, { 0.05f, -0.075f }, { -0.05f, -0.075f },\n\t\t};\n\t\tb2Hull diamondHull = b2ComputeHull( points, 6 );\n\t\tb2Polygon poly = b2MakePolygon( &diamondHull, 0.2f );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.rollingResistance = 0.2f;\n\n\t\tfloat y = 2.0f;\n\t\tint xCount = 10, yCount = 10;\n\n\t\tfor ( int i = 0; i < yCount; ++i )\n\t\t{\n\t\t\tfloat x = -5.0f;\n\t\t\tfor ( int j = 0; j < xCount; ++j )\n\t\t\t{\n\t\t\t\tbodyDef.position = { x, y };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &poly );\n\n\t\t\t\tx += 1.0f;\n\t\t\t}\n\n\t\t\ty += 1.0f;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new EllipseShape( context );\n\t}\n};\n\nstatic int sampleEllipseShape = RegisterSample( \"Shapes\", \"Ellipse\", EllipseShape::Create );\n\nclass OffsetShapes : public Sample\n{\npublic:\n\texplicit OffsetShapes( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.zoom = 25.0f * 0.55f;\n\t\t\tm_context->camera.center = { 2.0f, 8.0f };\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { -1.0f, 1.0f };\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 1.0f, 1.0f, { 10.0f, -2.0f }, b2MakeRot( 0.5f * B2_PI ) );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\t{\n\t\t\tb2Capsule capsule = { { -5.0f, 1.0f }, { -4.0f, 1.0f }, 0.25f };\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 13.5f, -0.75f };\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\t\t}\n\n\t\t{\n\t\t\tb2Polygon box = b2MakeOffsetBox( 0.75f, 0.5f, { 9.0f, 2.0f }, b2MakeRot( 0.5f * B2_PI ) );\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, 0.0f };\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tDrawTransform( m_draw, b2Transform_identity, 1.0f );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new OffsetShapes( context );\n\t}\n};\n\nstatic int sampleOffsetShapes = RegisterSample( \"Shapes\", \"Offset\", OffsetShapes::Create );\n\n// This shows how to use explosions and demonstrates the projected perimeter\nclass Explosion : public Sample\n{\npublic:\n\texplicit Explosion( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 0.0f };\n\t\t\tm_context->camera.zoom = 14.0f;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.gravityScale = 0.0f;\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tm_referenceAngle = 0.0f;\n\n\t\tb2WeldJointDef weldDef = b2DefaultWeldJointDef();\n\t\tweldDef.base.bodyIdA = groundId;\n\t\tweldDef.base.localFrameA.q = b2MakeRot( m_referenceAngle );\n\t\tweldDef.base.localFrameB.p = b2Vec2_zero;\n\t\tweldDef.angularHertz = 0.5f;\n\t\tweldDef.angularDampingRatio = 0.7f;\n\t\tweldDef.linearHertz = 0.5f;\n\t\tweldDef.linearDampingRatio = 0.7f;\n\n\t\tfloat r = 8.0f;\n\t\tfor ( float angle = 0.0f; angle < 360.0f; angle += 30.0f )\n\t\t{\n\t\t\tb2CosSin cosSin = b2ComputeCosSin( angle * B2_PI / 180.0f );\n\t\t\tbodyDef.position = { r * cosSin.cosine, r * cosSin.sine };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 1.0f, 0.1f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tweldDef.base.localFrameA.p = bodyDef.position;\n\t\t\tweldDef.base.bodyIdB = bodyId;\n\n\t\t\tb2JointId jointId = b2CreateWeldJoint( m_worldId, &weldDef );\n\t\t\tm_jointIds.push_back( jointId );\n\t\t}\n\n\t\tm_radius = 7.0f;\n\t\tm_falloff = 3.0f;\n\t\tm_impulse = 10.0f;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 160.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Explosion\", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Button( \"Explode\" ) )\n\t\t{\n\t\t\tb2ExplosionDef def = b2DefaultExplosionDef();\n\t\t\tdef.position = b2Vec2_zero;\n\t\t\tdef.radius = m_radius;\n\t\t\tdef.falloff = m_falloff;\n\t\t\tdef.impulsePerLength = m_impulse;\n\t\t\tb2World_Explode( m_worldId, &def );\n\t\t}\n\n\t\tImGui::SliderFloat( \"radius\", &m_radius, 0.0f, 20.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"falloff\", &m_falloff, 0.0f, 20.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"impulse\", &m_impulse, -20.0f, 20.0f, \"%.1f\" );\n\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( m_context->pause == false || m_context->singleStep == true )\n\t\t{\n\t\t\tm_referenceAngle += m_context->hertz > 0.0f ? 60.0f * B2_PI / 180.0f / m_context->hertz : 0.0f;\n\t\t\tm_referenceAngle = b2UnwindAngle( m_referenceAngle );\n\n\t\t\tint count = (int)m_jointIds.size();\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tb2Transform localFrameA = b2Joint_GetLocalFrameA( m_jointIds[i] );\n\t\t\t\tlocalFrameA.q = b2MakeRot( m_referenceAngle );\n\t\t\t\tb2Joint_SetLocalFrameA( m_jointIds[i], localFrameA );\n\t\t\t}\n\t\t}\n\n\t\tSample::Step();\n\n\t\tDrawTextLine( \"reference angle = %g\", m_referenceAngle );\n\n\t\tDrawCircle( m_draw, b2Vec2_zero, m_radius + m_falloff, b2_colorBox2DBlue );\n\t\tDrawCircle( m_draw, b2Vec2_zero, m_radius, b2_colorBox2DYellow );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Explosion( context );\n\t}\n\n\tstd::vector<b2JointId> m_jointIds;\n\tfloat m_radius;\n\tfloat m_falloff;\n\tfloat m_impulse;\n\tfloat m_referenceAngle;\n};\n\nstatic int sampleExplosion = RegisterSample( \"Shapes\", \"Explosion\", Explosion::Create );\n\n// This sample tests a static shape being recreated every step.\nclass RecreateStatic : public Sample\n{\npublic:\n\texplicit RecreateStatic( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 2.5f };\n\t\t\tm_context->camera.zoom = 3.5f;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = { 0.0f, 1.0f };\n\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2Polygon box = b2MakeBox( 1.0f, 1.0f );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\tm_groundId = {};\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( B2_IS_NON_NULL( m_groundId ) )\n\t\t{\n\t\t\tb2DestroyBody( m_groundId );\n\t\t\tm_groundId = {};\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tm_groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t// Invoke contact creation so that contact points are created immediately\n\t\t// on a static body.\n\t\tshapeDef.invokeContactCreation = true;\n\n\t\tb2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\tb2CreateSegmentShape( m_groundId, &shapeDef, &segment );\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new RecreateStatic( context );\n\t}\n\n\tb2BodyId m_groundId;\n};\n\nstatic int sampleSingleBox = RegisterSample( \"Shapes\", \"Recreate Static\", RecreateStatic::Create );\n\nclass BoxRestitution : public Sample\n{\npublic:\n\texplicit BoxRestitution( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 10.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tfloat h = 2.0f * m_count;\n\t\t\tb2Segment segment = { { -h, 0.0f }, { h, 0.0f } };\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2Polygon box = b2MakeBox( 0.5f, 0.5f );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.restitution = 0.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tfloat dr = 1.0f / ( m_count > 1 ? m_count - 1 : 1 );\n\t\tfloat x = -1.0f * ( m_count - 1 );\n\t\tfloat dx = 2.0f;\n\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tchar buffer[32];\n\t\t\tsnprintf( buffer, 32, \"%.2f\", shapeDef.material.restitution );\n\n\t\t\tbodyDef.position = { x, 1.0f };\n\t\t\tbodyDef.name = buffer;\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tbodyDef.position = { x, 4.0f };\n\t\t\tbodyDef.name = buffer;\n\t\t\tbodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tshapeDef.material.restitution += dr;\n\t\t\tx += dx;\n\t\t}\n\t}\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new BoxRestitution( context );\n\t}\n\n\tstatic constexpr int m_count = 10;\n};\n\nstatic int sampleBoxRestitution = RegisterSample( \"Shapes\", \"Box Restitution\", BoxRestitution::Create );\n\nclass Wind : public Sample\n{\npublic:\n\tenum ShapeType\n\t{\n\t\te_circleShape = 0,\n\t\te_capsuleShape,\n\t\te_boxShape\n\t};\n\n\texplicit Wind( SampleContext* context )\n\t\t: Sample( context )\n\t\t, m_bodyIds{}\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 1.0f };\n\t\t\tm_context->camera.zoom = 2.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tm_groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t}\n\n\t\tm_shapeType = e_capsuleShape;\n\t\tm_wind = { 6.0f, 0.0f };\n\t\tm_drag = 1.0f;\n\t\tm_lift = 0.75f;\n\t\tm_count = 10;\n\t\tm_noise = { 0.0f, 0.0f };\n\n\t\tCreateScene();\n\t}\n\n\tvoid CreateScene()\n\t{\n\t\tfor ( int i = 0; i < m_maxCount; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_bodyIds[i] ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t\t}\n\t\t}\n\n\t\tfloat radius = 0.1f;\n\t\tb2Circle circle = { { 0.0f, 0.0f }, radius };\n\t\tb2Capsule capsule = { { 0.0f, -radius }, { 0.0f, radius }, 0.25f * radius };\n\t\tb2Polygon box = b2MakeBox( 0.25f * radius, 1.25f * radius );\n\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = m_groundId;\n\t\tjointDef.base.localFrameA.p = { 0.0f, 2.0f + radius };\n\t\tjointDef.base.drawScale = 0.1f;\n\t\tjointDef.hertz = 0.1f;\n\t\tjointDef.dampingRatio = 0.0f;\n\t\tjointDef.enableSpring = true;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 20.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.gravityScale = 0.5f;\n\t\tbodyDef.enableSleep = false;\n\n\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t{\n\t\t\tbodyDef.position = { 0.0f, 2.0f - 2.0f * radius * i };\n\t\t\tm_bodyIds[i] = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tif ( m_shapeType == e_circleShape )\n\t\t\t{\n\t\t\t\tb2CreateCircleShape( m_bodyIds[i], &shapeDef, &circle );\n\t\t\t}\n\t\t\telse if ( m_shapeType == e_capsuleShape )\n\t\t\t{\n\t\t\t\tb2CreateCapsuleShape( m_bodyIds[i], &shapeDef, &capsule );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2CreatePolygonShape( m_bodyIds[i], &shapeDef, &box );\n\t\t\t}\n\n\t\t\tjointDef.base.bodyIdB = m_bodyIds[i];\n\t\t\tjointDef.base.localFrameB.p = { 0.0f, radius };\n\t\t\tb2CreateRevoluteJoint( m_worldId, &jointDef );\n\n\t\t\tjointDef.base.bodyIdA = m_bodyIds[i];\n\t\t\tjointDef.base.localFrameA.p = { 0.0f, -radius };\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 15.0f * fontSize;\n\t\tImGui::SetNextWindowPos( { 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize }, ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( { 24.0f * fontSize, height } );\n\n\t\tImGui::Begin( \"Wind\", nullptr, ImGuiWindowFlags_NoResize );\n\t\tImGui::PushItemWidth( 18.0f * fontSize );\n\n\t\tconst char* shapeTypes[] = { \"Circle\", \"Capsule\", \"Box\" };\n\t\tint shapeType = int( m_shapeType );\n\t\tif ( ImGui::Combo( \"Shape\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) )\n\t\t{\n\t\t\tm_shapeType = ShapeType( shapeType );\n\t\t\tCreateScene();\n\t\t}\n\n\t\tImGui::SliderFloat2( \"Wind\", &m_wind.x, -20.0f, 20.0f, \"%.1f\" );\n\t\tImGui::SliderFloat( \"Drag\", &m_drag, 0.0f, 1.0f, \"%.2f\" );\n\t\tImGui::SliderFloat( \"Lift\", &m_lift, 0.0f, 4.0f, \"%.2f\" );\n\t\tif ( ImGui::SliderInt( \"Count\", &m_count, 1, m_maxCount, \"%d\" ) )\n\t\t{\n\t\t\tCreateScene();\n\t\t}\n\n\t\tImGui::PopItemWidth();\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tif ( m_context->pause == false || m_context->singleStep == true )\n\t\t{\n\t\t\tfloat speed;\n\t\t\tb2Vec2 direction = b2GetLengthAndNormalize( &speed, m_wind );\n\t\t\tb2Vec2 wind = b2MulSV( speed, b2Add( direction, m_noise ) );\n\n\t\t\tfor ( int i = 0; i < m_count; ++i )\n\t\t\t{\n\t\t\t\tb2ShapeId shapeIds[1];\n\t\t\t\tint count = b2Body_GetShapes( m_bodyIds[i], shapeIds, 1 );\n\t\t\t\tfor ( int j = 0; j < count; ++j )\n\t\t\t\t{\n\t\t\t\t\tb2Shape_ApplyWind( shapeIds[j], wind, m_drag, m_lift, true );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tb2Vec2 rand = RandomVec2( -0.3f, 0.3f );\n\t\t\tm_noise = b2Lerp( m_noise, rand, 0.05f );\n\n\t\t\tDrawLine( m_draw, b2Vec2_zero, b2MulSV( 0.2f, wind ), b2_colorFuchsia );\n\t\t}\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Wind( context );\n\t}\n\n\tstatic constexpr int m_maxCount = 60;\n\n\tShapeType m_shapeType;\n\tb2Vec2 m_wind;\n\tfloat m_drag;\n\tfloat m_lift;\n\tb2Vec2 m_noise;\n\tb2BodyId m_groundId;\n\tb2BodyId m_bodyIds[m_maxCount];\n\tint m_count;\n};\n\nstatic int sampleWind = RegisterSample( \"Shapes\", \"Wind\", Wind::Create );\n"
  },
  {
    "path": "samples/sample_stacking.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"draw.h\"\n#include \"random.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n#include <imgui.h>\n#include <vector>\n\nclass SingleBox : public Sample\n{\npublic:\n\texplicit SingleBox( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 2.5f };\n\t\t\tm_context->camera.zoom = 3.5f;\n\t\t}\n\n\t\tfloat extent = 1.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tfloat groundWidth = 66.0f * extent;\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t// shapeDef.friction = 0.5f;\n\n\t\tb2Segment segment = { { -0.5f * 2.0f * groundWidth, 0.0f }, { 0.5f * 2.0f * groundWidth, 0.0f } };\n\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tb2Polygon box = b2MakeBox( extent, extent );\n\t\tbodyDef.position = { 0.0f, 1.0f };\n\t\tbodyDef.linearVelocity = { 5.0f, 0.0f };\n\t\tm_bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\tb2CreatePolygonShape( m_bodyId, &shapeDef, &box );\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\t// DrawCircle({0.0f, 2.0f}, 1.0f, b2_colorWhite);\n\n\t\tb2Vec2 position = b2Body_GetPosition( m_bodyId );\n\t\tDrawTextLine( \"(x, y) = (%.2g, %.2g)\", position.x, position.y );\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new SingleBox( context );\n\t}\n\n\tb2BodyId m_bodyId;\n};\n\nstatic int sampleSingleBox = RegisterSample( \"Stacking\", \"Single Box\", SingleBox::Create );\n\nclass TiltedStack : public Sample\n{\npublic:\n\texplicit TiltedStack( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 7.5f, 7.5f };\n\t\t\tm_context->camera.zoom = 20.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, -1.0f };\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 1000.0f, 1.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tfor ( int i = 0; i < m_rows * m_columns; ++i )\n\t\t{\n\t\t\tm_bodies[i] = b2_nullBodyId;\n\t\t}\n\n\t\tb2Polygon box = b2MakeRoundedBox( 0.45f, 0.45f, 0.05f );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.friction = 0.3f;\n\n\t\tfloat offset = 0.2f;\n\t\tfloat dx = 5.0f;\n\t\tfloat xroot = -0.5f * dx * ( m_columns - 1.0f );\n\n\t\tfor ( int j = 0; j < m_columns; ++j )\n\t\t{\n\t\t\tfloat x = xroot + j * dx;\n\n\t\t\tfor ( int i = 0; i < m_rows; ++i )\n\t\t\t{\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\t\tint n = j * m_rows + i;\n\n\t\t\t\tbodyDef.position = { x + offset * i, 0.5f + 1.0f * i };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\tm_bodies[n] = bodyId;\n\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new TiltedStack( context );\n\t}\n\n\tstatic constexpr int m_columns = 10;\n\tstatic constexpr int m_rows = 10;\n\n\tb2BodyId m_bodies[m_rows * m_columns];\n};\n\nstatic int sampleTiltedStack = RegisterSample( \"Stacking\", \"Tilted Stack\", TiltedStack::Create );\n\n// This sample shows some aspects of Box2D continuous collision:\n// - bullet dynamic bodies which support continuous collision with non-bullet dynamic bodies\n// - prevention of chain reaction tunneling\n// Try disabling continuous collision and firing a bullet. You might see a bullet push a\n// a through the static wall.\nclass VerticalStack : public Sample\n{\npublic:\n\tenum\n\t{\n\t\te_maxColumns = 10,\n\t\te_maxRows = 15,\n\t\te_maxBullets = 8\n\t};\n\n\tenum ShapeType\n\t{\n\t\te_circleShape = 0,\n\t\te_boxShape\n\t};\n\n\texplicit VerticalStack( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { -7.0f, 9.0f };\n\t\t\tm_context->camera.zoom = 14.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, 0.0f };\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\tb2Segment segment = { { 10.0f, 0.0f }, { 10.0f, 20.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tsegment = { { -30.0f, 0.0f }, { 30.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tfor ( int i = 0; i < e_maxRows * e_maxColumns; ++i )\n\t\t{\n\t\t\tm_bodies[i] = b2_nullBodyId;\n\t\t}\n\n\t\tfor ( int i = 0; i < e_maxBullets; ++i )\n\t\t{\n\t\t\tm_bullets[i] = b2_nullBodyId;\n\t\t}\n\n\t\tm_shapeType = e_boxShape;\n\t\tm_rowCount = 12;\n\t\tm_columnCount = 1;\n\t\tm_bulletCount = 1;\n\t\tm_bulletType = e_circleShape;\n\n\t\tCreateStacks();\n\t}\n\n\tvoid CreateStacks()\n\t{\n\t\tfor ( int i = 0; i < e_maxRows * e_maxColumns; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_bodies[i] ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_bodies[i] );\n\t\t\t\tm_bodies[i] = b2_nullBodyId;\n\t\t\t}\n\t\t}\n\n\t\tb2Circle circle = {};\n\t\tcircle.radius = 0.5f;\n\n\t\tb2Polygon box = b2MakeRoundedBox( 0.45f, 0.45f, 0.05f );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 1.0f;\n\t\tshapeDef.material.friction = 0.3f;\n\n\t\tfloat offset;\n\n\t\tif ( m_shapeType == e_circleShape )\n\t\t{\n\t\t\toffset = 0.0f;\n\t\t}\n\t\telse\n\t\t{\n\t\t\toffset = 0.01f;\n\t\t}\n\n\t\tfloat dx = -3.0f;\n\t\tfloat xroot = 8.0f;\n\n\t\tfor ( int j = 0; j < m_columnCount; ++j )\n\t\t{\n\t\t\tfloat x = xroot + j * dx;\n\n\t\t\tfor ( int i = 0; i < m_rowCount; ++i )\n\t\t\t{\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\t\tint n = j * m_rowCount + i;\n\n\t\t\t\tfloat shift = ( i % 2 == 0 ? -offset : offset );\n\t\t\t\tbodyDef.position = { x + shift, 0.5f + 1.0f * i };\n\t\t\t\t// bodyDef.position = {x + shift, 1.0f + 1.51f * i};\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\t\tm_bodies[n] = bodyId;\n\n\t\t\t\tif ( m_shapeType == e_circleShape )\n\t\t\t\t{\n\t\t\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid DestroyBody()\n\t{\n\t\tfor ( int j = 0; j < m_columnCount; ++j )\n\t\t{\n\t\t\tfor ( int i = 0; i < m_rowCount; ++i )\n\t\t\t{\n\t\t\t\tint n = j * m_rowCount + i;\n\n\t\t\t\tif ( B2_IS_NON_NULL( m_bodies[n] ) )\n\t\t\t\t{\n\t\t\t\t\tb2DestroyBody( m_bodies[n] );\n\t\t\t\t\tm_bodies[n] = b2_nullBodyId;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid DestroyBullets()\n\t{\n\t\tfor ( int i = 0; i < e_maxBullets; ++i )\n\t\t{\n\t\t\tb2BodyId bullet = m_bullets[i];\n\n\t\t\tif ( B2_IS_NON_NULL( bullet ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( bullet );\n\t\t\t\tm_bullets[i] = b2_nullBodyId;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid FireBullets()\n\t{\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.25f };\n\t\tb2Polygon box = b2MakeBox( 0.25f, 0.25f );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 4.0f;\n\n\t\tfor ( int i = 0; i < m_bulletCount; ++i )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\tbodyDef.position = { -26.7f - i, 6.0f };\n\t\t\tfloat speed = RandomFloatRange( 200.0f, 300.0f );\n\t\t\tbodyDef.linearVelocity = { speed, 0.0f };\n\t\t\tbodyDef.isBullet = true;\n\n\t\t\tb2BodyId bullet = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tif ( m_bulletType == e_boxShape )\n\t\t\t{\n\t\t\t\tb2CreatePolygonShape( bullet, &shapeDef, &box );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2CreateCircleShape( bullet, &shapeDef, &circle );\n\t\t\t}\n\t\t\tassert( B2_IS_NULL( m_bullets[i] ) );\n\t\t\tm_bullets[i] = bullet;\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 230.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );\n\n\t\tImGui::Begin( \"Vertical Stack\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::PushItemWidth( 120.0f );\n\n\t\tbool changed = false;\n\t\tconst char* shapeTypes[] = { \"Circle\", \"Box\" };\n\n\t\tint shapeType = int( m_shapeType );\n\t\tchanged = changed || ImGui::Combo( \"Shape\", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) );\n\t\tm_shapeType = ShapeType( shapeType );\n\n\t\tchanged = changed || ImGui::SliderInt( \"Rows\", &m_rowCount, 1, e_maxRows );\n\t\tchanged = changed || ImGui::SliderInt( \"Columns\", &m_columnCount, 1, e_maxColumns );\n\n\t\tImGui::SliderInt( \"Bullets\", &m_bulletCount, 1, e_maxBullets );\n\n\t\tint bulletType = int( m_bulletType );\n\t\tImGui::Combo( \"Bullet Shape\", &bulletType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) );\n\t\tm_bulletType = ShapeType( bulletType );\n\n\t\tImGui::PopItemWidth();\n\n\t\tif ( ImGui::Button( \"Fire Bullets\" ) || glfwGetKey( m_context->window, GLFW_KEY_B ) == GLFW_PRESS )\n\t\t{\n\t\t\tDestroyBullets();\n\t\t\tFireBullets();\n\t\t}\n\n\t\tif ( ImGui::Button( \"Destroy Body\" ) )\n\t\t{\n\t\t\tDestroyBody();\n\t\t}\n\n\t\tchanged = changed || ImGui::Button( \"Reset Stack\" );\n\n\t\tif ( changed )\n\t\t{\n\t\t\tDestroyBullets();\n\t\t\tCreateStacks();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new VerticalStack( context );\n\t}\n\n\tb2BodyId m_bullets[e_maxBullets];\n\tb2BodyId m_bodies[e_maxRows * e_maxColumns];\n\tint m_columnCount;\n\tint m_rowCount;\n\tint m_bulletCount;\n\tShapeType m_shapeType;\n\tShapeType m_bulletType;\n};\n\nstatic int sampleVerticalStack = RegisterSample( \"Stacking\", \"Vertical Stack\", VerticalStack::Create );\n\n// A simple circle stack that also shows how to collect hit events\nclass CircleStack : public Sample\n{\npublic:\n\tstruct Event\n\t{\n\t\tint indexA, indexB;\n\t};\n\n\texplicit CircleStack( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 6.0f;\n\t\t}\n\n\t\tint shapeIndex = 0;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.userData = reinterpret_cast<void*>( intptr_t( shapeIndex ) );\n\t\t\tshapeIndex += 1;\n\n\t\t\tb2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2World_SetGravity( m_worldId, { 0.0f, -20.0f } );\n\t\tb2World_SetContactTuning( m_worldId, 0.25f * 360.0f, 10.0f, 3.0f );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tb2Circle circle = {};\n\t\tcircle.radius = 0.5f;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.enableHitEvents = true;\n\t\t// shapeDef.rollingResistance = 0.2f;\n\t\tshapeDef.material.friction = 0.0f;\n\n\t\tfloat y = 0.75f;\n\n\t\tfor ( int i = 0; i < 10; ++i )\n\t\t{\n\t\t\tbodyDef.position.y = y;\n\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tshapeDef.userData = reinterpret_cast<void*>( intptr_t( shapeIndex ) );\n\t\t\tshapeDef.density = 1.0f + 4.0f * i;\n\t\t\tshapeIndex += 1;\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\n\t\t\ty += 1.25f;\n\t\t}\n\t}\n\n\tvoid Step() override\n\t{\n\t\tSample::Step();\n\n\t\tb2ContactEvents events = b2World_GetContactEvents( m_worldId );\n\t\tfor ( int i = 0; i < events.hitCount; ++i )\n\t\t{\n\t\t\tb2ContactHitEvent* event = events.hitEvents + i;\n\n\t\t\tvoid* userDataA = b2Shape_GetUserData( event->shapeIdA );\n\t\t\tvoid* userDataB = b2Shape_GetUserData( event->shapeIdB );\n\t\t\tint indexA = static_cast<int>( reinterpret_cast<intptr_t>( userDataA ) );\n\t\t\tint indexB = static_cast<int>( reinterpret_cast<intptr_t>( userDataB ) );\n\n\t\t\tDrawPoint( m_draw, event->point, 10.0f, b2_colorWhite );\n\n\t\t\tm_events.push_back( { indexA, indexB } );\n\t\t}\n\n\t\tint eventCount = (int)m_events.size();\n\t\tfor ( int i = 0; i < eventCount; ++i )\n\t\t{\n\t\t\tDrawTextLine( \"%d, %d\", m_events[i].indexA, m_events[i].indexB );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new CircleStack( context );\n\t}\n\n\tstd::vector<Event> m_events;\n};\n\nstatic int sampleCircleStack = RegisterSample( \"Stacking\", \"Circle Stack\", CircleStack::Create );\n\nclass CapsuleStack : public Sample\n{\npublic:\n\tstruct Event\n\t{\n\t\tint indexA, indexB;\n\t};\n\n\texplicit CapsuleStack( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t\tm_context->camera.zoom = 6.0f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, -1.0f };\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon polygon = b2MakeBox( 10.0f, 1.0f );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &polygon );\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tfloat a = 0.25f;\n\t\tb2Capsule capsule = { { -4.0f * a, 0.0f }, { 4.0f * a, 0.0f }, a };\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t// rolling resistance increases stacking stability\n\t\t// shapeDef.rollingResistance = 0.2f;\n\n\t\tfloat y = 2.0f * a;\n\n\t\tfor ( int i = 0; i < 20; ++i )\n\t\t{\n\t\t\tbodyDef.position.y = y;\n\t\t\t// bodyDef.position.x += ( i & 1 ) == 1 ? -0.5f * a : 0.5f * a;\n\t\t\t// bodyDef.linearVelocity = { 0.0f, -10.0f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\n\t\t\ty += 3.0f * a;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new CapsuleStack( context );\n\t}\n};\n\nstatic int sampleCapsuleStack = RegisterSample( \"Stacking\", \"Capsule Stack\", CapsuleStack::Create );\n\nclass Cliff : public Sample\n{\npublic:\n\texplicit Cliff( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t\tm_context->camera.center = { 0.0f, 5.0f };\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, 0.0f };\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Polygon box = b2MakeOffsetBox( 100.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tb2Segment segment = { { -14.0f, 4.0f }, { -8.0f, 4.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\tbox = b2MakeOffsetBox( 3.0f, 0.5f, { 0.0f, 4.0f }, b2Rot_identity );\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\tb2Capsule capsule = { { 8.5f, 4.0f }, { 13.5f, 4.0f }, 0.5f };\n\t\t\tb2CreateCapsuleShape( groundId, &shapeDef, &capsule );\n\t\t}\n\n\t\tm_flip = false;\n\n\t\tfor ( int i = 0; i < 9; ++i )\n\t\t{\n\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t}\n\n\t\tCreateBodies();\n\t}\n\n\tvoid CreateBodies()\n\t{\n\t\tfor ( int i = 0; i < 9; ++i )\n\t\t{\n\t\t\tif ( B2_IS_NON_NULL( m_bodyIds[i] ) )\n\t\t\t{\n\t\t\t\tb2DestroyBody( m_bodyIds[i] );\n\t\t\t\tm_bodyIds[i] = b2_nullBodyId;\n\t\t\t}\n\t\t}\n\n\t\tfloat sign = m_flip ? -1.0f : 1.0f;\n\n\t\tb2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f };\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\t\tb2Polygon square = b2MakeSquare( 0.5f );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t{\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.01f;\n\t\t\tbodyDef.linearVelocity = { 2.0f * sign, 0.0f };\n\n\t\t\tfloat offset = m_flip ? -4.0f : 0.0f;\n\n\t\t\tbodyDef.position = { -9.0f + offset, 4.25f };\n\t\t\tm_bodyIds[0] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCapsuleShape( m_bodyIds[0], &shapeDef, &capsule );\n\n\t\t\tbodyDef.position = { 2.0f + offset, 4.75f };\n\t\t\tm_bodyIds[1] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCapsuleShape( m_bodyIds[1], &shapeDef, &capsule );\n\n\t\t\tbodyDef.position = { 13.0f + offset, 4.75f };\n\t\t\tm_bodyIds[2] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCapsuleShape( m_bodyIds[2], &shapeDef, &capsule );\n\t\t}\n\n\t\t{\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.01f;\n\t\t\tbodyDef.linearVelocity = { 2.5f * sign, 0.0f };\n\n\t\t\tbodyDef.position = { -11.0f, 4.5f };\n\t\t\tm_bodyIds[3] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bodyIds[3], &shapeDef, &square );\n\n\t\t\tbodyDef.position = { 0.0f, 5.0f };\n\t\t\tm_bodyIds[4] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bodyIds[4], &shapeDef, &square );\n\n\t\t\tbodyDef.position = { 11.0f, 5.0f };\n\t\t\tm_bodyIds[5] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( m_bodyIds[5], &shapeDef, &square );\n\t\t}\n\n\t\t{\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tshapeDef.material.friction = 0.2f;\n\t\t\tbodyDef.linearVelocity = { 1.5f * sign, 0.0f };\n\n\t\t\tfloat offset = m_flip ? 4.0f : 0.0f;\n\n\t\t\tbodyDef.position = { -13.0f + offset, 4.5f };\n\t\t\tm_bodyIds[6] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCircleShape( m_bodyIds[6], &shapeDef, &circle );\n\n\t\t\tbodyDef.position = { -2.0f + offset, 5.0f };\n\t\t\tm_bodyIds[7] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCircleShape( m_bodyIds[7], &shapeDef, &circle );\n\n\t\t\tbodyDef.position = { 9.0f + offset, 5.0f };\n\t\t\tm_bodyIds[8] = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreateCircleShape( m_bodyIds[8], &shapeDef, &circle );\n\t\t}\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 60.0f;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 160.0f, height ) );\n\n\t\tImGui::Begin( \"Cliff\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tif ( ImGui::Button( \"Flip\" ) )\n\t\t{\n\t\t\tm_flip = !m_flip;\n\t\t\tCreateBodies();\n\t\t}\n\n\t\tImGui::End();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Cliff( context );\n\t}\n\n\tb2BodyId m_bodyIds[9];\n\tbool m_flip;\n};\n\nstatic int sampleCliff = RegisterSample( \"Stacking\", \"Cliff\", Cliff::Create );\n\nclass Arch : public Sample\n{\npublic:\n\texplicit Arch( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 8.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.35f;\n\t\t}\n\n\t\tb2Vec2 ps1[9] = { { 16.0f, 0.0f },\n\t\t\t\t\t\t  { 14.93803712795643f, 5.133601056842984f },\n\t\t\t\t\t\t  { 13.79871746027416f, 10.24928069555078f },\n\t\t\t\t\t\t  { 12.56252963284711f, 15.34107019122473f },\n\t\t\t\t\t\t  { 11.20040987372525f, 20.39856541571217f },\n\t\t\t\t\t\t  { 9.66521217819836f, 25.40369899225096f },\n\t\t\t\t\t\t  { 7.87179930638133f, 30.3179337000085f },\n\t\t\t\t\t\t  { 5.635199558196225f, 35.03820717801641f },\n\t\t\t\t\t\t  { 2.405937953536585f, 39.09554102558315f } };\n\n\t\tb2Vec2 ps2[9] = { { 24.0f, 0.0f },\n\t\t\t\t\t\t  { 22.33619528222415f, 6.02299846205841f },\n\t\t\t\t\t\t  { 20.54936888969905f, 12.00964361211476f },\n\t\t\t\t\t\t  { 18.60854610798073f, 17.9470321677465f },\n\t\t\t\t\t\t  { 16.46769273811807f, 23.81367936585418f },\n\t\t\t\t\t\t  { 14.05325025774858f, 29.57079353071012f },\n\t\t\t\t\t\t  { 11.23551045834022f, 35.13775818285372f },\n\t\t\t\t\t\t  { 7.752568160730571f, 40.30450679009583f },\n\t\t\t\t\t\t  { 3.016931552701656f, 44.28891593799322f } };\n\n\t\tfloat scale = 0.25f;\n\t\tfor ( int i = 0; i < 9; ++i )\n\t\t{\n\t\t\tps1[i] = b2MulSV( scale, ps1[i] );\n\t\t\tps2[i] = b2MulSV( scale, ps2[i] );\n\t\t}\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.friction = 0.6f;\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2Segment segment = { { -100.0f, 0.0f }, { 100.0f, 0.0f } };\n\t\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tfor ( int i = 0; i < 8; ++i )\n\t\t{\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2Vec2 ps[4] = { ps1[i], ps2[i], ps2[i + 1], ps1[i + 1] };\n\t\t\tb2Hull hull = b2ComputeHull( ps, 4 );\n\t\t\tb2Polygon polygon = b2MakePolygon( &hull, 0.0f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\t}\n\n\t\tfor ( int i = 0; i < 8; ++i )\n\t\t{\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2Vec2 ps[4] = { { -ps2[i].x, ps2[i].y },\n\t\t\t\t\t\t\t { -ps1[i].x, ps1[i].y },\n\t\t\t\t\t\t\t { -ps1[i + 1].x, ps1[i + 1].y },\n\t\t\t\t\t\t\t { -ps2[i + 1].x, ps2[i + 1].y } };\n\t\t\tb2Hull hull = b2ComputeHull( ps, 4 );\n\t\t\tb2Polygon polygon = b2MakePolygon( &hull, 0.0f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\t}\n\n\t\t{\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2Vec2 ps[4] = { ps1[8], ps2[8], { -ps2[8].x, ps2[8].y }, { -ps1[8].x, ps1[8].y } };\n\t\t\tb2Hull hull = b2ComputeHull( ps, 4 );\n\t\t\tb2Polygon polygon = b2MakePolygon( &hull, 0.0f );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\t}\n\n\t\tfor ( int i = 0; i < 4; ++i )\n\t\t{\n\t\t\tb2Polygon box = b2MakeBox( 2.0f, 0.5f );\n\t\t\tbodyDef.position = { 0.0f, 0.5f + ps2[8].y + 1.0f * i };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Arch( context );\n\t}\n};\n\nstatic int sampleArch = RegisterSample( \"Stacking\", \"Arch\", Arch::Create );\n\nclass DoubleDomino : public Sample\n{\npublic:\n\texplicit DoubleDomino( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 4.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.25f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.position = { 0.0f, -1.0f };\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2Polygon box = b2MakeBox( 100.0f, 1.0f );\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t\t}\n\n\t\tb2Polygon box = b2MakeBox( 0.125f, 0.5f );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.friction = 0.6f;\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tint count = 15;\n\t\tfloat x = -0.5f * count;\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tbodyDef.position = { x, 0.5f };\n\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\tif ( i == 0 )\n\t\t\t{\n\t\t\t\tb2Body_ApplyLinearImpulse( bodyId, b2Vec2{ 0.2f, 0.0f }, b2Vec2{ x, 1.0f }, true );\n\t\t\t}\n\n\t\t\tx += 1.0f;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new DoubleDomino( context );\n\t}\n};\n\nstatic int sampleDoubleDomino = RegisterSample( \"Stacking\", \"Double Domino\", DoubleDomino::Create );\n\nclass Confined : public Sample\n{\npublic:\n\texplicit Confined( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.0f, 10.0f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.5f;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\tb2Capsule capsule;\n\t\t\tcapsule = { { -10.5f, 0.0f }, { 10.5f, 0.0f }, 0.5f };\n\t\t\tb2CreateCapsuleShape( groundId, &shapeDef, &capsule );\n\t\t\tcapsule = { { -10.5f, 0.0f }, { -10.5f, 20.5f }, 0.5f };\n\t\t\tb2CreateCapsuleShape( groundId, &shapeDef, &capsule );\n\t\t\tcapsule = { { 10.5f, 0.0f }, { 10.5f, 20.5f }, 0.5f };\n\t\t\tb2CreateCapsuleShape( groundId, &shapeDef, &capsule );\n\t\t\tcapsule = { { -10.5f, 20.5f }, { 10.5f, 20.5f }, 0.5f };\n\t\t\tb2CreateCapsuleShape( groundId, &shapeDef, &capsule );\n\t\t}\n\n\t\tm_row = 0;\n\t\tm_column = 0;\n\t\tm_count = 0;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.gravityScale = 0.0f;\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2Circle circle = { { 0.0f, 0.0f }, 0.5f };\n\n\t\twhile ( m_count < m_maxCount )\n\t\t{\n\t\t\tm_row = 0;\n\t\t\tfor ( int i = 0; i < m_gridCount; ++i )\n\t\t\t{\n\t\t\t\tfloat x = -8.75f + m_column * 18.0f / m_gridCount;\n\t\t\t\tfloat y = 1.5f + m_row * 18.0f / m_gridCount;\n\n\t\t\t\tbodyDef.position = { x, y };\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\n\t\t\t\tm_count += 1;\n\t\t\t\tm_row += 1;\n\t\t\t}\n\t\t\tm_column += 1;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new Confined( context );\n\t}\n\n\tstatic constexpr int m_gridCount = 25;\n\tstatic constexpr int m_maxCount = m_gridCount * m_gridCount;\n\tint m_row;\n\tint m_column;\n\tint m_count;\n};\n\nstatic int sampleConfined = RegisterSample( \"Stacking\", \"Confined\", Confined::Create );\n\n// From PEEL\nclass CardHouse : public Sample\n{\npublic:\n\texplicit CardHouse( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = { 0.75f, 0.9f };\n\t\t\tm_context->camera.zoom = 25.0f * 0.05f;\n\t\t}\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.position = { 0.0f, -2.0f };\n\t\tb2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.friction = 0.7f;\n\n\t\tb2Polygon groundBox = b2MakeBox( 40.0f, 2.0f );\n\t\tb2CreatePolygonShape( groundId, &shapeDef, &groundBox );\n\n\t\tfloat cardHeight = 0.2f;\n\t\tfloat cardThickness = 0.001f;\n\n\t\tfloat angle0 = 25.0f * B2_PI / 180.0f;\n\t\tfloat angle1 = -25.0f * B2_PI / 180.0f;\n\t\tfloat angle2 = 0.5f * B2_PI;\n\n\t\tb2Polygon cardBox = b2MakeBox( cardThickness, cardHeight );\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tint Nb = 5;\n\t\tfloat z0 = 0.0f;\n\t\tfloat y = cardHeight - 0.02f;\n\t\twhile ( Nb )\n\t\t{\n\t\t\tfloat z = z0;\n\t\t\tfor ( int i = 0; i < Nb; i++ )\n\t\t\t{\n\t\t\t\tif ( i != Nb - 1 )\n\t\t\t\t{\n\t\t\t\t\tbodyDef.position = { z + 0.25f, y + cardHeight - 0.015f };\n\t\t\t\t\tbodyDef.rotation = b2MakeRot( angle2 );\n\t\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &cardBox );\n\t\t\t\t}\n\n\t\t\t\tbodyDef.position = { z, y };\n\t\t\t\tbodyDef.rotation = b2MakeRot( angle1 );\n\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &cardBox );\n\n\t\t\t\tz += 0.175f;\n\n\t\t\t\tbodyDef.position = { z, y };\n\t\t\t\tbodyDef.rotation = b2MakeRot( angle0 );\n\t\t\t\tbodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &cardBox );\n\n\t\t\t\tz += 0.175f;\n\t\t\t}\n\t\t\ty += cardHeight * 2.0f - 0.03f;\n\t\t\tz0 += 0.175f;\n\t\t\tNb--;\n\t\t}\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new CardHouse( context );\n\t}\n};\n\nstatic int sampleCardHouse = RegisterSample( \"Stacking\", \"Card House\", CardHouse::Create );\n"
  },
  {
    "path": "samples/sample_world.cpp",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"car.h\"\n#include \"donut.h\"\n#include \"draw.h\"\n#include \"human.h\"\n#include \"sample.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <GLFW/glfw3.h>\n#include <imgui.h>\n\nclass LargeWorld : public Sample\n{\npublic:\n\texplicit LargeWorld( SampleContext* context )\n\t\t: Sample( context )\n\t{\n\t\tm_period = 40.0f;\n\t\tfloat omega = 2.0f * B2_PI / m_period;\n\t\tm_cycleCount = m_isDebug ? 10 : 600;\n\t\tm_gridSize = 1.0f;\n\t\tm_gridCount = (int)( m_cycleCount * m_period / m_gridSize );\n\n\t\tfloat xStart = -0.5f * ( m_cycleCount * m_period );\n\n\t\tm_viewPosition = { xStart, 15.0f };\n\n\t\tif ( m_context->restart == false )\n\t\t{\n\t\t\tm_context->camera.center = m_viewPosition;\n\t\t\tm_context->camera.zoom = 25.0f * 1.0f;\n\t\t\tm_context->debugDraw.drawJoints = false;\n\t\t}\n\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\t\t// Setting this to false significantly reduces the cost of creating\n\t\t\t// static bodies and shapes.\n\t\t\tshapeDef.invokeContactCreation = false;\n\n\t\t\tfloat height = 4.0f;\n\t\t\tfloat xBody = xStart;\n\t\t\tfloat xShape = xStart;\n\n\t\t\tb2BodyId groundId;\n\n\t\t\tfor ( int i = 0; i < m_gridCount; ++i )\n\t\t\t{\n\t\t\t\t// Create a new body regularly so that shapes are not too far from the body origin.\n\t\t\t\t// Most algorithms in Box2D work in local coordinates, but contact points are computed\n\t\t\t\t// relative to the body origin.\n\t\t\t\t// This makes a noticeable improvement in stability far from the origin.\n\t\t\t\tif ( i % 10 == 0 )\n\t\t\t\t{\n\t\t\t\t\tbodyDef.position.x = xBody;\n\t\t\t\t\tgroundId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\t\txShape = 0.0f;\n\t\t\t\t}\n\n\t\t\t\tfloat y = 0.0f;\n\n\t\t\t\tint ycount = (int)roundf( height * cosf( omega * xBody ) ) + 12;\n\n\t\t\t\tfor ( int j = 0; j < ycount; ++j )\n\t\t\t\t{\n\t\t\t\t\tb2Polygon square = b2MakeOffsetBox( 0.4f * m_gridSize, 0.4f * m_gridSize, { xShape, y }, b2Rot_identity );\n\t\t\t\t\tsquare.radius = 0.1f;\n\t\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &square );\n\n\t\t\t\t\ty += m_gridSize;\n\t\t\t\t}\n\n\t\t\t\txBody += m_gridSize;\n\t\t\t\txShape += m_gridSize;\n\t\t\t}\n\t\t}\n\n\t\tint humanIndex = 0;\n\t\tfor ( int cycleIndex = 0; cycleIndex < m_cycleCount; ++cycleIndex )\n\t\t{\n\t\t\tfloat xbase = ( 0.5f + cycleIndex ) * m_period + xStart;\n\n\t\t\tint remainder = cycleIndex % 3;\n\t\t\tif ( remainder == 0 )\n\t\t\t{\n\t\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t\tbodyDef.position = { xbase - 3.0f, 10.0f };\n\n\t\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\t\tb2Polygon box = b2MakeBox( 0.3f, 0.2f );\n\n\t\t\t\tfor ( int i = 0; i < 10; ++i )\n\t\t\t\t{\n\t\t\t\t\tbodyDef.position.y = 10.0f;\n\t\t\t\t\tfor ( int j = 0; j < 5; ++j )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );\n\t\t\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t\t\t\t\tbodyDef.position.y += 0.5f;\n\t\t\t\t\t}\n\t\t\t\t\tbodyDef.position.x += 0.6f;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( remainder == 1 )\n\t\t\t{\n\t\t\t\tb2Vec2 position = { xbase - 2.0f, 10.0f };\n\t\t\t\tfor ( int i = 0; i < 5; ++i )\n\t\t\t\t{\n\t\t\t\t\tHuman human = {};\n\t\t\t\t\tCreateHuman( &human, m_worldId, position, 1.5f, 0.05f, 0.0f, 0.0f, humanIndex + 1, nullptr, false );\n\t\t\t\t\thumanIndex += 1;\n\t\t\t\t\tposition.x += 1.0f;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2Vec2 position = { xbase - 4.0f, 12.0f };\n\n\t\t\t\tfor ( int i = 0; i < 5; ++i )\n\t\t\t\t{\n\t\t\t\t\tDonut donut;\n\t\t\t\t\tdonut.Create( m_worldId, position, 0.75f, 0, false, nullptr );\n\t\t\t\t\tposition.x += 2.0f;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tm_car.Spawn( m_worldId, { xStart + 20.0f, 40.0f }, 10.0f, 2.0f, 0.7f, 2000.0f, nullptr );\n\n\t\tm_cycleIndex = 0;\n\t\tm_speed = 0.0f;\n\t\tm_explosionPosition = { ( 0.5f + m_cycleIndex ) * m_period + xStart, 7.0f };\n\t\tm_explode = true;\n\t\tm_followCar = false;\n\t}\n\n\tvoid UpdateGui() override\n\t{\n\t\tfloat fontSize = ImGui::GetFontSize();\n\t\tfloat height = 13.0f * fontSize;\n\t\tImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );\n\t\tImGui::SetNextWindowSize( ImVec2( 18.0f * fontSize, height ) );\n\n\t\tImGui::Begin( \"Large World\", nullptr, ImGuiWindowFlags_NoResize );\n\n\t\tImGui::SliderFloat( \"speed\", &m_speed, -400.0f, 400.0f, \"%.0f\" );\n\t\tif ( ImGui::Button( \"stop\" ) )\n\t\t{\n\t\t\tm_speed = 0.0f;\n\t\t}\n\n\t\tImGui::Checkbox( \"explode\", &m_explode );\n\t\tImGui::Checkbox( \"follow car\", &m_followCar );\n\n\t\tImGui::Text( \"world size = %g kilometers\", m_gridSize * m_gridCount / 1000.0f );\n\t\tImGui::End();\n\t}\n\n\tvoid Step() override\n\t{\n\t\tfloat span = 0.5f * ( m_period * m_cycleCount );\n\t\tfloat timeStep = m_context->hertz > 0.0f ? 1.0f / m_context->hertz : 0.0f;\n\n\t\tif ( m_context->pause )\n\t\t{\n\t\t\ttimeStep = 0.0f;\n\t\t}\n\n\t\tm_viewPosition.x += timeStep * m_speed;\n\t\tm_viewPosition.x = b2ClampFloat( m_viewPosition.x, -span, span );\n\n\t\tif ( m_speed != 0.0f )\n\t\t{\n\t\t\tm_context->camera.center = m_viewPosition;\n\t\t}\n\n\t\tif ( m_followCar )\n\t\t{\n\t\t\tm_context->camera.center.x = b2Body_GetPosition( m_car.m_chassisId ).x;\n\t\t}\n\n\t\tfloat radius = 2.0f;\n\t\tif ( ( m_stepCount & 0x1 ) == 0x1 && m_explode )\n\t\t{\n\t\t\tm_explosionPosition.x = ( 0.5f + m_cycleIndex ) * m_period - span;\n\n\t\t\tb2ExplosionDef def = b2DefaultExplosionDef();\n\t\t\tdef.position = m_explosionPosition;\n\t\t\tdef.radius = radius;\n\t\t\tdef.falloff = 0.1f;\n\t\t\tdef.impulsePerLength = 1.0f;\n\t\t\tb2World_Explode( m_worldId, &def );\n\n\t\t\tm_cycleIndex = ( m_cycleIndex + 1 ) % m_cycleCount;\n\t\t}\n\n\t\tif ( m_explode )\n\t\t{\n\t\t\tDrawCircle( m_draw, m_explosionPosition, radius, b2_colorAzure );\n\t\t}\n\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_A ) == GLFW_PRESS )\n\t\t{\n\t\t\tm_car.SetSpeed( 20.0f );\n\t\t}\n\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_S ) == GLFW_PRESS )\n\t\t{\n\t\t\tm_car.SetSpeed( 0.0f );\n\t\t}\n\n\t\tif ( glfwGetKey( m_context->window, GLFW_KEY_D ) == GLFW_PRESS )\n\t\t{\n\t\t\tm_car.SetSpeed( -5.0f );\n\t\t}\n\n\t\tSample::Step();\n\t}\n\n\tstatic Sample* Create( SampleContext* context )\n\t{\n\t\treturn new LargeWorld( context );\n\t}\n\n\tCar m_car;\n\tb2Vec2 m_viewPosition;\n\tfloat m_period;\n\tint m_cycleCount;\n\tint m_cycleIndex;\n\tfloat m_gridCount;\n\tfloat m_gridSize;\n\tfloat m_speed;\n\n\tb2Vec2 m_explosionPosition;\n\tbool m_explode;\n\tbool m_followCar;\n};\n\nstatic int sampleLargeWorld = RegisterSample( \"World\", \"Large World\", LargeWorld::Create );\n"
  },
  {
    "path": "samples/shader.c",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"shader.h\"\n\n#include <assert.h>\n#include <stdbool.h>\n#include <glad/glad.h>\n#include <stdio.h>\n\n#if defined( _MSC_VER )\n\t#define _CRTDBG_MAP_ALLOC\n\t#include <crtdbg.h>\n\t#include <stdlib.h>\n#else\n\t#include <stdlib.h>\n#endif\n\nvoid DumpInfoGL()\n{\n\tconst char* renderer = (const char*)glGetString( GL_RENDERER );\n\tconst char* vendor = (const char*)glGetString( GL_VENDOR );\n\tconst char* version = (const char*)glGetString( GL_VERSION );\n\tconst char* glslVersion = (const char*)glGetString( GL_SHADING_LANGUAGE_VERSION );\n\n\tint major, minor;\n\tglGetIntegerv( GL_MAJOR_VERSION, &major );\n\tglGetIntegerv( GL_MINOR_VERSION, &minor );\n\n\tprintf( \"-------------------------------------------------------------\\n\" );\n\tprintf( \"GL Vendor    : %s\\n\", vendor );\n\tprintf( \"GL Renderer  : %s\\n\", renderer );\n\tprintf( \"GL Version   : %s\\n\", version );\n\tprintf( \"GL Version   : %d.%d\\n\", major, minor );\n\tprintf( \"GLSL Version : %s\\n\", glslVersion );\n\tprintf( \"-------------------------------------------------------------\\n\" );\n}\n\nvoid CheckOpenGL()\n{\n\tGLenum errCode = glGetError();\n\tif ( errCode != GL_NO_ERROR )\n\t{\n\t\tprintf( \"OpenGL error = %d\\n\", errCode );\n\t\tassert( false );\n\t}\n}\n\nvoid PrintLogGL( uint32_t object )\n{\n\tGLint log_length = 0;\n\tif ( glIsShader( object ) )\n\t{\n\t\tglGetShaderiv( object, GL_INFO_LOG_LENGTH, &log_length );\n\t}\n\telse if ( glIsProgram( object ) )\n\t{\n\t\tglGetProgramiv( object, GL_INFO_LOG_LENGTH, &log_length );\n\t}\n\telse\n\t{\n\t\tprintf( \"PrintLogGL: Not a shader or a program\\n\" );\n\t\treturn;\n\t}\n\n\tchar* log = (char*)malloc( log_length );\n\n\tif ( glIsShader( object ) )\n\t{\n\t\tglGetShaderInfoLog( object, log_length, NULL, log );\n\t}\n\telse if ( glIsProgram( object ) )\n\t{\n\t\tglGetProgramInfoLog( object, log_length, NULL, log );\n\t}\n\n\tprintf( \"PrintLogGL: %s\", log );\n\tfree( log );\n}\n\nstatic GLuint sCreateShaderFromString( const char* source, GLenum type )\n{\n\tGLuint shader = glCreateShader( type );\n\tconst char* sources[] = { source };\n\n\tglShaderSource( shader, 1, sources, NULL );\n\tglCompileShader( shader );\n\n\tint success = GL_FALSE;\n\tglGetShaderiv( shader, GL_COMPILE_STATUS, &success );\n\n\tif ( success == GL_FALSE )\n\t{\n\t\tprintf( \"Error compiling shader of type %d!\\n\", type );\n\t\tPrintLogGL( shader );\n\t\tglDeleteShader( shader );\n\t\treturn 0;\n\t}\n\n\treturn shader;\n}\n\nuint32_t CreateProgramFromStrings( const char* vertexString, const char* fragmentString )\n{\n\tGLuint vertex = sCreateShaderFromString( vertexString, GL_VERTEX_SHADER );\n\tif ( vertex == 0 )\n\t{\n\t\treturn 0;\n\t}\n\n\tGLuint fragment = sCreateShaderFromString( fragmentString, GL_FRAGMENT_SHADER );\n\tif ( fragment == 0 )\n\t{\n\t\treturn 0;\n\t}\n\n\tGLuint program = glCreateProgram();\n\tglAttachShader( program, vertex );\n\tglAttachShader( program, fragment );\n\n\tglLinkProgram( program );\n\n\tint success = GL_FALSE;\n\tglGetProgramiv( program, GL_LINK_STATUS, &success );\n\tif ( success == GL_FALSE )\n\t{\n\t\tprintf( \"glLinkProgram:\" );\n\t\tPrintLogGL( program );\n\t\treturn 0;\n\t}\n\n\tglDeleteShader( vertex );\n\tglDeleteShader( fragment );\n\n\treturn program;\n}\n\nstatic GLuint sCreateShaderFromFile( const char* filename, GLenum type )\n{\n\tFILE* file = fopen( filename, \"rb\" );\n\tif ( file == NULL )\n\t{\n\t\tfprintf( stderr, \"Error opening %s\\n\", filename );\n\t\treturn 0;\n\t}\n\n\tfseek( file, 0, SEEK_END );\n\tlong size = ftell( file );\n\tfseek( file, 0, SEEK_SET );\n\n\tchar* source = malloc( size + 1 );\n\tsize_t count = fread( source, size, 1, file );\n\t(void) count;\n\tfclose( file );\n\n\tsource[size] = 0;\n\n\tGLuint shader = glCreateShader( type );\n\tconst char* sources[] = { source };\n\n\tglShaderSource( shader, 1, sources, NULL );\n\tglCompileShader( shader );\n\n\tint success = GL_FALSE;\n\tglGetShaderiv( shader, GL_COMPILE_STATUS, &success );\n\n\tif ( success == GL_FALSE )\n\t{\n\t\tfprintf( stderr, \"Error compiling shader of type %d!\\n\", type );\n\t\tPrintLogGL( shader );\n\t}\n\n\tfree( source );\n\treturn shader;\n}\n\nuint32_t CreateProgramFromFiles( const char* vertexPath, const char* fragmentPath )\n{\n\tGLuint vertex = sCreateShaderFromFile( vertexPath, GL_VERTEX_SHADER );\n\tif ( vertex == 0 )\n\t{\n\t\treturn 0;\n\t}\n\n\tGLuint fragment = sCreateShaderFromFile( fragmentPath, GL_FRAGMENT_SHADER );\n\tif ( fragment == 0 )\n\t{\n\t\treturn 0;\n\t}\n\n\tGLuint program = glCreateProgram();\n\tglAttachShader( program, vertex );\n\tglAttachShader( program, fragment );\n\n\tglLinkProgram( program );\n\n\tint success = GL_FALSE;\n\tglGetProgramiv( program, GL_LINK_STATUS, &success );\n\tif ( success == GL_FALSE )\n\t{\n\t\tprintf( \"glLinkProgram:\" );\n\t\tPrintLogGL( program );\n\t\treturn 0;\n\t}\n\n\tglDeleteShader( vertex );\n\tglDeleteShader( fragment );\n\n\treturn program;\n}\n"
  },
  {
    "path": "samples/shader.h",
    "content": "// SPDX-FileCopyrightText: 2024 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\nuint32_t CreateProgramFromFiles( const char* vertexPath, const char* fragmentPath );\nuint32_t CreateProgramFromStrings( const char* vertexString, const char* fragmentString );\n\nvoid CheckOpenGL();\nvoid DumpInfoGL();\nvoid PrintLogGL( uint32_t object );\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "samples/stb_image_write.h",
    "content": "/* stb_image_write - v1.16 - public domain - http://nothings.org/stb\n   writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015\n                                     no warranty implied; use at your own risk\n\n   Before #including,\n\n       #define STB_IMAGE_WRITE_IMPLEMENTATION\n\n   in the file that you want to have the implementation.\n\n   Will probably not work correctly with strict-aliasing optimizations.\n\nABOUT:\n\n   This header file is a library for writing images to C stdio or a callback.\n\n   The PNG output is not optimal; it is 20-50% larger than the file\n   written by a decent optimizing implementation; though providing a custom\n   zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that.\n   This library is designed for source code compactness and simplicity,\n   not optimal image file size or run-time performance.\n\nBUILDING:\n\n   You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.\n   You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace\n   malloc,realloc,free.\n   You can #define STBIW_MEMMOVE() to replace memmove()\n   You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function\n   for PNG compression (instead of the builtin one), it must have the following signature:\n   unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality);\n   The returned data will be freed with STBIW_FREE() (free() by default),\n   so it must be heap allocated with STBIW_MALLOC() (malloc() by default),\n\nUNICODE:\n\n   If compiling for Windows and you wish to use Unicode filenames, compile\n   with\n       #define STBIW_WINDOWS_UTF8\n   and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert\n   Windows wchar_t filenames to utf8.\n\nUSAGE:\n\n   There are five functions, one for each image file format:\n\n     int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);\n     int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);\n     int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);\n     int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality);\n     int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);\n\n     void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically\n\n   There are also five equivalent functions that use an arbitrary write function. You are\n   expected to open/close your file-equivalent before and after calling these:\n\n     int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data, int stride_in_bytes);\n     int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\n     int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\n     int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);\n     int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);\n\n   where the callback is:\n      void stbi_write_func(void *context, void *data, int size);\n\n   You can configure it with these global variables:\n      int stbi_write_tga_with_rle;             // defaults to true; set to 0 to disable RLE\n      int stbi_write_png_compression_level;    // defaults to 8; set to higher for more compression\n      int stbi_write_force_png_filter;         // defaults to -1; set to 0..5 to force a filter mode\n\n\n   You can define STBI_WRITE_NO_STDIO to disable the file variant of these\n   functions, so the library will not use stdio.h at all. However, this will\n   also disable HDR writing, because it requires stdio for formatted output.\n\n   Each function returns 0 on failure and non-0 on success.\n\n   The functions create an image file defined by the parameters. The image\n   is a rectangle of pixels stored from left-to-right, top-to-bottom.\n   Each pixel contains 'comp' channels of data stored interleaved with 8-bits\n   per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is\n   monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.\n   The *data pointer points to the first byte of the top-left-most pixel.\n   For PNG, \"stride_in_bytes\" is the distance in bytes from the first byte of\n   a row of pixels to the first byte of the next row of pixels.\n\n   PNG creates output files with the same number of components as the input.\n   The BMP format expands Y to RGB in the file format and does not\n   output alpha.\n\n   PNG supports writing rectangles of data even when the bytes storing rows of\n   data are not consecutive in memory (e.g. sub-rectangles of a larger image),\n   by supplying the stride between the beginning of adjacent rows. The other\n   formats do not. (Thus you cannot write a native-format BMP through the BMP\n   writer, both because it is in BGR order and because it may have padding\n   at the end of the line.)\n\n   PNG allows you to set the deflate compression level by setting the global\n   variable 'stbi_write_png_compression_level' (it defaults to 8).\n\n   HDR expects linear float data. Since the format is always 32-bit rgb(e)\n   data, alpha (if provided) is discarded, and for monochrome data it is\n   replicated across all three channels.\n\n   TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed\n   data, set the global variable 'stbi_write_tga_with_rle' to 0.\n\n   JPEG does ignore alpha channels in input data; quality is between 1 and 100.\n   Higher quality looks better but results in a bigger image.\n   JPEG baseline (no JPEG progressive).\n\nCREDITS:\n\n\n   Sean Barrett           -    PNG/BMP/TGA\n   Baldur Karlsson        -    HDR\n   Jean-Sebastien Guay    -    TGA monochrome\n   Tim Kelsey             -    misc enhancements\n   Alan Hickman           -    TGA RLE\n   Emmanuel Julien        -    initial file IO callback implementation\n   Jon Olick              -    original jo_jpeg.cpp code\n   Daniel Gibson          -    integrate JPEG, allow external zlib\n   Aarni Koskela          -    allow choosing PNG filter\n\n   bugfixes:\n      github:Chribba\n      Guillaume Chereau\n      github:jry2\n      github:romigrou\n      Sergio Gonzalez\n      Jonas Karlsson\n      Filip Wasil\n      Thatcher Ulrich\n      github:poppolopoppo\n      Patrick Boettcher\n      github:xeekworx\n      Cap Petschulat\n      Simon Rodriguez\n      Ivan Tikhonov\n      github:ignotion\n      Adam Schackart\n      Andrew Kensler\n\nLICENSE\n\n  See end of file for license information.\n\n*/\n\n#ifndef INCLUDE_STB_IMAGE_WRITE_H\n#define INCLUDE_STB_IMAGE_WRITE_H\n\n#include <stdlib.h>\n\n// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline'\n#ifndef STBIWDEF\n#ifdef STB_IMAGE_WRITE_STATIC\n#define STBIWDEF  static\n#else\n#ifdef __cplusplus\n#define STBIWDEF  extern \"C\"\n#else\n#define STBIWDEF  extern\n#endif\n#endif\n#endif\n\n#ifndef STB_IMAGE_WRITE_STATIC  // C++ forbids static forward declarations\nSTBIWDEF int stbi_write_tga_with_rle;\nSTBIWDEF int stbi_write_png_compression_level;\nSTBIWDEF int stbi_write_force_png_filter;\n#endif\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void  *data, int stride_in_bytes);\nSTBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);\nSTBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void  *data, int quality);\n\n#ifdef STBIW_WINDOWS_UTF8\nSTBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);\n#endif\n#endif\n\ntypedef void stbi_write_func(void *context, void *data, int size);\n\nSTBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data, int stride_in_bytes);\nSTBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);\nSTBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void  *data, int quality);\n\nSTBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);\n\n#endif//INCLUDE_STB_IMAGE_WRITE_H\n\n#ifdef STB_IMAGE_WRITE_IMPLEMENTATION\n\n#ifdef _WIN32\n   #ifndef _CRT_SECURE_NO_WARNINGS\n   #define _CRT_SECURE_NO_WARNINGS\n   #endif\n   #ifndef _CRT_NONSTDC_NO_DEPRECATE\n   #define _CRT_NONSTDC_NO_DEPRECATE\n   #endif\n#endif\n\n#ifndef STBI_WRITE_NO_STDIO\n#include <stdio.h>\n#endif // STBI_WRITE_NO_STDIO\n\n#include <stdarg.h>\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n\n#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))\n// ok\n#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)\n// ok\n#else\n#error \"Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED).\"\n#endif\n\n#ifndef STBIW_MALLOC\n#define STBIW_MALLOC(sz)        malloc(sz)\n#define STBIW_REALLOC(p,newsz)  realloc(p,newsz)\n#define STBIW_FREE(p)           free(p)\n#endif\n\n#ifndef STBIW_REALLOC_SIZED\n#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)\n#endif\n\n\n#ifndef STBIW_MEMMOVE\n#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)\n#endif\n\n\n#ifndef STBIW_ASSERT\n#include <assert.h>\n#define STBIW_ASSERT(x) assert(x)\n#endif\n\n#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)\n\n#ifdef STB_IMAGE_WRITE_STATIC\nstatic int stbi_write_png_compression_level = 8;\nstatic int stbi_write_tga_with_rle = 1;\nstatic int stbi_write_force_png_filter = -1;\n#else\nint stbi_write_png_compression_level = 8;\nint stbi_write_tga_with_rle = 1;\nint stbi_write_force_png_filter = -1;\n#endif\n\nstatic int stbi__flip_vertically_on_write = 0;\n\nSTBIWDEF void stbi_flip_vertically_on_write(int flag)\n{\n   stbi__flip_vertically_on_write = flag;\n}\n\ntypedef struct\n{\n   stbi_write_func *func;\n   void *context;\n   unsigned char buffer[64];\n   int buf_used;\n} stbi__write_context;\n\n// initialize a callback-based context\nstatic void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context)\n{\n   s->func    = c;\n   s->context = context;\n}\n\n#ifndef STBI_WRITE_NO_STDIO\n\nstatic void stbi__stdio_write(void *context, void *data, int size)\n{\n   fwrite(data,1,size,(FILE*) context);\n}\n\n#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8)\n#ifdef __cplusplus\n#define STBIW_EXTERN extern \"C\"\n#else\n#define STBIW_EXTERN extern\n#endif\nSTBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);\nSTBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);\n\nSTBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)\n{\n   return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);\n}\n#endif\n\nstatic FILE *stbiw__fopen(char const *filename, char const *mode)\n{\n   FILE *f;\n#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8)\n   wchar_t wMode[64];\n   wchar_t wFilename[1024];\n   if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))\n      return 0;\n\n   if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))\n      return 0;\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n   if (0 != _wfopen_s(&f, wFilename, wMode))\n      f = 0;\n#else\n   f = _wfopen(wFilename, wMode);\n#endif\n\n#elif defined(_MSC_VER) && _MSC_VER >= 1400\n   if (0 != fopen_s(&f, filename, mode))\n      f=0;\n#else\n   f = fopen(filename, mode);\n#endif\n   return f;\n}\n\nstatic int stbi__start_write_file(stbi__write_context *s, const char *filename)\n{\n   FILE *f = stbiw__fopen(filename, \"wb\");\n   stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);\n   return f != NULL;\n}\n\nstatic void stbi__end_write_file(stbi__write_context *s)\n{\n   fclose((FILE *)s->context);\n}\n\n#endif // !STBI_WRITE_NO_STDIO\n\ntypedef unsigned int stbiw_uint32;\ntypedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];\n\nstatic void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)\n{\n   while (*fmt) {\n      switch (*fmt++) {\n         case ' ': break;\n         case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));\n                     s->func(s->context,&x,1);\n                     break; }\n         case '2': { int x = va_arg(v,int);\n                     unsigned char b[2];\n                     b[0] = STBIW_UCHAR(x);\n                     b[1] = STBIW_UCHAR(x>>8);\n                     s->func(s->context,b,2);\n                     break; }\n         case '4': { stbiw_uint32 x = va_arg(v,int);\n                     unsigned char b[4];\n                     b[0]=STBIW_UCHAR(x);\n                     b[1]=STBIW_UCHAR(x>>8);\n                     b[2]=STBIW_UCHAR(x>>16);\n                     b[3]=STBIW_UCHAR(x>>24);\n                     s->func(s->context,b,4);\n                     break; }\n         default:\n            STBIW_ASSERT(0);\n            return;\n      }\n   }\n}\n\nstatic void stbiw__writef(stbi__write_context *s, const char *fmt, ...)\n{\n   va_list v;\n   va_start(v, fmt);\n   stbiw__writefv(s, fmt, v);\n   va_end(v);\n}\n\nstatic void stbiw__write_flush(stbi__write_context *s)\n{\n   if (s->buf_used) {\n      s->func(s->context, &s->buffer, s->buf_used);\n      s->buf_used = 0;\n   }\n}\n\nstatic void stbiw__putc(stbi__write_context *s, unsigned char c)\n{\n   s->func(s->context, &c, 1);\n}\n\nstatic void stbiw__write1(stbi__write_context *s, unsigned char a)\n{\n   if ((size_t)s->buf_used + 1 > sizeof(s->buffer))\n      stbiw__write_flush(s);\n   s->buffer[s->buf_used++] = a;\n}\n\nstatic void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)\n{\n   int n;\n   if ((size_t)s->buf_used + 3 > sizeof(s->buffer))\n      stbiw__write_flush(s);\n   n = s->buf_used;\n   s->buf_used = n+3;\n   s->buffer[n+0] = a;\n   s->buffer[n+1] = b;\n   s->buffer[n+2] = c;\n}\n\nstatic void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)\n{\n   unsigned char bg[3] = { 255, 0, 255}, px[3];\n   int k;\n\n   if (write_alpha < 0)\n      stbiw__write1(s, d[comp - 1]);\n\n   switch (comp) {\n      case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case\n      case 1:\n         if (expand_mono)\n            stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp\n         else\n            stbiw__write1(s, d[0]);  // monochrome TGA\n         break;\n      case 4:\n         if (!write_alpha) {\n            // composite against pink background\n            for (k = 0; k < 3; ++k)\n               px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;\n            stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);\n            break;\n         }\n         /* FALLTHROUGH */\n      case 3:\n         stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);\n         break;\n   }\n   if (write_alpha > 0)\n      stbiw__write1(s, d[comp - 1]);\n}\n\nstatic void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)\n{\n   stbiw_uint32 zero = 0;\n   int i,j, j_end;\n\n   if (y <= 0)\n      return;\n\n   if (stbi__flip_vertically_on_write)\n      vdir *= -1;\n\n   if (vdir < 0) {\n      j_end = -1; j = y-1;\n   } else {\n      j_end =  y; j = 0;\n   }\n\n   for (; j != j_end; j += vdir) {\n      for (i=0; i < x; ++i) {\n         unsigned char *d = (unsigned char *) data + (j*x+i)*comp;\n         stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);\n      }\n      stbiw__write_flush(s);\n      s->func(s->context, &zero, scanline_pad);\n   }\n}\n\nstatic int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)\n{\n   if (y < 0 || x < 0) {\n      return 0;\n   } else {\n      va_list v;\n      va_start(v, fmt);\n      stbiw__writefv(s, fmt, v);\n      va_end(v);\n      stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);\n      return 1;\n   }\n}\n\nstatic int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)\n{\n   if (comp != 4) {\n      // write RGB bitmap\n      int pad = (-x*3) & 3;\n      return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,\n              \"11 4 22 4\" \"4 44 22 444444\",\n              'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40,  // file header\n               40, x,y, 1,24, 0,0,0,0,0,0);             // bitmap header\n   } else {\n      // RGBA bitmaps need a v4 header\n      // use BI_BITFIELDS mode with 32bpp and alpha mask\n      // (straight BI_RGB with alpha mask doesn't work in most readers)\n      return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0,\n         \"11 4 22 4\" \"4 44 22 444444 4444 4 444 444 444 444\",\n         'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header\n         108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header\n   }\n}\n\nSTBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s = { 0 };\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_bmp_core(&s, x, y, comp, data);\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s = { 0 };\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_bmp_core(&s, x, y, comp, data);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif //!STBI_WRITE_NO_STDIO\n\nstatic int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data)\n{\n   int has_alpha = (comp == 2 || comp == 4);\n   int colorbytes = has_alpha ? comp-1 : comp;\n   int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3\n\n   if (y < 0 || x < 0)\n      return 0;\n\n   if (!stbi_write_tga_with_rle) {\n      return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,\n         \"111 221 2222 11\", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);\n   } else {\n      int i,j,k;\n      int jend, jdir;\n\n      stbiw__writef(s, \"111 221 2222 11\", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);\n\n      if (stbi__flip_vertically_on_write) {\n         j = 0;\n         jend = y;\n         jdir = 1;\n      } else {\n         j = y-1;\n         jend = -1;\n         jdir = -1;\n      }\n      for (; j != jend; j += jdir) {\n         unsigned char *row = (unsigned char *) data + j * x * comp;\n         int len;\n\n         for (i = 0; i < x; i += len) {\n            unsigned char *begin = row + i * comp;\n            int diff = 1;\n            len = 1;\n\n            if (i < x - 1) {\n               ++len;\n               diff = memcmp(begin, row + (i + 1) * comp, comp);\n               if (diff) {\n                  const unsigned char *prev = begin;\n                  for (k = i + 2; k < x && len < 128; ++k) {\n                     if (memcmp(prev, row + k * comp, comp)) {\n                        prev += comp;\n                        ++len;\n                     } else {\n                        --len;\n                        break;\n                     }\n                  }\n               } else {\n                  for (k = i + 2; k < x && len < 128; ++k) {\n                     if (!memcmp(begin, row + k * comp, comp)) {\n                        ++len;\n                     } else {\n                        break;\n                     }\n                  }\n               }\n            }\n\n            if (diff) {\n               unsigned char header = STBIW_UCHAR(len - 1);\n               stbiw__write1(s, header);\n               for (k = 0; k < len; ++k) {\n                  stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);\n               }\n            } else {\n               unsigned char header = STBIW_UCHAR(len - 129);\n               stbiw__write1(s, header);\n               stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);\n            }\n         }\n      }\n      stbiw__write_flush(s);\n   }\n   return 1;\n}\n\nSTBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s = { 0 };\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_tga_core(&s, x, y, comp, (void *) data);\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s = { 0 };\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif\n\n// *************************************************************************************************\n// Radiance RGBE HDR writer\n// by Baldur Karlsson\n\n#define stbiw__max(a, b)  ((a) > (b) ? (a) : (b))\n\n#ifndef STBI_WRITE_NO_STDIO\n\nstatic void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)\n{\n   int exponent;\n   float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));\n\n   if (maxcomp < 1e-32f) {\n      rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;\n   } else {\n      float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;\n\n      rgbe[0] = (unsigned char)(linear[0] * normalize);\n      rgbe[1] = (unsigned char)(linear[1] * normalize);\n      rgbe[2] = (unsigned char)(linear[2] * normalize);\n      rgbe[3] = (unsigned char)(exponent + 128);\n   }\n}\n\nstatic void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)\n{\n   unsigned char lengthbyte = STBIW_UCHAR(length+128);\n   STBIW_ASSERT(length+128 <= 255);\n   s->func(s->context, &lengthbyte, 1);\n   s->func(s->context, &databyte, 1);\n}\n\nstatic void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)\n{\n   unsigned char lengthbyte = STBIW_UCHAR(length);\n   STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code\n   s->func(s->context, &lengthbyte, 1);\n   s->func(s->context, data, length);\n}\n\nstatic void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)\n{\n   unsigned char scanlineheader[4] = { 2, 2, 0, 0 };\n   unsigned char rgbe[4];\n   float linear[3];\n   int x;\n\n   scanlineheader[2] = (width&0xff00)>>8;\n   scanlineheader[3] = (width&0x00ff);\n\n   /* skip RLE for images too small or large */\n   if (width < 8 || width >= 32768) {\n      for (x=0; x < width; x++) {\n         switch (ncomp) {\n            case 4: /* fallthrough */\n            case 3: linear[2] = scanline[x*ncomp + 2];\n                    linear[1] = scanline[x*ncomp + 1];\n                    linear[0] = scanline[x*ncomp + 0];\n                    break;\n            default:\n                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];\n                    break;\n         }\n         stbiw__linear_to_rgbe(rgbe, linear);\n         s->func(s->context, rgbe, 4);\n      }\n   } else {\n      int c,r;\n      /* encode into scratch buffer */\n      for (x=0; x < width; x++) {\n         switch(ncomp) {\n            case 4: /* fallthrough */\n            case 3: linear[2] = scanline[x*ncomp + 2];\n                    linear[1] = scanline[x*ncomp + 1];\n                    linear[0] = scanline[x*ncomp + 0];\n                    break;\n            default:\n                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];\n                    break;\n         }\n         stbiw__linear_to_rgbe(rgbe, linear);\n         scratch[x + width*0] = rgbe[0];\n         scratch[x + width*1] = rgbe[1];\n         scratch[x + width*2] = rgbe[2];\n         scratch[x + width*3] = rgbe[3];\n      }\n\n      s->func(s->context, scanlineheader, 4);\n\n      /* RLE each component separately */\n      for (c=0; c < 4; c++) {\n         unsigned char *comp = &scratch[width*c];\n\n         x = 0;\n         while (x < width) {\n            // find first run\n            r = x;\n            while (r+2 < width) {\n               if (comp[r] == comp[r+1] && comp[r] == comp[r+2])\n                  break;\n               ++r;\n            }\n            if (r+2 >= width)\n               r = width;\n            // dump up to first run\n            while (x < r) {\n               int len = r-x;\n               if (len > 128) len = 128;\n               stbiw__write_dump_data(s, len, &comp[x]);\n               x += len;\n            }\n            // if there's a run, output it\n            if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd\n               // find next byte after run\n               while (r < width && comp[r] == comp[x])\n                  ++r;\n               // output run up to r\n               while (x < r) {\n                  int len = r-x;\n                  if (len > 127) len = 127;\n                  stbiw__write_run_data(s, len, comp[x]);\n                  x += len;\n               }\n            }\n         }\n      }\n   }\n}\n\nstatic int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)\n{\n   if (y <= 0 || x <= 0 || data == NULL)\n      return 0;\n   else {\n      // Each component is stored separately. Allocate scratch space for full output scanline.\n      unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);\n      int i, len;\n      char buffer[128];\n      char header[] = \"#?RADIANCE\\n# Written by stb_image_write.h\\nFORMAT=32-bit_rle_rgbe\\n\";\n      s->func(s->context, header, sizeof(header)-1);\n\n#ifdef __STDC_LIB_EXT1__\n      len = sprintf_s(buffer, sizeof(buffer), \"EXPOSURE=          1.0000000000000\\n\\n-Y %d +X %d\\n\", y, x);\n#else\n      len = snprintf(buffer, sizeof(buffer), \"EXPOSURE=          1.0000000000000\\n\\n-Y %d +X %d\\n\", y, x);\n#endif\n      s->func(s->context, buffer, len);\n\n      for(i=0; i < y; i++)\n         stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i));\n      STBIW_FREE(scratch);\n      return 1;\n   }\n}\n\nSTBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)\n{\n   stbi__write_context s = { 0 };\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_hdr_core(&s, x, y, comp, (float *) data);\n}\n\nSTBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)\n{\n   stbi__write_context s = { 0 };\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif // STBI_WRITE_NO_STDIO\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// PNG writer\n//\n\n#ifndef STBIW_ZLIB_COMPRESS\n// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()\n#define stbiw__sbraw(a) ((int *) (void *) (a) - 2)\n#define stbiw__sbm(a)   stbiw__sbraw(a)[0]\n#define stbiw__sbn(a)   stbiw__sbraw(a)[1]\n\n#define stbiw__sbneedgrow(a,n)  ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a))\n#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0)\n#define stbiw__sbgrow(a,n)  stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a)))\n\n#define stbiw__sbpush(a, v)      (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v))\n#define stbiw__sbcount(a)        ((a) ? stbiw__sbn(a) : 0)\n#define stbiw__sbfree(a)         ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0)\n\nstatic void *stbiw__sbgrowf(void **arr, int increment, int itemsize)\n{\n   int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;\n   void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);\n   STBIW_ASSERT(p);\n   if (p) {\n      if (!*arr) ((int *) p)[1] = 0;\n      *arr = (void *) ((int *) p + 2);\n      stbiw__sbm(*arr) = m;\n   }\n   return *arr;\n}\n\nstatic unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)\n{\n   while (*bitcount >= 8) {\n      stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));\n      *bitbuffer >>= 8;\n      *bitcount -= 8;\n   }\n   return data;\n}\n\nstatic int stbiw__zlib_bitrev(int code, int codebits)\n{\n   int res=0;\n   while (codebits--) {\n      res = (res << 1) | (code & 1);\n      code >>= 1;\n   }\n   return res;\n}\n\nstatic unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit)\n{\n   int i;\n   for (i=0; i < limit && i < 258; ++i)\n      if (a[i] != b[i]) break;\n   return i;\n}\n\nstatic unsigned int stbiw__zhash(unsigned char *data)\n{\n   stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);\n   hash ^= hash << 3;\n   hash += hash >> 5;\n   hash ^= hash << 4;\n   hash += hash >> 17;\n   hash ^= hash << 25;\n   hash += hash >> 6;\n   return hash;\n}\n\n#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))\n#define stbiw__zlib_add(code,codebits) \\\n      (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())\n#define stbiw__zlib_huffa(b,c)  stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c)\n// default huffman tables\n#define stbiw__zlib_huff1(n)  stbiw__zlib_huffa(0x30 + (n), 8)\n#define stbiw__zlib_huff2(n)  stbiw__zlib_huffa(0x190 + (n)-144, 9)\n#define stbiw__zlib_huff3(n)  stbiw__zlib_huffa(0 + (n)-256,7)\n#define stbiw__zlib_huff4(n)  stbiw__zlib_huffa(0xc0 + (n)-280,8)\n#define stbiw__zlib_huff(n)  ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n))\n#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n))\n\n#define stbiw__ZHASH   16384\n\n#endif // STBIW_ZLIB_COMPRESS\n\nSTBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)\n{\n#ifdef STBIW_ZLIB_COMPRESS\n   // user provided a zlib compress implementation, use that\n   return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality);\n#else // use builtin\n   static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };\n   static unsigned char  lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,  4,  5,  5,  5,  5,  0 };\n   static unsigned short distc[]   = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };\n   static unsigned char  disteb[]  = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };\n   unsigned int bitbuf=0;\n   int i,j, bitcount=0;\n   unsigned char *out = NULL;\n   unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**));\n   if (hash_table == NULL)\n      return NULL;\n   if (quality < 5) quality = 5;\n\n   stbiw__sbpush(out, 0x78);   // DEFLATE 32K window\n   stbiw__sbpush(out, 0x5e);   // FLEVEL = 1\n   stbiw__zlib_add(1,1);  // BFINAL = 1\n   stbiw__zlib_add(1,2);  // BTYPE = 1 -- fixed huffman\n\n   for (i=0; i < stbiw__ZHASH; ++i)\n      hash_table[i] = NULL;\n\n   i=0;\n   while (i < data_len-3) {\n      // hash next 3 bytes of data to be compressed\n      int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;\n      unsigned char *bestloc = 0;\n      unsigned char **hlist = hash_table[h];\n      int n = stbiw__sbcount(hlist);\n      for (j=0; j < n; ++j) {\n         if (hlist[j]-data > i-32768) { // if entry lies within window\n            int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);\n            if (d >= best) { best=d; bestloc=hlist[j]; }\n         }\n      }\n      // when hash table entry is too long, delete half the entries\n      if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {\n         STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);\n         stbiw__sbn(hash_table[h]) = quality;\n      }\n      stbiw__sbpush(hash_table[h],data+i);\n\n      if (bestloc) {\n         // \"lazy matching\" - check match at *next* byte, and if it's better, do cur byte as literal\n         h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1);\n         hlist = hash_table[h];\n         n = stbiw__sbcount(hlist);\n         for (j=0; j < n; ++j) {\n            if (hlist[j]-data > i-32767) {\n               int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1);\n               if (e > best) { // if next match is better, bail on current match\n                  bestloc = NULL;\n                  break;\n               }\n            }\n         }\n      }\n\n      if (bestloc) {\n         int d = (int) (data+i - bestloc); // distance back\n         STBIW_ASSERT(d <= 32767 && best <= 258);\n         for (j=0; best > lengthc[j+1]-1; ++j);\n         stbiw__zlib_huff(j+257);\n         if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);\n         for (j=0; d > distc[j+1]-1; ++j);\n         stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5);\n         if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]);\n         i += best;\n      } else {\n         stbiw__zlib_huffb(data[i]);\n         ++i;\n      }\n   }\n   // write out final bytes\n   for (;i < data_len; ++i)\n      stbiw__zlib_huffb(data[i]);\n   stbiw__zlib_huff(256); // end of block\n   // pad with 0 bits to byte boundary\n   while (bitcount)\n      stbiw__zlib_add(0,1);\n\n   for (i=0; i < stbiw__ZHASH; ++i)\n      (void) stbiw__sbfree(hash_table[i]);\n   STBIW_FREE(hash_table);\n\n   // store uncompressed instead if compression was worse\n   if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) {\n      stbiw__sbn(out) = 2;  // truncate to DEFLATE 32K window and FLEVEL = 1\n      for (j = 0; j < data_len;) {\n         int blocklen = data_len - j;\n         if (blocklen > 32767) blocklen = 32767;\n         stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression\n         stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN\n         stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8));\n         stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN\n         stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8));\n         memcpy(out+stbiw__sbn(out), data+j, blocklen);\n         stbiw__sbn(out) += blocklen;\n         j += blocklen;\n      }\n   }\n\n   {\n      // compute adler32 on input\n      unsigned int s1=1, s2=0;\n      int blocklen = (int) (data_len % 5552);\n      j=0;\n      while (j < data_len) {\n         for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; }\n         s1 %= 65521; s2 %= 65521;\n         j += blocklen;\n         blocklen = 5552;\n      }\n      stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));\n      stbiw__sbpush(out, STBIW_UCHAR(s2));\n      stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));\n      stbiw__sbpush(out, STBIW_UCHAR(s1));\n   }\n   *out_len = stbiw__sbn(out);\n   // make returned pointer freeable\n   STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);\n   return (unsigned char *) stbiw__sbraw(out);\n#endif // STBIW_ZLIB_COMPRESS\n}\n\nstatic unsigned int stbiw__crc32(unsigned char *buffer, int len)\n{\n#ifdef STBIW_CRC32\n    return STBIW_CRC32(buffer, len);\n#else\n   static unsigned int crc_table[256] =\n   {\n      0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,\n      0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,\n      0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,\n      0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,\n      0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,\n      0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,\n      0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,\n      0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,\n      0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,\n      0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,\n      0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,\n      0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,\n      0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,\n      0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,\n      0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,\n      0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,\n      0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,\n      0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,\n      0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,\n      0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,\n      0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,\n      0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,\n      0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,\n      0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,\n      0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,\n      0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,\n      0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,\n      0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,\n      0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,\n      0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,\n      0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,\n      0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D\n   };\n\n   unsigned int crc = ~0u;\n   int i;\n   for (i=0; i < len; ++i)\n      crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];\n   return ~crc;\n#endif\n}\n\n#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4)\n#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));\n#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])\n\nstatic void stbiw__wpcrc(unsigned char **data, int len)\n{\n   unsigned int crc = stbiw__crc32(*data - len - 4, len+4);\n   stbiw__wp32(*data, crc);\n}\n\nstatic unsigned char stbiw__paeth(int a, int b, int c)\n{\n   int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);\n   if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);\n   if (pb <= pc) return STBIW_UCHAR(b);\n   return STBIW_UCHAR(c);\n}\n\n// @OPTIMIZE: provide an option that always forces left-predict or paeth predict\nstatic void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer)\n{\n   static int mapping[] = { 0,1,2,3,4 };\n   static int firstmap[] = { 0,1,0,5,6 };\n   int *mymap = (y != 0) ? mapping : firstmap;\n   int i;\n   int type = mymap[filter_type];\n   unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y);\n   int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;\n\n   if (type==0) {\n      memcpy(line_buffer, z, width*n);\n      return;\n   }\n\n   // first loop isn't optimized since it's just one pixel\n   for (i = 0; i < n; ++i) {\n      switch (type) {\n         case 1: line_buffer[i] = z[i]; break;\n         case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;\n         case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break;\n         case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break;\n         case 5: line_buffer[i] = z[i]; break;\n         case 6: line_buffer[i] = z[i]; break;\n      }\n   }\n   switch (type) {\n      case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break;\n      case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break;\n      case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break;\n      case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break;\n      case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break;\n      case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;\n   }\n}\n\nSTBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)\n{\n   int force_filter = stbi_write_force_png_filter;\n   int ctype[5] = { -1, 0, 4, 2, 6 };\n   unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };\n   unsigned char *out,*o, *filt, *zlib;\n   signed char *line_buffer;\n   int j,zlen;\n\n   if (stride_bytes == 0)\n      stride_bytes = x * n;\n\n   if (force_filter >= 5) {\n      force_filter = -1;\n   }\n\n   filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;\n   line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }\n   for (j=0; j < y; ++j) {\n      int filter_type;\n      if (force_filter > -1) {\n         filter_type = force_filter;\n         stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer);\n      } else { // Estimate the best filter by running through all of them:\n         int best_filter = 0, best_filter_val = 0x7fffffff, est, i;\n         for (filter_type = 0; filter_type < 5; filter_type++) {\n            stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer);\n\n            // Estimate the entropy of the line using this filter; the less, the better.\n            est = 0;\n            for (i = 0; i < x*n; ++i) {\n               est += abs((signed char) line_buffer[i]);\n            }\n            if (est < best_filter_val) {\n               best_filter_val = est;\n               best_filter = filter_type;\n            }\n         }\n         if (filter_type != best_filter) {  // If the last iteration already got us the best filter, don't redo it\n            stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer);\n            filter_type = best_filter;\n         }\n      }\n      // when we get here, filter_type contains the filter type, and line_buffer contains the data\n      filt[j*(x*n+1)] = (unsigned char) filter_type;\n      STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);\n   }\n   STBIW_FREE(line_buffer);\n   zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level);\n   STBIW_FREE(filt);\n   if (!zlib) return 0;\n\n   // each tag requires 12 bytes of overhead\n   out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);\n   if (!out) return 0;\n   *out_len = 8 + 12+13 + 12+zlen + 12;\n\n   o=out;\n   STBIW_MEMMOVE(o,sig,8); o+= 8;\n   stbiw__wp32(o, 13); // header length\n   stbiw__wptag(o, \"IHDR\");\n   stbiw__wp32(o, x);\n   stbiw__wp32(o, y);\n   *o++ = 8;\n   *o++ = STBIW_UCHAR(ctype[n]);\n   *o++ = 0;\n   *o++ = 0;\n   *o++ = 0;\n   stbiw__wpcrc(&o,13);\n\n   stbiw__wp32(o, zlen);\n   stbiw__wptag(o, \"IDAT\");\n   STBIW_MEMMOVE(o, zlib, zlen);\n   o += zlen;\n   STBIW_FREE(zlib);\n   stbiw__wpcrc(&o, zlen);\n\n   stbiw__wp32(o,0);\n   stbiw__wptag(o, \"IEND\");\n   stbiw__wpcrc(&o,0);\n\n   STBIW_ASSERT(o == out + *out_len);\n\n   return out;\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)\n{\n   FILE *f;\n   int len;\n   unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);\n   if (png == NULL) return 0;\n\n   f = stbiw__fopen(filename, \"wb\");\n   if (!f) { STBIW_FREE(png); return 0; }\n   fwrite(png, 1, len, f);\n   fclose(f);\n   STBIW_FREE(png);\n   return 1;\n}\n#endif\n\nSTBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)\n{\n   int len;\n   unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);\n   if (png == NULL) return 0;\n   func(context, png, len);\n   STBIW_FREE(png);\n   return 1;\n}\n\n\n/* ***************************************************************************\n *\n * JPEG writer\n *\n * This is based on Jon Olick's jo_jpeg.cpp:\n * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html\n */\n\nstatic const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,\n      24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };\n\nstatic void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) {\n   int bitBuf = *bitBufP, bitCnt = *bitCntP;\n   bitCnt += bs[1];\n   bitBuf |= bs[0] << (24 - bitCnt);\n   while(bitCnt >= 8) {\n      unsigned char c = (bitBuf >> 16) & 255;\n      stbiw__putc(s, c);\n      if(c == 255) {\n         stbiw__putc(s, 0);\n      }\n      bitBuf <<= 8;\n      bitCnt -= 8;\n   }\n   *bitBufP = bitBuf;\n   *bitCntP = bitCnt;\n}\n\nstatic void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) {\n   float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p;\n   float z1, z2, z3, z4, z5, z11, z13;\n\n   float tmp0 = d0 + d7;\n   float tmp7 = d0 - d7;\n   float tmp1 = d1 + d6;\n   float tmp6 = d1 - d6;\n   float tmp2 = d2 + d5;\n   float tmp5 = d2 - d5;\n   float tmp3 = d3 + d4;\n   float tmp4 = d3 - d4;\n\n   // Even part\n   float tmp10 = tmp0 + tmp3;   // phase 2\n   float tmp13 = tmp0 - tmp3;\n   float tmp11 = tmp1 + tmp2;\n   float tmp12 = tmp1 - tmp2;\n\n   d0 = tmp10 + tmp11;       // phase 3\n   d4 = tmp10 - tmp11;\n\n   z1 = (tmp12 + tmp13) * 0.707106781f; // c4\n   d2 = tmp13 + z1;       // phase 5\n   d6 = tmp13 - z1;\n\n   // Odd part\n   tmp10 = tmp4 + tmp5;       // phase 2\n   tmp11 = tmp5 + tmp6;\n   tmp12 = tmp6 + tmp7;\n\n   // The rotator is modified from fig 4-8 to avoid extra negations.\n   z5 = (tmp10 - tmp12) * 0.382683433f; // c6\n   z2 = tmp10 * 0.541196100f + z5; // c2-c6\n   z4 = tmp12 * 1.306562965f + z5; // c2+c6\n   z3 = tmp11 * 0.707106781f; // c4\n\n   z11 = tmp7 + z3;      // phase 5\n   z13 = tmp7 - z3;\n\n   *d5p = z13 + z2;         // phase 6\n   *d3p = z13 - z2;\n   *d1p = z11 + z4;\n   *d7p = z11 - z4;\n\n   *d0p = d0;  *d2p = d2;  *d4p = d4;  *d6p = d6;\n}\n\nstatic void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {\n   int tmp1 = val < 0 ? -val : val;\n   val = val < 0 ? val-1 : val;\n   bits[1] = 1;\n   while(tmp1 >>= 1) {\n      ++bits[1];\n   }\n   bits[0] = val & ((1<<bits[1])-1);\n}\n\nstatic int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, int du_stride, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) {\n   const unsigned short EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] };\n   const unsigned short M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] };\n   int dataOff, i, j, n, diff, end0pos, x, y;\n   int DU[64];\n\n   // DCT rows\n   for(dataOff=0, n=du_stride*8; dataOff<n; dataOff+=du_stride) {\n      stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]);\n   }\n   // DCT columns\n   for(dataOff=0; dataOff<8; ++dataOff) {\n      stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+du_stride], &CDU[dataOff+du_stride*2], &CDU[dataOff+du_stride*3], &CDU[dataOff+du_stride*4],\n                     &CDU[dataOff+du_stride*5], &CDU[dataOff+du_stride*6], &CDU[dataOff+du_stride*7]);\n   }\n   // Quantize/descale/zigzag the coefficients\n   for(y = 0, j=0; y < 8; ++y) {\n      for(x = 0; x < 8; ++x,++j) {\n         float v;\n         i = y*du_stride+x;\n         v = CDU[i]*fdtbl[j];\n         // DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));\n         // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?\n         DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);\n      }\n   }\n\n   // Encode DC\n   diff = DU[0] - DC;\n   if (diff == 0) {\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]);\n   } else {\n      unsigned short bits[2];\n      stbiw__jpg_calcBits(diff, bits);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);\n   }\n   // Encode ACs\n   end0pos = 63;\n   for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) {\n   }\n   // end0pos = first element in reverse order !=0\n   if(end0pos == 0) {\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);\n      return DU[0];\n   }\n   for(i = 1; i <= end0pos; ++i) {\n      int startpos = i;\n      int nrzeroes;\n      unsigned short bits[2];\n      for (; DU[i]==0 && i<=end0pos; ++i) {\n      }\n      nrzeroes = i-startpos;\n      if ( nrzeroes >= 16 ) {\n         int lng = nrzeroes>>4;\n         int nrmarker;\n         for (nrmarker=1; nrmarker <= lng; ++nrmarker)\n            stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);\n         nrzeroes &= 15;\n      }\n      stbiw__jpg_calcBits(DU[i], bits);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);\n   }\n   if(end0pos != 63) {\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);\n   }\n   return DU[0];\n}\n\nstatic int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) {\n   // Constants that don't pollute global namespace\n   static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0};\n   static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};\n   static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d};\n   static const unsigned char std_ac_luminance_values[] = {\n      0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n      0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n      0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n      0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n      0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n      0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n      0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa\n   };\n   static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0};\n   static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};\n   static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77};\n   static const unsigned char std_ac_chrominance_values[] = {\n      0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n      0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n      0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n      0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n      0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n      0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n      0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa\n   };\n   // Huffman tables\n   static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}};\n   static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}};\n   static const unsigned short YAC_HT[256][2] = {\n      {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}\n   };\n   static const unsigned short UVAC_HT[256][2] = {\n      {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}\n   };\n   static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,\n                             37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99};\n   static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,\n                              99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99};\n   static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,\n                                 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };\n\n   int row, col, i, k, subsample;\n   float fdtbl_Y[64], fdtbl_UV[64];\n   unsigned char YTable[64], UVTable[64];\n\n   if(!data || !width || !height || comp > 4 || comp < 1) {\n      return 0;\n   }\n\n   quality = quality ? quality : 90;\n   subsample = quality <= 90 ? 1 : 0;\n   quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;\n   quality = quality < 50 ? 5000 / quality : 200 - quality * 2;\n\n   for(i = 0; i < 64; ++i) {\n      int uvti, yti = (YQT[i]*quality+50)/100;\n      YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti);\n      uvti = (UVQT[i]*quality+50)/100;\n      UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);\n   }\n\n   for(row = 0, k = 0; row < 8; ++row) {\n      for(col = 0; col < 8; ++col, ++k) {\n         fdtbl_Y[k]  = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);\n         fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);\n      }\n   }\n\n   // Write Headers\n   {\n      static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 };\n      static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };\n      const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width),\n                                      3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };\n      s->func(s->context, (void*)head0, sizeof(head0));\n      s->func(s->context, (void*)YTable, sizeof(YTable));\n      stbiw__putc(s, 1);\n      s->func(s->context, UVTable, sizeof(UVTable));\n      s->func(s->context, (void*)head1, sizeof(head1));\n      s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1);\n      s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));\n      stbiw__putc(s, 0x10); // HTYACinfo\n      s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1);\n      s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));\n      stbiw__putc(s, 1); // HTUDCinfo\n      s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1);\n      s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));\n      stbiw__putc(s, 0x11); // HTUACinfo\n      s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1);\n      s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));\n      s->func(s->context, (void*)head2, sizeof(head2));\n   }\n\n   // Encode 8x8 macroblocks\n   {\n      static const unsigned short fillBits[] = {0x7F, 7};\n      int DCY=0, DCU=0, DCV=0;\n      int bitBuf=0, bitCnt=0;\n      // comp == 2 is grey+alpha (alpha is ignored)\n      int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;\n      const unsigned char *dataR = (const unsigned char *)data;\n      const unsigned char *dataG = dataR + ofsG;\n      const unsigned char *dataB = dataR + ofsB;\n      int x, y, pos;\n      if(subsample) {\n         for(y = 0; y < height; y += 16) {\n            for(x = 0; x < width; x += 16) {\n               float Y[256], U[256], V[256];\n               for(row = y, pos = 0; row < y+16; ++row) {\n                  // row >= height => use last input row\n                  int clamped_row = (row < height) ? row : height - 1;\n                  int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;\n                  for(col = x; col < x+16; ++col, ++pos) {\n                     // if col >= width => use pixel from last input column\n                     int p = base_p + ((col < width) ? col : (width-1))*comp;\n                     float r = dataR[p], g = dataG[p], b = dataB[p];\n                     Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;\n                     U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;\n                     V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;\n                  }\n               }\n               DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0,   16, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n               DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8,   16, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n               DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n               DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n\n               // subsample U,V\n               {\n                  float subU[64], subV[64];\n                  int yy, xx;\n                  for(yy = 0, pos = 0; yy < 8; ++yy) {\n                     for(xx = 0; xx < 8; ++xx, ++pos) {\n                        int j = yy*32+xx*2;\n                        subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f;\n                        subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f;\n                     }\n                  }\n                  DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n                  DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n               }\n            }\n         }\n      } else {\n         for(y = 0; y < height; y += 8) {\n            for(x = 0; x < width; x += 8) {\n               float Y[64], U[64], V[64];\n               for(row = y, pos = 0; row < y+8; ++row) {\n                  // row >= height => use last input row\n                  int clamped_row = (row < height) ? row : height - 1;\n                  int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;\n                  for(col = x; col < x+8; ++col, ++pos) {\n                     // if col >= width => use pixel from last input column\n                     int p = base_p + ((col < width) ? col : (width-1))*comp;\n                     float r = dataR[p], g = dataG[p], b = dataB[p];\n                     Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;\n                     U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;\n                     V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;\n                  }\n               }\n\n               DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y,  DCY, YDC_HT, YAC_HT);\n               DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n               DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n            }\n         }\n      }\n\n      // Do the bit alignment of the EOI marker\n      stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);\n   }\n\n   // EOI\n   stbiw__putc(s, 0xFF);\n   stbiw__putc(s, 0xD9);\n\n   return 1;\n}\n\nSTBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality)\n{\n   stbi__write_context s = { 0 };\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality);\n}\n\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)\n{\n   stbi__write_context s = { 0 };\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif\n\n#endif // STB_IMAGE_WRITE_IMPLEMENTATION\n\n/* Revision history\n      1.16  (2021-07-11)\n             make Deflate code emit uncompressed blocks when it would otherwise expand\n             support writing BMPs with alpha channel\n      1.15  (2020-07-13) unknown\n      1.14  (2020-02-02) updated JPEG writer to downsample chroma channels\n      1.13\n      1.12\n      1.11  (2019-08-11)\n\n      1.10  (2019-02-07)\n             support utf8 filenames in Windows; fix warnings and platform ifdefs\n      1.09  (2018-02-11)\n             fix typo in zlib quality API, improve STB_I_W_STATIC in C++\n      1.08  (2018-01-29)\n             add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter\n      1.07  (2017-07-24)\n             doc fix\n      1.06 (2017-07-23)\n             writing JPEG (using Jon Olick's code)\n      1.05   ???\n      1.04 (2017-03-03)\n             monochrome BMP expansion\n      1.03   ???\n      1.02 (2016-04-02)\n             avoid allocating large structures on the stack\n      1.01 (2016-01-16)\n             STBIW_REALLOC_SIZED: support allocators with no realloc support\n             avoid race-condition in crc initialization\n             minor compile issues\n      1.00 (2015-09-14)\n             installable file IO function\n      0.99 (2015-09-13)\n             warning fixes; TGA rle support\n      0.98 (2015-04-08)\n             added STBIW_MALLOC, STBIW_ASSERT etc\n      0.97 (2015-01-18)\n             fixed HDR asserts, rewrote HDR rle logic\n      0.96 (2015-01-17)\n             add HDR output\n             fix monochrome BMP\n      0.95 (2014-08-17)\n             add monochrome TGA output\n      0.94 (2014-05-31)\n             rename private functions to avoid conflicts with stb_image.h\n      0.93 (2014-05-27)\n             warning fixes\n      0.92 (2010-08-01)\n             casts to unsigned char to fix warnings\n      0.91 (2010-07-17)\n             first public release\n      0.90   first internal release\n*/\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "samples/stb_truetype.h",
    "content": "// stb_truetype.h - v1.26 - public domain\n// authored from 2009-2021 by Sean Barrett / RAD Game Tools\n//\n// =======================================================================\n//\n//    NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES\n//\n// This library does no range checking of the offsets found in the file,\n// meaning an attacker can use it to read arbitrary memory.\n//\n// =======================================================================\n//\n//   This library processes TrueType files:\n//        parse files\n//        extract glyph metrics\n//        extract glyph shapes\n//        render glyphs to one-channel bitmaps with antialiasing (box filter)\n//        render glyphs to one-channel SDF bitmaps (signed-distance field/function)\n//\n//   Todo:\n//        non-MS cmaps\n//        crashproof on bad data\n//        hinting? (no longer patented)\n//        cleartype-style AA?\n//        optimize: use simple memory allocator for intermediates\n//        optimize: build edge-list directly from curves\n//        optimize: rasterize directly from curves?\n//\n// ADDITIONAL CONTRIBUTORS\n//\n//   Mikko Mononen: compound shape support, more cmap formats\n//   Tor Andersson: kerning, subpixel rendering\n//   Dougall Johnson: OpenType / Type 2 font handling\n//   Daniel Ribeiro Maciel: basic GPOS-based kerning\n//\n//   Misc other:\n//       Ryan Gordon\n//       Simon Glass\n//       github:IntellectualKitty\n//       Imanol Celaya\n//       Daniel Ribeiro Maciel\n//\n//   Bug/warning reports/fixes:\n//       \"Zer\" on mollyrocket       Fabian \"ryg\" Giesen   github:NiLuJe\n//       Cass Everitt               Martins Mozeiko       github:aloucks\n//       stoiko (Haemimont Games)   Cap Petschulat        github:oyvindjam\n//       Brian Hook                 Omar Cornut           github:vassvik\n//       Walter van Niftrik         Ryan Griege\n//       David Gow                  Peter LaValle\n//       David Given                Sergey Popov\n//       Ivan-Assen Ivanov          Giumo X. Clanjor\n//       Anthony Pesch              Higor Euripedes\n//       Johan Duparc               Thomas Fields\n//       Hou Qiming                 Derek Vinyard\n//       Rob Loach                  Cort Stratton\n//       Kenney Phillis Jr.         Brian Costabile\n//       Ken Voskuil (kaesve)       Yakov Galka\n//\n// VERSION HISTORY\n//\n//   1.26 (2021-08-28) fix broken rasterizer\n//   1.25 (2021-07-11) many fixes\n//   1.24 (2020-02-05) fix warning\n//   1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)\n//   1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined\n//   1.21 (2019-02-25) fix warning\n//   1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()\n//   1.19 (2018-02-11) GPOS kerning, STBTT_fmod\n//   1.18 (2018-01-29) add missing function\n//   1.17 (2017-07-23) make more arguments const; doc fix\n//   1.16 (2017-07-12) SDF support\n//   1.15 (2017-03-03) make more arguments const\n//   1.14 (2017-01-16) num-fonts-in-TTC function\n//   1.13 (2017-01-02) support OpenType fonts, certain Apple fonts\n//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual\n//   1.11 (2016-04-02) fix unused-variable warning\n//   1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef\n//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly\n//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges\n//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;\n//                     variant PackFontRanges to pack and render in separate phases;\n//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);\n//                     fixed an assert() bug in the new rasterizer\n//                     replace assert() with STBTT_assert() in new rasterizer\n//\n//   Full history can be found at the end of this file.\n//\n// LICENSE\n//\n//   See end of file for license information.\n//\n// USAGE\n//\n//   Include this file in whatever places need to refer to it. In ONE C/C++\n//   file, write:\n//      #define STB_TRUETYPE_IMPLEMENTATION\n//   before the #include of this file. This expands out the actual\n//   implementation into that C/C++ file.\n//\n//   To make the implementation private to the file that generates the implementation,\n//      #define STBTT_STATIC\n//\n//   Simple 3D API (don't ship this, but it's fine for tools and quick start)\n//           stbtt_BakeFontBitmap()               -- bake a font to a bitmap for use as texture\n//           stbtt_GetBakedQuad()                 -- compute quad to draw for a given char\n//\n//   Improved 3D API (more shippable):\n//           #include \"stb_rect_pack.h\"           -- optional, but you really want it\n//           stbtt_PackBegin()\n//           stbtt_PackSetOversampling()          -- for improved quality on small fonts\n//           stbtt_PackFontRanges()               -- pack and renders\n//           stbtt_PackEnd()\n//           stbtt_GetPackedQuad()\n//\n//   \"Load\" a font file from a memory buffer (you have to keep the buffer loaded)\n//           stbtt_InitFont()\n//           stbtt_GetFontOffsetForIndex()        -- indexing for TTC font collections\n//           stbtt_GetNumberOfFonts()             -- number of fonts for TTC font collections\n//\n//   Render a unicode codepoint to a bitmap\n//           stbtt_GetCodepointBitmap()           -- allocates and returns a bitmap\n//           stbtt_MakeCodepointBitmap()          -- renders into bitmap you provide\n//           stbtt_GetCodepointBitmapBox()        -- how big the bitmap must be\n//\n//   Character advance/positioning\n//           stbtt_GetCodepointHMetrics()\n//           stbtt_GetFontVMetrics()\n//           stbtt_GetFontVMetricsOS2()\n//           stbtt_GetCodepointKernAdvance()\n//\n//   Starting with version 1.06, the rasterizer was replaced with a new,\n//   faster and generally-more-precise rasterizer. The new rasterizer more\n//   accurately measures pixel coverage for anti-aliasing, except in the case\n//   where multiple shapes overlap, in which case it overestimates the AA pixel\n//   coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If\n//   this turns out to be a problem, you can re-enable the old rasterizer with\n//        #define STBTT_RASTERIZER_VERSION 1\n//   which will incur about a 15% speed hit.\n//\n// ADDITIONAL DOCUMENTATION\n//\n//   Immediately after this block comment are a series of sample programs.\n//\n//   After the sample programs is the \"header file\" section. This section\n//   includes documentation for each API function.\n//\n//   Some important concepts to understand to use this library:\n//\n//      Codepoint\n//         Characters are defined by unicode codepoints, e.g. 65 is\n//         uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is\n//         the hiragana for \"ma\".\n//\n//      Glyph\n//         A visual character shape (every codepoint is rendered as\n//         some glyph)\n//\n//      Glyph index\n//         A font-specific integer ID representing a glyph\n//\n//      Baseline\n//         Glyph shapes are defined relative to a baseline, which is the\n//         bottom of uppercase characters. Characters extend both above\n//         and below the baseline.\n//\n//      Current Point\n//         As you draw text to the screen, you keep track of a \"current point\"\n//         which is the origin of each character. The current point's vertical\n//         position is the baseline. Even \"baked fonts\" use this model.\n//\n//      Vertical Font Metrics\n//         The vertical qualities of the font, used to vertically position\n//         and space the characters. See docs for stbtt_GetFontVMetrics.\n//\n//      Font Size in Pixels or Points\n//         The preferred interface for specifying font sizes in stb_truetype\n//         is to specify how tall the font's vertical extent should be in pixels.\n//         If that sounds good enough, skip the next paragraph.\n//\n//         Most font APIs instead use \"points\", which are a common typographic\n//         measurement for describing font size, defined as 72 points per inch.\n//         stb_truetype provides a point API for compatibility. However, true\n//         \"per inch\" conventions don't make much sense on computer displays\n//         since different monitors have different number of pixels per\n//         inch. For example, Windows traditionally uses a convention that\n//         there are 96 pixels per inch, thus making 'inch' measurements have\n//         nothing to do with inches, and thus effectively defining a point to\n//         be 1.333 pixels. Additionally, the TrueType font data provides\n//         an explicit scale factor to scale a given font's glyphs to points,\n//         but the author has observed that this scale factor is often wrong\n//         for non-commercial fonts, thus making fonts scaled in points\n//         according to the TrueType spec incoherently sized in practice.\n//\n// DETAILED USAGE:\n//\n//  Scale:\n//    Select how high you want the font to be, in points or pixels.\n//    Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute\n//    a scale factor SF that will be used by all other functions.\n//\n//  Baseline:\n//    You need to select a y-coordinate that is the baseline of where\n//    your text will appear. Call GetFontBoundingBox to get the baseline-relative\n//    bounding box for all characters. SF*-y0 will be the distance in pixels\n//    that the worst-case character could extend above the baseline, so if\n//    you want the top edge of characters to appear at the top of the\n//    screen where y=0, then you would set the baseline to SF*-y0.\n//\n//  Current point:\n//    Set the current point where the first character will appear. The\n//    first character could extend left of the current point; this is font\n//    dependent. You can either choose a current point that is the leftmost\n//    point and hope, or add some padding, or check the bounding box or\n//    left-side-bearing of the first character to be displayed and set\n//    the current point based on that.\n//\n//  Displaying a character:\n//    Compute the bounding box of the character. It will contain signed values\n//    relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,\n//    then the character should be displayed in the rectangle from\n//    <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).\n//\n//  Advancing for the next character:\n//    Call GlyphHMetrics, and compute 'current_point += SF * advance'.\n//\n//\n// ADVANCED USAGE\n//\n//   Quality:\n//\n//    - Use the functions with Subpixel at the end to allow your characters\n//      to have subpixel positioning. Since the font is anti-aliased, not\n//      hinted, this is very import for quality. (This is not possible with\n//      baked fonts.)\n//\n//    - Kerning is now supported, and if you're supporting subpixel rendering\n//      then kerning is worth using to give your text a polished look.\n//\n//   Performance:\n//\n//    - Convert Unicode codepoints to glyph indexes and operate on the glyphs;\n//      if you don't do this, stb_truetype is forced to do the conversion on\n//      every call.\n//\n//    - There are a lot of memory allocations. We should modify it to take\n//      a temp buffer and allocate from the temp buffer (without freeing),\n//      should help performance a lot.\n//\n// NOTES\n//\n//   The system uses the raw data found in the .ttf file without changing it\n//   and without building auxiliary data structures. This is a bit inefficient\n//   on little-endian systems (the data is big-endian), but assuming you're\n//   caching the bitmaps or glyph shapes this shouldn't be a big deal.\n//\n//   It appears to be very hard to programmatically determine what font a\n//   given file is in a general way. I provide an API for this, but I don't\n//   recommend it.\n//\n//\n// PERFORMANCE MEASUREMENTS FOR 1.06:\n//\n//                      32-bit     64-bit\n//   Previous release:  8.83 s     7.68 s\n//   Pool allocations:  7.72 s     6.34 s\n//   Inline sort     :  6.54 s     5.65 s\n//   New rasterizer  :  5.63 s     5.00 s\n\n//////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////\n////\n////  SAMPLE PROGRAMS\n////\n//\n//  Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.\n//  See \"tests/truetype_demo_win32.c\" for a complete version.\n#if 0\n#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation\n#include \"stb_truetype.h\"\n\nunsigned char ttf_buffer[1<<20];\nunsigned char temp_bitmap[512*512];\n\nstbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs\nGLuint ftex;\n\nvoid my_stbtt_initfont(void)\n{\n   fread(ttf_buffer, 1, 1<<20, fopen(\"c:/windows/fonts/times.ttf\", \"rb\"));\n   stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!\n   // can free ttf_buffer at this point\n   glGenTextures(1, &ftex);\n   glBindTexture(GL_TEXTURE_2D, ftex);\n   glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);\n   // can free temp_bitmap at this point\n   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n}\n\nvoid my_stbtt_print(float x, float y, char *text)\n{\n   // assume orthographic projection with units = screen pixels, origin at top left\n   glEnable(GL_BLEND);\n   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n   glEnable(GL_TEXTURE_2D);\n   glBindTexture(GL_TEXTURE_2D, ftex);\n   glBegin(GL_QUADS);\n   while (*text) {\n      if (*text >= 32 && *text < 128) {\n         stbtt_aligned_quad q;\n         stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9\n         glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);\n         glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);\n         glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);\n         glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);\n      }\n      ++text;\n   }\n   glEnd();\n}\n#endif\n//\n//\n//////////////////////////////////////////////////////////////////////////////\n//\n// Complete program (this compiles): get a single bitmap, print as ASCII art\n//\n#if 0\n#include <stdio.h>\n#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation\n#include \"stb_truetype.h\"\n\nchar ttf_buffer[1<<25];\n\nint main(int argc, char **argv)\n{\n   stbtt_fontinfo font;\n   unsigned char *bitmap;\n   int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);\n\n   fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : \"c:/windows/fonts/arialbd.ttf\", \"rb\"));\n\n   stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));\n   bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);\n\n   for (j=0; j < h; ++j) {\n      for (i=0; i < w; ++i)\n         putchar(\" .:ioVM@\"[bitmap[j*w+i]>>5]);\n      putchar('\\n');\n   }\n   return 0;\n}\n#endif\n//\n// Output:\n//\n//     .ii.\n//    @@@@@@.\n//   V@Mio@@o\n//   :i.  V@V\n//     :oM@@M\n//   :@@@MM@M\n//   @@o  o@M\n//  :@@.  M@M\n//   @@@o@@@@\n//   :M@@V:@@.\n//\n//////////////////////////////////////////////////////////////////////////////\n//\n// Complete program: print \"Hello World!\" banner, with bugs\n//\n#if 0\nchar buffer[24<<20];\nunsigned char screen[20][79];\n\nint main(int arg, char **argv)\n{\n   stbtt_fontinfo font;\n   int i,j,ascent,baseline,ch=0;\n   float scale, xpos=2; // leave a little padding in case the character extends left\n   char *text = \"Heljo World!\"; // intentionally misspelled to show 'lj' brokenness\n\n   fread(buffer, 1, 1000000, fopen(\"c:/windows/fonts/arialbd.ttf\", \"rb\"));\n   stbtt_InitFont(&font, buffer, 0);\n\n   scale = stbtt_ScaleForPixelHeight(&font, 15);\n   stbtt_GetFontVMetrics(&font, &ascent,0,0);\n   baseline = (int) (ascent*scale);\n\n   while (text[ch]) {\n      int advance,lsb,x0,y0,x1,y1;\n      float x_shift = xpos - (float) floor(xpos);\n      stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);\n      stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);\n      stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);\n      // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong\n      // because this API is really for baking character bitmaps into textures. if you want to render\n      // a sequence of characters, you really need to render each bitmap to a temp buffer, then\n      // \"alpha blend\" that into the working buffer\n      xpos += (advance * scale);\n      if (text[ch+1])\n         xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);\n      ++ch;\n   }\n\n   for (j=0; j < 20; ++j) {\n      for (i=0; i < 78; ++i)\n         putchar(\" .:ioVM@\"[screen[j][i]>>5]);\n      putchar('\\n');\n   }\n\n   return 0;\n}\n#endif\n\n\n//////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////\n////\n////   INTEGRATION WITH YOUR CODEBASE\n////\n////   The following sections allow you to supply alternate definitions\n////   of C library functions used by stb_truetype, e.g. if you don't\n////   link with the C runtime library.\n\n#ifdef STB_TRUETYPE_IMPLEMENTATION\n   // #define your own (u)stbtt_int8/16/32 before including to override this\n   #ifndef stbtt_uint8\n   typedef unsigned char   stbtt_uint8;\n   typedef signed   char   stbtt_int8;\n   typedef unsigned short  stbtt_uint16;\n   typedef signed   short  stbtt_int16;\n   typedef unsigned int    stbtt_uint32;\n   typedef signed   int    stbtt_int32;\n   #endif\n\n   typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];\n   typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];\n\n   // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h\n   #ifndef STBTT_ifloor\n   #include <math.h>\n   #define STBTT_ifloor(x)   ((int) floor(x))\n   #define STBTT_iceil(x)    ((int) ceil(x))\n   #endif\n\n   #ifndef STBTT_sqrt\n   #include <math.h>\n   #define STBTT_sqrt(x)      sqrt(x)\n   #define STBTT_pow(x,y)     pow(x,y)\n   #endif\n\n   #ifndef STBTT_fmod\n   #include <math.h>\n   #define STBTT_fmod(x,y)    fmod(x,y)\n   #endif\n\n   #ifndef STBTT_cos\n   #include <math.h>\n   #define STBTT_cos(x)       cos(x)\n   #define STBTT_acos(x)      acos(x)\n   #endif\n\n   #ifndef STBTT_fabs\n   #include <math.h>\n   #define STBTT_fabs(x)      fabs(x)\n   #endif\n\n   // #define your own functions \"STBTT_malloc\" / \"STBTT_free\" to avoid malloc.h\n   #ifndef STBTT_malloc\n   #include <stdlib.h>\n   #define STBTT_malloc(x,u)  ((void)(u),malloc(x))\n   #define STBTT_free(x,u)    ((void)(u),free(x))\n   #endif\n\n   #ifndef STBTT_assert\n   #include <assert.h>\n   #define STBTT_assert(x)    assert(x)\n   #endif\n\n   #ifndef STBTT_strlen\n   #include <string.h>\n   #define STBTT_strlen(x)    strlen(x)\n   #endif\n\n   #ifndef STBTT_memcpy\n   #include <string.h>\n   #define STBTT_memcpy       memcpy\n   #define STBTT_memset       memset\n   #endif\n#endif\n\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n////\n////   INTERFACE\n////\n////\n\n#ifndef __STB_INCLUDE_STB_TRUETYPE_H__\n#define __STB_INCLUDE_STB_TRUETYPE_H__\n\n#ifdef STBTT_STATIC\n#define STBTT_DEF static\n#else\n#define STBTT_DEF extern\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// private structure\ntypedef struct\n{\n   unsigned char *data;\n   int cursor;\n   int size;\n} stbtt__buf;\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// TEXTURE BAKING API\n//\n// If you use this API, you only have to call two functions ever.\n//\n\ntypedef struct\n{\n   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap\n   float xoff,yoff,xadvance;\n} stbtt_bakedchar;\n\nSTBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)\n                                float pixel_height,                     // height of font in pixels\n                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in\n                                int first_char, int num_chars,          // characters to bake\n                                stbtt_bakedchar *chardata);             // you allocate this, it's num_chars long\n// if return is positive, the first unused row of the bitmap\n// if return is negative, returns the negative of the number of characters that fit\n// if return is 0, no characters fit and no rows were used\n// This uses a very crappy packing.\n\ntypedef struct\n{\n   float x0,y0,s0,t0; // top-left\n   float x1,y1,s1,t1; // bottom-right\n} stbtt_aligned_quad;\n\nSTBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph,  // same data as above\n                               int char_index,             // character to display\n                               float *xpos, float *ypos,   // pointers to current position in screen pixel space\n                               stbtt_aligned_quad *q,      // output: quad to draw\n                               int opengl_fillrule);       // true if opengl fill rule; false if DX9 or earlier\n// Call GetBakedQuad with char_index = 'character - first_char', and it\n// creates the quad you need to draw and advances the current position.\n//\n// The coordinate system used assumes y increases downwards.\n//\n// Characters will extend both above and below the current position;\n// see discussion of \"BASELINE\" above.\n//\n// It's inefficient; you might want to c&p it and optimize it.\n\nSTBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap);\n// Query the font vertical metrics without having to create a font first.\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// NEW TEXTURE BAKING API\n//\n// This provides options for packing multiple fonts into one atlas, not\n// perfectly but better than nothing.\n\ntypedef struct\n{\n   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap\n   float xoff,yoff,xadvance;\n   float xoff2,yoff2;\n} stbtt_packedchar;\n\ntypedef struct stbtt_pack_context stbtt_pack_context;\ntypedef struct stbtt_fontinfo stbtt_fontinfo;\n#ifndef STB_RECT_PACK_VERSION\ntypedef struct stbrp_rect stbrp_rect;\n#endif\n\nSTBTT_DEF int  stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);\n// Initializes a packing context stored in the passed-in stbtt_pack_context.\n// Future calls using this context will pack characters into the bitmap passed\n// in here: a 1-channel bitmap that is width * height. stride_in_bytes is\n// the distance from one row to the next (or 0 to mean they are packed tightly\n// together). \"padding\" is the amount of padding to leave between each\n// character (normally you want '1' for bitmaps you'll use as textures with\n// bilinear filtering).\n//\n// Returns 0 on failure, 1 on success.\n\nSTBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc);\n// Cleans up the packing context and frees all memory.\n\n#define STBTT_POINT_SIZE(x)   (-(x))\n\nSTBTT_DEF int  stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,\n                                int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);\n// Creates character bitmaps from the font_index'th font found in fontdata (use\n// font_index=0 if you don't know what that is). It creates num_chars_in_range\n// bitmaps for characters with unicode values starting at first_unicode_char_in_range\n// and increasing. Data for how to render them is stored in chardata_for_range;\n// pass these to stbtt_GetPackedQuad to get back renderable quads.\n//\n// font_size is the full height of the character from ascender to descender,\n// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed\n// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()\n// and pass that result as 'font_size':\n//       ...,                  20 , ... // font max minus min y is 20 pixels tall\n//       ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall\n\ntypedef struct\n{\n   float font_size;\n   int first_unicode_codepoint_in_range;  // if non-zero, then the chars are continuous, and this is the first codepoint\n   int *array_of_unicode_codepoints;       // if non-zero, then this is an array of unicode codepoints\n   int num_chars;\n   stbtt_packedchar *chardata_for_range; // output\n   unsigned char h_oversample, v_oversample; // don't set these, they're used internally\n} stbtt_pack_range;\n\nSTBTT_DEF int  stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);\n// Creates character bitmaps from multiple ranges of characters stored in\n// ranges. This will usually create a better-packed bitmap than multiple\n// calls to stbtt_PackFontRange. Note that you can call this multiple\n// times within a single PackBegin/PackEnd.\n\nSTBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);\n// Oversampling a font increases the quality by allowing higher-quality subpixel\n// positioning, and is especially valuable at smaller text sizes.\n//\n// This function sets the amount of oversampling for all following calls to\n// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given\n// pack context. The default (no oversampling) is achieved by h_oversample=1\n// and v_oversample=1. The total number of pixels required is\n// h_oversample*v_oversample larger than the default; for example, 2x2\n// oversampling requires 4x the storage of 1x1. For best results, render\n// oversampled textures with bilinear filtering. Look at the readme in\n// stb/tests/oversample for information about oversampled fonts\n//\n// To use with PackFontRangesGather etc., you must set it before calls\n// call to PackFontRangesGatherRects.\n\nSTBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);\n// If skip != 0, this tells stb_truetype to skip any codepoints for which\n// there is no corresponding glyph. If skip=0, which is the default, then\n// codepoints without a glyph recived the font's \"missing character\" glyph,\n// typically an empty box by convention.\n\nSTBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph,  // same data as above\n                               int char_index,             // character to display\n                               float *xpos, float *ypos,   // pointers to current position in screen pixel space\n                               stbtt_aligned_quad *q,      // output: quad to draw\n                               int align_to_integer);\n\nSTBTT_DEF int  stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);\nSTBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);\nSTBTT_DEF int  stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);\n// Calling these functions in sequence is roughly equivalent to calling\n// stbtt_PackFontRanges(). If you more control over the packing of multiple\n// fonts, or if you want to pack custom data into a font texture, take a look\n// at the source to of stbtt_PackFontRanges() and create a custom version\n// using these functions, e.g. call GatherRects multiple times,\n// building up a single array of rects, then call PackRects once,\n// then call RenderIntoRects repeatedly. This may result in a\n// better packing than calling PackFontRanges multiple times\n// (or it may not).\n\n// this is an opaque structure that you shouldn't mess with which holds\n// all the context needed from PackBegin to PackEnd.\nstruct stbtt_pack_context {\n   void *user_allocator_context;\n   void *pack_info;\n   int   width;\n   int   height;\n   int   stride_in_bytes;\n   int   padding;\n   int   skip_missing;\n   unsigned int   h_oversample, v_oversample;\n   unsigned char *pixels;\n   void  *nodes;\n};\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// FONT LOADING\n//\n//\n\nSTBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data);\n// This function will determine the number of fonts in a font file.  TrueType\n// collection (.ttc) files may contain multiple fonts, while TrueType font\n// (.ttf) files only contain one font. The number of fonts can be used for\n// indexing with the previous function where the index is between zero and one\n// less than the total fonts. If an error occurs, -1 is returned.\n\nSTBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);\n// Each .ttf/.ttc file may have more than one font. Each font has a sequential\n// index number starting from 0. Call this function to get the font offset for\n// a given index; it returns -1 if the index is out of range. A regular .ttf\n// file will only define one font and it always be at offset 0, so it will\n// return '0' for index 0, and -1 for all other indices.\n\n// The following structure is defined publicly so you can declare one on\n// the stack or as a global or etc, but you should treat it as opaque.\nstruct stbtt_fontinfo\n{\n   void           * userdata;\n   unsigned char  * data;              // pointer to .ttf file\n   int              fontstart;         // offset of start of font\n\n   int numGlyphs;                     // number of glyphs, needed for range checking\n\n   int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf\n   int index_map;                     // a cmap mapping for our chosen character encoding\n   int indexToLocFormat;              // format needed to map from glyph index to glyph\n\n   stbtt__buf cff;                    // cff font data\n   stbtt__buf charstrings;            // the charstring index\n   stbtt__buf gsubrs;                 // global charstring subroutines index\n   stbtt__buf subrs;                  // private charstring subroutines index\n   stbtt__buf fontdicts;              // array of font dicts\n   stbtt__buf fdselect;               // map from glyph to fontdict\n};\n\nSTBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);\n// Given an offset into the file that defines a font, this function builds\n// the necessary cached info for the rest of the system. You must allocate\n// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't\n// need to do anything special to free it, because the contents are pure\n// value data with no additional data structures. Returns 0 on failure.\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// CHARACTER TO GLYPH-INDEX CONVERSIOn\n\nSTBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);\n// If you're going to perform multiple operations on the same character\n// and you want a speed-up, call this function with the character you're\n// going to process, then use glyph-based functions instead of the\n// codepoint-based functions.\n// Returns 0 if the character codepoint is not defined in the font.\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// CHARACTER PROPERTIES\n//\n\nSTBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);\n// computes a scale factor to produce a font whose \"height\" is 'pixels' tall.\n// Height is measured as the distance from the highest ascender to the lowest\n// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics\n// and computing:\n//       scale = pixels / (ascent - descent)\n// so if you prefer to measure height by the ascent only, use a similar calculation.\n\nSTBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);\n// computes a scale factor to produce a font whose EM size is mapped to\n// 'pixels' tall. This is probably what traditional APIs compute, but\n// I'm not positive.\n\nSTBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);\n// ascent is the coordinate above the baseline the font extends; descent\n// is the coordinate below the baseline the font extends (i.e. it is typically negative)\n// lineGap is the spacing between one row's descent and the next row's ascent...\n// so you should advance the vertical position by \"*ascent - *descent + *lineGap\"\n//   these are expressed in unscaled coordinates, so you must multiply by\n//   the scale factor for a given size\n\nSTBTT_DEF int  stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap);\n// analogous to GetFontVMetrics, but returns the \"typographic\" values from the OS/2\n// table (specific to MS/Windows TTF files).\n//\n// Returns 1 on success (table present), 0 on failure.\n\nSTBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);\n// the bounding box around all possible characters\n\nSTBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);\n// leftSideBearing is the offset from the current horizontal position to the left edge of the character\n// advanceWidth is the offset from the current horizontal position to the next horizontal position\n//   these are expressed in unscaled coordinates\n\nSTBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);\n// an additional amount to add to the 'advance' value between ch1 and ch2\n\nSTBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);\n// Gets the bounding box of the visible part of the glyph, in unscaled coordinates\n\nSTBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);\nSTBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);\nSTBTT_DEF int  stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);\n// as above, but takes one or more glyph indices for greater efficiency\n\ntypedef struct stbtt_kerningentry\n{\n   int glyph1; // use stbtt_FindGlyphIndex\n   int glyph2;\n   int advance;\n} stbtt_kerningentry;\n\nSTBTT_DEF int  stbtt_GetKerningTableLength(const stbtt_fontinfo *info);\nSTBTT_DEF int  stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length);\n// Retrieves a complete list of all of the kerning pairs provided by the font\n// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write.\n// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1)\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// GLYPH SHAPES (you probably don't need these, but they have to go before\n// the bitmaps for C declaration-order reasons)\n//\n\n#ifndef STBTT_vmove // you can predefine these to use different values (but why?)\n   enum {\n      STBTT_vmove=1,\n      STBTT_vline,\n      STBTT_vcurve,\n      STBTT_vcubic\n   };\n#endif\n\n#ifndef stbtt_vertex // you can predefine this to use different values\n                   // (we share this with other code at RAD)\n   #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file\n   typedef struct\n   {\n      stbtt_vertex_type x,y,cx,cy,cx1,cy1;\n      unsigned char type,padding;\n   } stbtt_vertex;\n#endif\n\nSTBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);\n// returns non-zero if nothing is drawn for this glyph\n\nSTBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);\nSTBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);\n// returns # of vertices and fills *vertices with the pointer to them\n//   these are expressed in \"unscaled\" coordinates\n//\n// The shape is a series of contours. Each one starts with\n// a STBTT_moveto, then consists of a series of mixed\n// STBTT_lineto and STBTT_curveto segments. A lineto\n// draws a line from previous endpoint to its x,y; a curveto\n// draws a quadratic bezier from previous endpoint to\n// its x,y, using cx,cy as the bezier control point.\n\nSTBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);\n// frees the data allocated above\n\nSTBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl);\nSTBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg);\nSTBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);\n// fills svg with the character's SVG data.\n// returns data size or 0 if SVG not found.\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// BITMAP RENDERING\n//\n\nSTBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);\n// frees the bitmap allocated below\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);\n// allocates a large-enough single-channel 8bpp bitmap and renders the\n// specified character/glyph at the specified scale into it, with\n// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).\n// *width & *height are filled out with the width & height of the bitmap,\n// which is stored left-to-right, top-to-bottom.\n//\n// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);\n// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel\n// shift for the character\n\nSTBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);\n// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap\n// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap\n// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the\n// width and height and positioning info for it first.\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);\n// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel\n// shift for the character\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint);\n// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering\n// is performed (see stbtt_PackSetOversampling)\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);\n// get the bbox of the bitmap centered around the glyph origin; so the\n// bitmap width is ix1-ix0, height is iy1-iy0, and location to place\n// the bitmap top left is (leftSideBearing*scale,iy0).\n// (Note that the bitmap uses y-increases-down, but the shape uses\n// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);\n// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel\n// shift for the character\n\n// the following functions are equivalent to the above functions, but operate\n// on glyph indices instead of Unicode codepoints (for efficiency)\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);\nSTBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph);\nSTBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);\nSTBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);\n\n\n// @TODO: don't expose this structure\ntypedef struct\n{\n   int w,h,stride;\n   unsigned char *pixels;\n} stbtt__bitmap;\n\n// rasterize a shape with quadratic beziers into a bitmap\nSTBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result,        // 1-channel bitmap to draw into\n                               float flatness_in_pixels,     // allowable error of curve in pixels\n                               stbtt_vertex *vertices,       // array of vertices defining shape\n                               int num_verts,                // number of vertices in above array\n                               float scale_x, float scale_y, // scale applied to input vertices\n                               float shift_x, float shift_y, // translation applied to input vertices\n                               int x_off, int y_off,         // another translation applied to input\n                               int invert,                   // if non-zero, vertically flip shape\n                               void *userdata);              // context for to STBTT_MALLOC\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Signed Distance Function (or Field) rendering\n\nSTBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata);\n// frees the SDF bitmap allocated below\n\nSTBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);\nSTBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);\n// These functions compute a discretized SDF field for a single character, suitable for storing\n// in a single-channel texture, sampling with bilinear filtering, and testing against\n// larger than some threshold to produce scalable fonts.\n//        info              --  the font\n//        scale             --  controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap\n//        glyph/codepoint   --  the character to generate the SDF for\n//        padding           --  extra \"pixels\" around the character which are filled with the distance to the character (not 0),\n//                                 which allows effects like bit outlines\n//        onedge_value      --  value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)\n//        pixel_dist_scale  --  what value the SDF should increase by when moving one SDF \"pixel\" away from the edge (on the 0..255 scale)\n//                                 if positive, > onedge_value is inside; if negative, < onedge_value is inside\n//        width,height      --  output height & width of the SDF bitmap (including padding)\n//        xoff,yoff         --  output origin of the character\n//        return value      --  a 2D array of bytes 0..255, width*height in size\n//\n// pixel_dist_scale & onedge_value are a scale & bias that allows you to make\n// optimal use of the limited 0..255 for your application, trading off precision\n// and special effects. SDF values outside the range 0..255 are clamped to 0..255.\n//\n// Example:\n//      scale = stbtt_ScaleForPixelHeight(22)\n//      padding = 5\n//      onedge_value = 180\n//      pixel_dist_scale = 180/5.0 = 36.0\n//\n//      This will create an SDF bitmap in which the character is about 22 pixels\n//      high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled\n//      shape, sample the SDF at each pixel and fill the pixel if the SDF value\n//      is greater than or equal to 180/255. (You'll actually want to antialias,\n//      which is beyond the scope of this example.) Additionally, you can compute\n//      offset outlines (e.g. to stroke the character border inside & outside,\n//      or only outside). For example, to fill outside the character up to 3 SDF\n//      pixels, you would compare against (180-36.0*3)/255 = 72/255. The above\n//      choice of variables maps a range from 5 pixels outside the shape to\n//      2 pixels inside the shape to 0..255; this is intended primarily for apply\n//      outside effects only (the interior range is needed to allow proper\n//      antialiasing of the font at *smaller* sizes)\n//\n// The function computes the SDF analytically at each SDF pixel, not by e.g.\n// building a higher-res bitmap and approximating it. In theory the quality\n// should be as high as possible for an SDF of this size & representation, but\n// unclear if this is true in practice (perhaps building a higher-res bitmap\n// and computing from that can allow drop-out prevention).\n//\n// The algorithm has not been optimized at all, so expect it to be slow\n// if computing lots of characters or very large sizes.\n\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Finding the right font...\n//\n// You should really just solve this offline, keep your own tables\n// of what font is what, and don't try to get it out of the .ttf file.\n// That's because getting it out of the .ttf file is really hard, because\n// the names in the file can appear in many possible encodings, in many\n// possible languages, and e.g. if you need a case-insensitive comparison,\n// the details of that depend on the encoding & language in a complex way\n// (actually underspecified in truetype, but also gigantic).\n//\n// But you can use the provided functions in two possible ways:\n//     stbtt_FindMatchingFont() will use *case-sensitive* comparisons on\n//             unicode-encoded names to try to find the font you want;\n//             you can run this before calling stbtt_InitFont()\n//\n//     stbtt_GetFontNameString() lets you get any of the various strings\n//             from the file yourself and do your own comparisons on them.\n//             You have to have called stbtt_InitFont() first.\n\n\nSTBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);\n// returns the offset (not index) of the font that matches, or -1 if none\n//   if you use STBTT_MACSTYLE_DONTCARE, use a font name like \"Arial Bold\".\n//   if you use any other flag, use a font name like \"Arial\"; this checks\n//     the 'macStyle' header field; i don't know if fonts set this consistently\n#define STBTT_MACSTYLE_DONTCARE     0\n#define STBTT_MACSTYLE_BOLD         1\n#define STBTT_MACSTYLE_ITALIC       2\n#define STBTT_MACSTYLE_UNDERSCORE   4\n#define STBTT_MACSTYLE_NONE         8   // <= not same as 0, this makes us check the bitfield is 0\n\nSTBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);\n// returns 1/0 whether the first string interpreted as utf8 is identical to\n// the second string interpreted as big-endian utf16... useful for strings from next func\n\nSTBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);\n// returns the string (which may be big-endian double byte, e.g. for unicode)\n// and puts the length in bytes in *length.\n//\n// some of the values for the IDs are below; for more see the truetype spec:\n//     http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html\n//     http://www.microsoft.com/typography/otspec/name.htm\n\nenum { // platformID\n   STBTT_PLATFORM_ID_UNICODE   =0,\n   STBTT_PLATFORM_ID_MAC       =1,\n   STBTT_PLATFORM_ID_ISO       =2,\n   STBTT_PLATFORM_ID_MICROSOFT =3\n};\n\nenum { // encodingID for STBTT_PLATFORM_ID_UNICODE\n   STBTT_UNICODE_EID_UNICODE_1_0    =0,\n   STBTT_UNICODE_EID_UNICODE_1_1    =1,\n   STBTT_UNICODE_EID_ISO_10646      =2,\n   STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,\n   STBTT_UNICODE_EID_UNICODE_2_0_FULL=4\n};\n\nenum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT\n   STBTT_MS_EID_SYMBOL        =0,\n   STBTT_MS_EID_UNICODE_BMP   =1,\n   STBTT_MS_EID_SHIFTJIS      =2,\n   STBTT_MS_EID_UNICODE_FULL  =10\n};\n\nenum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes\n   STBTT_MAC_EID_ROMAN        =0,   STBTT_MAC_EID_ARABIC       =4,\n   STBTT_MAC_EID_JAPANESE     =1,   STBTT_MAC_EID_HEBREW       =5,\n   STBTT_MAC_EID_CHINESE_TRAD =2,   STBTT_MAC_EID_GREEK        =6,\n   STBTT_MAC_EID_KOREAN       =3,   STBTT_MAC_EID_RUSSIAN      =7\n};\n\nenum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...\n       // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs\n   STBTT_MS_LANG_ENGLISH     =0x0409,   STBTT_MS_LANG_ITALIAN     =0x0410,\n   STBTT_MS_LANG_CHINESE     =0x0804,   STBTT_MS_LANG_JAPANESE    =0x0411,\n   STBTT_MS_LANG_DUTCH       =0x0413,   STBTT_MS_LANG_KOREAN      =0x0412,\n   STBTT_MS_LANG_FRENCH      =0x040c,   STBTT_MS_LANG_RUSSIAN     =0x0419,\n   STBTT_MS_LANG_GERMAN      =0x0407,   STBTT_MS_LANG_SPANISH     =0x0409,\n   STBTT_MS_LANG_HEBREW      =0x040d,   STBTT_MS_LANG_SWEDISH     =0x041D\n};\n\nenum { // languageID for STBTT_PLATFORM_ID_MAC\n   STBTT_MAC_LANG_ENGLISH      =0 ,   STBTT_MAC_LANG_JAPANESE     =11,\n   STBTT_MAC_LANG_ARABIC       =12,   STBTT_MAC_LANG_KOREAN       =23,\n   STBTT_MAC_LANG_DUTCH        =4 ,   STBTT_MAC_LANG_RUSSIAN      =32,\n   STBTT_MAC_LANG_FRENCH       =1 ,   STBTT_MAC_LANG_SPANISH      =6 ,\n   STBTT_MAC_LANG_GERMAN       =2 ,   STBTT_MAC_LANG_SWEDISH      =5 ,\n   STBTT_MAC_LANG_HEBREW       =10,   STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,\n   STBTT_MAC_LANG_ITALIAN      =3 ,   STBTT_MAC_LANG_CHINESE_TRAD =19\n};\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // __STB_INCLUDE_STB_TRUETYPE_H__\n\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n////\n////   IMPLEMENTATION\n////\n////\n\n#ifdef STB_TRUETYPE_IMPLEMENTATION\n\n#ifndef STBTT_MAX_OVERSAMPLE\n#define STBTT_MAX_OVERSAMPLE   8\n#endif\n\n#if STBTT_MAX_OVERSAMPLE > 255\n#error \"STBTT_MAX_OVERSAMPLE cannot be > 255\"\n#endif\n\ntypedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];\n\n#ifndef STBTT_RASTERIZER_VERSION\n#define STBTT_RASTERIZER_VERSION 2\n#endif\n\n#ifdef _MSC_VER\n#define STBTT__NOTUSED(v)  (void)(v)\n#else\n#define STBTT__NOTUSED(v)  (void)sizeof(v)\n#endif\n\n//////////////////////////////////////////////////////////////////////////\n//\n// stbtt__buf helpers to parse data from file\n//\n\nstatic stbtt_uint8 stbtt__buf_get8(stbtt__buf *b)\n{\n   if (b->cursor >= b->size)\n      return 0;\n   return b->data[b->cursor++];\n}\n\nstatic stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b)\n{\n   if (b->cursor >= b->size)\n      return 0;\n   return b->data[b->cursor];\n}\n\nstatic void stbtt__buf_seek(stbtt__buf *b, int o)\n{\n   STBTT_assert(!(o > b->size || o < 0));\n   b->cursor = (o > b->size || o < 0) ? b->size : o;\n}\n\nstatic void stbtt__buf_skip(stbtt__buf *b, int o)\n{\n   stbtt__buf_seek(b, b->cursor + o);\n}\n\nstatic stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n)\n{\n   stbtt_uint32 v = 0;\n   int i;\n   STBTT_assert(n >= 1 && n <= 4);\n   for (i = 0; i < n; i++)\n      v = (v << 8) | stbtt__buf_get8(b);\n   return v;\n}\n\nstatic stbtt__buf stbtt__new_buf(const void *p, size_t size)\n{\n   stbtt__buf r;\n   STBTT_assert(size < 0x40000000);\n   r.data = (stbtt_uint8*) p;\n   r.size = (int) size;\n   r.cursor = 0;\n   return r;\n}\n\n#define stbtt__buf_get16(b)  stbtt__buf_get((b), 2)\n#define stbtt__buf_get32(b)  stbtt__buf_get((b), 4)\n\nstatic stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s)\n{\n   stbtt__buf r = stbtt__new_buf(NULL, 0);\n   if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;\n   r.data = b->data + o;\n   r.size = s;\n   return r;\n}\n\nstatic stbtt__buf stbtt__cff_get_index(stbtt__buf *b)\n{\n   int count, start, offsize;\n   start = b->cursor;\n   count = stbtt__buf_get16(b);\n   if (count) {\n      offsize = stbtt__buf_get8(b);\n      STBTT_assert(offsize >= 1 && offsize <= 4);\n      stbtt__buf_skip(b, offsize * count);\n      stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1);\n   }\n   return stbtt__buf_range(b, start, b->cursor - start);\n}\n\nstatic stbtt_uint32 stbtt__cff_int(stbtt__buf *b)\n{\n   int b0 = stbtt__buf_get8(b);\n   if (b0 >= 32 && b0 <= 246)       return b0 - 139;\n   else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108;\n   else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108;\n   else if (b0 == 28)               return stbtt__buf_get16(b);\n   else if (b0 == 29)               return stbtt__buf_get32(b);\n   STBTT_assert(0);\n   return 0;\n}\n\nstatic void stbtt__cff_skip_operand(stbtt__buf *b) {\n   int v, b0 = stbtt__buf_peek8(b);\n   STBTT_assert(b0 >= 28);\n   if (b0 == 30) {\n      stbtt__buf_skip(b, 1);\n      while (b->cursor < b->size) {\n         v = stbtt__buf_get8(b);\n         if ((v & 0xF) == 0xF || (v >> 4) == 0xF)\n            break;\n      }\n   } else {\n      stbtt__cff_int(b);\n   }\n}\n\nstatic stbtt__buf stbtt__dict_get(stbtt__buf *b, int key)\n{\n   stbtt__buf_seek(b, 0);\n   while (b->cursor < b->size) {\n      int start = b->cursor, end, op;\n      while (stbtt__buf_peek8(b) >= 28)\n         stbtt__cff_skip_operand(b);\n      end = b->cursor;\n      op = stbtt__buf_get8(b);\n      if (op == 12)  op = stbtt__buf_get8(b) | 0x100;\n      if (op == key) return stbtt__buf_range(b, start, end-start);\n   }\n   return stbtt__buf_range(b, 0, 0);\n}\n\nstatic void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out)\n{\n   int i;\n   stbtt__buf operands = stbtt__dict_get(b, key);\n   for (i = 0; i < outcount && operands.cursor < operands.size; i++)\n      out[i] = stbtt__cff_int(&operands);\n}\n\nstatic int stbtt__cff_index_count(stbtt__buf *b)\n{\n   stbtt__buf_seek(b, 0);\n   return stbtt__buf_get16(b);\n}\n\nstatic stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i)\n{\n   int count, offsize, start, end;\n   stbtt__buf_seek(&b, 0);\n   count = stbtt__buf_get16(&b);\n   offsize = stbtt__buf_get8(&b);\n   STBTT_assert(i >= 0 && i < count);\n   STBTT_assert(offsize >= 1 && offsize <= 4);\n   stbtt__buf_skip(&b, i*offsize);\n   start = stbtt__buf_get(&b, offsize);\n   end = stbtt__buf_get(&b, offsize);\n   return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);\n}\n\n//////////////////////////////////////////////////////////////////////////\n//\n// accessors to parse data from file\n//\n\n// on platforms that don't allow misaligned reads, if we want to allow\n// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE\n\n#define ttBYTE(p)     (* (stbtt_uint8 *) (p))\n#define ttCHAR(p)     (* (stbtt_int8 *) (p))\n#define ttFixed(p)    ttLONG(p)\n\nstatic stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }\nstatic stbtt_int16 ttSHORT(stbtt_uint8 *p)   { return p[0]*256 + p[1]; }\nstatic stbtt_uint32 ttULONG(stbtt_uint8 *p)  { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }\nstatic stbtt_int32 ttLONG(stbtt_uint8 *p)    { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }\n\n#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))\n#define stbtt_tag(p,str)           stbtt_tag4(p,str[0],str[1],str[2],str[3])\n\nstatic int stbtt__isfont(stbtt_uint8 *font)\n{\n   // check the version number\n   if (stbtt_tag4(font, '1',0,0,0))  return 1; // TrueType 1\n   if (stbtt_tag(font, \"typ1\"))   return 1; // TrueType with type 1 font -- we don't support this!\n   if (stbtt_tag(font, \"OTTO\"))   return 1; // OpenType with CFF\n   if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0\n   if (stbtt_tag(font, \"true\"))   return 1; // Apple specification for TrueType fonts\n   return 0;\n}\n\n// @OPTIMIZE: binary search\nstatic stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)\n{\n   stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);\n   stbtt_uint32 tabledir = fontstart + 12;\n   stbtt_int32 i;\n   for (i=0; i < num_tables; ++i) {\n      stbtt_uint32 loc = tabledir + 16*i;\n      if (stbtt_tag(data+loc+0, tag))\n         return ttULONG(data+loc+8);\n   }\n   return 0;\n}\n\nstatic int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index)\n{\n   // if it's just a font, there's only one valid index\n   if (stbtt__isfont(font_collection))\n      return index == 0 ? 0 : -1;\n\n   // check if it's a TTC\n   if (stbtt_tag(font_collection, \"ttcf\")) {\n      // version 1?\n      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {\n         stbtt_int32 n = ttLONG(font_collection+8);\n         if (index >= n)\n            return -1;\n         return ttULONG(font_collection+12+index*4);\n      }\n   }\n   return -1;\n}\n\nstatic int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)\n{\n   // if it's just a font, there's only one valid font\n   if (stbtt__isfont(font_collection))\n      return 1;\n\n   // check if it's a TTC\n   if (stbtt_tag(font_collection, \"ttcf\")) {\n      // version 1?\n      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {\n         return ttLONG(font_collection+8);\n      }\n   }\n   return 0;\n}\n\nstatic stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)\n{\n   stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };\n   stbtt__buf pdict;\n   stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);\n   if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);\n   pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);\n   stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);\n   if (!subrsoff) return stbtt__new_buf(NULL, 0);\n   stbtt__buf_seek(&cff, private_loc[1]+subrsoff);\n   return stbtt__cff_get_index(&cff);\n}\n\n// since most people won't use this, find this table the first time it's needed\nstatic int stbtt__get_svg(stbtt_fontinfo *info)\n{\n   stbtt_uint32 t;\n   if (info->svg < 0) {\n      t = stbtt__find_table(info->data, info->fontstart, \"SVG \");\n      if (t) {\n         stbtt_uint32 offset = ttULONG(info->data + t + 2);\n         info->svg = t + offset;\n      } else {\n         info->svg = 0;\n      }\n   }\n   return info->svg;\n}\n\nstatic int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)\n{\n   stbtt_uint32 cmap, t;\n   stbtt_int32 i,numTables;\n\n   info->data = data;\n   info->fontstart = fontstart;\n   info->cff = stbtt__new_buf(NULL, 0);\n\n   cmap = stbtt__find_table(data, fontstart, \"cmap\");       // required\n   info->loca = stbtt__find_table(data, fontstart, \"loca\"); // required\n   info->head = stbtt__find_table(data, fontstart, \"head\"); // required\n   info->glyf = stbtt__find_table(data, fontstart, \"glyf\"); // required\n   info->hhea = stbtt__find_table(data, fontstart, \"hhea\"); // required\n   info->hmtx = stbtt__find_table(data, fontstart, \"hmtx\"); // required\n   info->kern = stbtt__find_table(data, fontstart, \"kern\"); // not required\n   info->gpos = stbtt__find_table(data, fontstart, \"GPOS\"); // not required\n\n   if (!cmap || !info->head || !info->hhea || !info->hmtx)\n      return 0;\n   if (info->glyf) {\n      // required for truetype\n      if (!info->loca) return 0;\n   } else {\n      // initialization for CFF / Type2 fonts (OTF)\n      stbtt__buf b, topdict, topdictidx;\n      stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;\n      stbtt_uint32 cff;\n\n      cff = stbtt__find_table(data, fontstart, \"CFF \");\n      if (!cff) return 0;\n\n      info->fontdicts = stbtt__new_buf(NULL, 0);\n      info->fdselect = stbtt__new_buf(NULL, 0);\n\n      // @TODO this should use size from table (not 512MB)\n      info->cff = stbtt__new_buf(data+cff, 512*1024*1024);\n      b = info->cff;\n\n      // read the header\n      stbtt__buf_skip(&b, 2);\n      stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize\n\n      // @TODO the name INDEX could list multiple fonts,\n      // but we just use the first one.\n      stbtt__cff_get_index(&b);  // name INDEX\n      topdictidx = stbtt__cff_get_index(&b);\n      topdict = stbtt__cff_index_get(topdictidx, 0);\n      stbtt__cff_get_index(&b);  // string INDEX\n      info->gsubrs = stbtt__cff_get_index(&b);\n\n      stbtt__dict_get_ints(&topdict, 17, 1, &charstrings);\n      stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);\n      stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);\n      stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);\n      info->subrs = stbtt__get_subrs(b, topdict);\n\n      // we only support Type 2 charstrings\n      if (cstype != 2) return 0;\n      if (charstrings == 0) return 0;\n\n      if (fdarrayoff) {\n         // looks like a CID font\n         if (!fdselectoff) return 0;\n         stbtt__buf_seek(&b, fdarrayoff);\n         info->fontdicts = stbtt__cff_get_index(&b);\n         info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff);\n      }\n\n      stbtt__buf_seek(&b, charstrings);\n      info->charstrings = stbtt__cff_get_index(&b);\n   }\n\n   t = stbtt__find_table(data, fontstart, \"maxp\");\n   if (t)\n      info->numGlyphs = ttUSHORT(data+t+4);\n   else\n      info->numGlyphs = 0xffff;\n\n   info->svg = -1;\n\n   // find a cmap encoding table we understand *now* to avoid searching\n   // later. (todo: could make this installable)\n   // the same regardless of glyph.\n   numTables = ttUSHORT(data + cmap + 2);\n   info->index_map = 0;\n   for (i=0; i < numTables; ++i) {\n      stbtt_uint32 encoding_record = cmap + 4 + 8 * i;\n      // find an encoding we understand:\n      switch(ttUSHORT(data+encoding_record)) {\n         case STBTT_PLATFORM_ID_MICROSOFT:\n            switch (ttUSHORT(data+encoding_record+2)) {\n               case STBTT_MS_EID_UNICODE_BMP:\n               case STBTT_MS_EID_UNICODE_FULL:\n                  // MS/Unicode\n                  info->index_map = cmap + ttULONG(data+encoding_record+4);\n                  break;\n            }\n            break;\n        case STBTT_PLATFORM_ID_UNICODE:\n            // Mac/iOS has these\n            // all the encodingIDs are unicode, so we don't bother to check it\n            info->index_map = cmap + ttULONG(data+encoding_record+4);\n            break;\n      }\n   }\n   if (info->index_map == 0)\n      return 0;\n\n   info->indexToLocFormat = ttUSHORT(data+info->head + 50);\n   return 1;\n}\n\nSTBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)\n{\n   stbtt_uint8 *data = info->data;\n   stbtt_uint32 index_map = info->index_map;\n\n   stbtt_uint16 format = ttUSHORT(data + index_map + 0);\n   if (format == 0) { // apple byte encoding\n      stbtt_int32 bytes = ttUSHORT(data + index_map + 2);\n      if (unicode_codepoint < bytes-6)\n         return ttBYTE(data + index_map + 6 + unicode_codepoint);\n      return 0;\n   } else if (format == 6) {\n      stbtt_uint32 first = ttUSHORT(data + index_map + 6);\n      stbtt_uint32 count = ttUSHORT(data + index_map + 8);\n      if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)\n         return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);\n      return 0;\n   } else if (format == 2) {\n      STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean\n      return 0;\n   } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges\n      stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;\n      stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;\n      stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);\n      stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;\n\n      // do a binary search of the segments\n      stbtt_uint32 endCount = index_map + 14;\n      stbtt_uint32 search = endCount;\n\n      if (unicode_codepoint > 0xffff)\n         return 0;\n\n      // they lie from endCount .. endCount + segCount\n      // but searchRange is the nearest power of two, so...\n      if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))\n         search += rangeShift*2;\n\n      // now decrement to bias correctly to find smallest\n      search -= 2;\n      while (entrySelector) {\n         stbtt_uint16 end;\n         searchRange >>= 1;\n         end = ttUSHORT(data + search + searchRange*2);\n         if (unicode_codepoint > end)\n            search += searchRange*2;\n         --entrySelector;\n      }\n      search += 2;\n\n      {\n         stbtt_uint16 offset, start, last;\n         stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);\n\n         start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);\n         last = ttUSHORT(data + endCount + 2*item);\n         if (unicode_codepoint < start || unicode_codepoint > last)\n            return 0;\n\n         offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);\n         if (offset == 0)\n            return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));\n\n         return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);\n      }\n   } else if (format == 12 || format == 13) {\n      stbtt_uint32 ngroups = ttULONG(data+index_map+12);\n      stbtt_int32 low,high;\n      low = 0; high = (stbtt_int32)ngroups;\n      // Binary search the right group.\n      while (low < high) {\n         stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high\n         stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);\n         stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);\n         if ((stbtt_uint32) unicode_codepoint < start_char)\n            high = mid;\n         else if ((stbtt_uint32) unicode_codepoint > end_char)\n            low = mid+1;\n         else {\n            stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);\n            if (format == 12)\n               return start_glyph + unicode_codepoint-start_char;\n            else // format == 13\n               return start_glyph;\n         }\n      }\n      return 0; // not found\n   }\n   // @TODO\n   STBTT_assert(0);\n   return 0;\n}\n\nSTBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)\n{\n   return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);\n}\n\nstatic void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)\n{\n   v->type = type;\n   v->x = (stbtt_int16) x;\n   v->y = (stbtt_int16) y;\n   v->cx = (stbtt_int16) cx;\n   v->cy = (stbtt_int16) cy;\n}\n\nstatic int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)\n{\n   int g1,g2;\n\n   STBTT_assert(!info->cff.size);\n\n   if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range\n   if (info->indexToLocFormat >= 2)    return -1; // unknown index->glyph map format\n\n   if (info->indexToLocFormat == 0) {\n      g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;\n      g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;\n   } else {\n      g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);\n      g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);\n   }\n\n   return g1==g2 ? -1 : g1; // if length is 0, return -1\n}\n\nstatic int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);\n\nSTBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)\n{\n   if (info->cff.size) {\n      stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);\n   } else {\n      int g = stbtt__GetGlyfOffset(info, glyph_index);\n      if (g < 0) return 0;\n\n      if (x0) *x0 = ttSHORT(info->data + g + 2);\n      if (y0) *y0 = ttSHORT(info->data + g + 4);\n      if (x1) *x1 = ttSHORT(info->data + g + 6);\n      if (y1) *y1 = ttSHORT(info->data + g + 8);\n   }\n   return 1;\n}\n\nSTBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)\n{\n   return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);\n}\n\nSTBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)\n{\n   stbtt_int16 numberOfContours;\n   int g;\n   if (info->cff.size)\n      return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0;\n   g = stbtt__GetGlyfOffset(info, glyph_index);\n   if (g < 0) return 1;\n   numberOfContours = ttSHORT(info->data + g);\n   return numberOfContours == 0;\n}\n\nstatic int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,\n    stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)\n{\n   if (start_off) {\n      if (was_off)\n         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);\n      stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);\n   } else {\n      if (was_off)\n         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);\n      else\n         stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);\n   }\n   return num_vertices;\n}\n\nstatic int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)\n{\n   stbtt_int16 numberOfContours;\n   stbtt_uint8 *endPtsOfContours;\n   stbtt_uint8 *data = info->data;\n   stbtt_vertex *vertices=0;\n   int num_vertices=0;\n   int g = stbtt__GetGlyfOffset(info, glyph_index);\n\n   *pvertices = NULL;\n\n   if (g < 0) return 0;\n\n   numberOfContours = ttSHORT(data + g);\n\n   if (numberOfContours > 0) {\n      stbtt_uint8 flags=0,flagcount;\n      stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;\n      stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;\n      stbtt_uint8 *points;\n      endPtsOfContours = (data + g + 10);\n      ins = ttUSHORT(data + g + 10 + numberOfContours * 2);\n      points = data + g + 10 + numberOfContours * 2 + 2 + ins;\n\n      n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);\n\n      m = n + 2*numberOfContours;  // a loose bound on how many vertices we might need\n      vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);\n      if (vertices == 0)\n         return 0;\n\n      next_move = 0;\n      flagcount=0;\n\n      // in first pass, we load uninterpreted data into the allocated array\n      // above, shifted to the end of the array so we won't overwrite it when\n      // we create our final data starting from the front\n\n      off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated\n\n      // first load flags\n\n      for (i=0; i < n; ++i) {\n         if (flagcount == 0) {\n            flags = *points++;\n            if (flags & 8)\n               flagcount = *points++;\n         } else\n            --flagcount;\n         vertices[off+i].type = flags;\n      }\n\n      // now load x coordinates\n      x=0;\n      for (i=0; i < n; ++i) {\n         flags = vertices[off+i].type;\n         if (flags & 2) {\n            stbtt_int16 dx = *points++;\n            x += (flags & 16) ? dx : -dx; // ???\n         } else {\n            if (!(flags & 16)) {\n               x = x + (stbtt_int16) (points[0]*256 + points[1]);\n               points += 2;\n            }\n         }\n         vertices[off+i].x = (stbtt_int16) x;\n      }\n\n      // now load y coordinates\n      y=0;\n      for (i=0; i < n; ++i) {\n         flags = vertices[off+i].type;\n         if (flags & 4) {\n            stbtt_int16 dy = *points++;\n            y += (flags & 32) ? dy : -dy; // ???\n         } else {\n            if (!(flags & 32)) {\n               y = y + (stbtt_int16) (points[0]*256 + points[1]);\n               points += 2;\n            }\n         }\n         vertices[off+i].y = (stbtt_int16) y;\n      }\n\n      // now convert them to our format\n      num_vertices=0;\n      sx = sy = cx = cy = scx = scy = 0;\n      for (i=0; i < n; ++i) {\n         flags = vertices[off+i].type;\n         x     = (stbtt_int16) vertices[off+i].x;\n         y     = (stbtt_int16) vertices[off+i].y;\n\n         if (next_move == i) {\n            if (i != 0)\n               num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);\n\n            // now start the new one\n            start_off = !(flags & 1);\n            if (start_off) {\n               // if we start off with an off-curve point, then when we need to find a point on the curve\n               // where we can start, and we need to save some state for when we wraparound.\n               scx = x;\n               scy = y;\n               if (!(vertices[off+i+1].type & 1)) {\n                  // next point is also a curve point, so interpolate an on-point curve\n                  sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;\n                  sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;\n               } else {\n                  // otherwise just use the next point as our start point\n                  sx = (stbtt_int32) vertices[off+i+1].x;\n                  sy = (stbtt_int32) vertices[off+i+1].y;\n                  ++i; // we're using point i+1 as the starting point, so skip it\n               }\n            } else {\n               sx = x;\n               sy = y;\n            }\n            stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);\n            was_off = 0;\n            next_move = 1 + ttUSHORT(endPtsOfContours+j*2);\n            ++j;\n         } else {\n            if (!(flags & 1)) { // if it's a curve\n               if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint\n                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);\n               cx = x;\n               cy = y;\n               was_off = 1;\n            } else {\n               if (was_off)\n                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);\n               else\n                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);\n               was_off = 0;\n            }\n         }\n      }\n      num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);\n   } else if (numberOfContours < 0) {\n      // Compound shapes.\n      int more = 1;\n      stbtt_uint8 *comp = data + g + 10;\n      num_vertices = 0;\n      vertices = 0;\n      while (more) {\n         stbtt_uint16 flags, gidx;\n         int comp_num_verts = 0, i;\n         stbtt_vertex *comp_verts = 0, *tmp = 0;\n         float mtx[6] = {1,0,0,1,0,0}, m, n;\n\n         flags = ttSHORT(comp); comp+=2;\n         gidx = ttSHORT(comp); comp+=2;\n\n         if (flags & 2) { // XY values\n            if (flags & 1) { // shorts\n               mtx[4] = ttSHORT(comp); comp+=2;\n               mtx[5] = ttSHORT(comp); comp+=2;\n            } else {\n               mtx[4] = ttCHAR(comp); comp+=1;\n               mtx[5] = ttCHAR(comp); comp+=1;\n            }\n         }\n         else {\n            // @TODO handle matching point\n            STBTT_assert(0);\n         }\n         if (flags & (1<<3)) { // WE_HAVE_A_SCALE\n            mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[1] = mtx[2] = 0;\n         } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE\n            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[1] = mtx[2] = 0;\n            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;\n         } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO\n            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;\n         }\n\n         // Find transformation scales.\n         m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);\n         n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);\n\n         // Get indexed glyph.\n         comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);\n         if (comp_num_verts > 0) {\n            // Transform vertices.\n            for (i = 0; i < comp_num_verts; ++i) {\n               stbtt_vertex* v = &comp_verts[i];\n               stbtt_vertex_type x,y;\n               x=v->x; y=v->y;\n               v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));\n               v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));\n               x=v->cx; y=v->cy;\n               v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));\n               v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));\n            }\n            // Append vertices.\n            tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);\n            if (!tmp) {\n               if (vertices) STBTT_free(vertices, info->userdata);\n               if (comp_verts) STBTT_free(comp_verts, info->userdata);\n               return 0;\n            }\n            if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));\n            STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));\n            if (vertices) STBTT_free(vertices, info->userdata);\n            vertices = tmp;\n            STBTT_free(comp_verts, info->userdata);\n            num_vertices += comp_num_verts;\n         }\n         // More components ?\n         more = flags & (1<<5);\n      }\n   } else {\n      // numberOfCounters == 0, do nothing\n   }\n\n   *pvertices = vertices;\n   return num_vertices;\n}\n\ntypedef struct\n{\n   int bounds;\n   int started;\n   float first_x, first_y;\n   float x, y;\n   stbtt_int32 min_x, max_x, min_y, max_y;\n\n   stbtt_vertex *pvertices;\n   int num_vertices;\n} stbtt__csctx;\n\n#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}\n\nstatic void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y)\n{\n   if (x > c->max_x || !c->started) c->max_x = x;\n   if (y > c->max_y || !c->started) c->max_y = y;\n   if (x < c->min_x || !c->started) c->min_x = x;\n   if (y < c->min_y || !c->started) c->min_y = y;\n   c->started = 1;\n}\n\nstatic void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1)\n{\n   if (c->bounds) {\n      stbtt__track_vertex(c, x, y);\n      if (type == STBTT_vcubic) {\n         stbtt__track_vertex(c, cx, cy);\n         stbtt__track_vertex(c, cx1, cy1);\n      }\n   } else {\n      stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);\n      c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1;\n      c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1;\n   }\n   c->num_vertices++;\n}\n\nstatic void stbtt__csctx_close_shape(stbtt__csctx *ctx)\n{\n   if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)\n      stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);\n}\n\nstatic void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy)\n{\n   stbtt__csctx_close_shape(ctx);\n   ctx->first_x = ctx->x = ctx->x + dx;\n   ctx->first_y = ctx->y = ctx->y + dy;\n   stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);\n}\n\nstatic void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy)\n{\n   ctx->x += dx;\n   ctx->y += dy;\n   stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);\n}\n\nstatic void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)\n{\n   float cx1 = ctx->x + dx1;\n   float cy1 = ctx->y + dy1;\n   float cx2 = cx1 + dx2;\n   float cy2 = cy1 + dy2;\n   ctx->x = cx2 + dx3;\n   ctx->y = cy2 + dy3;\n   stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);\n}\n\nstatic stbtt__buf stbtt__get_subr(stbtt__buf idx, int n)\n{\n   int count = stbtt__cff_index_count(&idx);\n   int bias = 107;\n   if (count >= 33900)\n      bias = 32768;\n   else if (count >= 1240)\n      bias = 1131;\n   n += bias;\n   if (n < 0 || n >= count)\n      return stbtt__new_buf(NULL, 0);\n   return stbtt__cff_index_get(idx, n);\n}\n\nstatic stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index)\n{\n   stbtt__buf fdselect = info->fdselect;\n   int nranges, start, end, v, fmt, fdselector = -1, i;\n\n   stbtt__buf_seek(&fdselect, 0);\n   fmt = stbtt__buf_get8(&fdselect);\n   if (fmt == 0) {\n      // untested\n      stbtt__buf_skip(&fdselect, glyph_index);\n      fdselector = stbtt__buf_get8(&fdselect);\n   } else if (fmt == 3) {\n      nranges = stbtt__buf_get16(&fdselect);\n      start = stbtt__buf_get16(&fdselect);\n      for (i = 0; i < nranges; i++) {\n         v = stbtt__buf_get8(&fdselect);\n         end = stbtt__buf_get16(&fdselect);\n         if (glyph_index >= start && glyph_index < end) {\n            fdselector = v;\n            break;\n         }\n         start = end;\n      }\n   }\n   if (fdselector == -1) stbtt__new_buf(NULL, 0);\n   return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));\n}\n\nstatic int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c)\n{\n   int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;\n   int has_subrs = 0, clear_stack;\n   float s[48];\n   stbtt__buf subr_stack[10], subrs = info->subrs, b;\n   float f;\n\n#define STBTT__CSERR(s) (0)\n\n   // this currently ignores the initial width value, which isn't needed if we have hmtx\n   b = stbtt__cff_index_get(info->charstrings, glyph_index);\n   while (b.cursor < b.size) {\n      i = 0;\n      clear_stack = 1;\n      b0 = stbtt__buf_get8(&b);\n      switch (b0) {\n      // @TODO implement hinting\n      case 0x13: // hintmask\n      case 0x14: // cntrmask\n         if (in_header)\n            maskbits += (sp / 2); // implicit \"vstem\"\n         in_header = 0;\n         stbtt__buf_skip(&b, (maskbits + 7) / 8);\n         break;\n\n      case 0x01: // hstem\n      case 0x03: // vstem\n      case 0x12: // hstemhm\n      case 0x17: // vstemhm\n         maskbits += (sp / 2);\n         break;\n\n      case 0x15: // rmoveto\n         in_header = 0;\n         if (sp < 2) return STBTT__CSERR(\"rmoveto stack\");\n         stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);\n         break;\n      case 0x04: // vmoveto\n         in_header = 0;\n         if (sp < 1) return STBTT__CSERR(\"vmoveto stack\");\n         stbtt__csctx_rmove_to(c, 0, s[sp-1]);\n         break;\n      case 0x16: // hmoveto\n         in_header = 0;\n         if (sp < 1) return STBTT__CSERR(\"hmoveto stack\");\n         stbtt__csctx_rmove_to(c, s[sp-1], 0);\n         break;\n\n      case 0x05: // rlineto\n         if (sp < 2) return STBTT__CSERR(\"rlineto stack\");\n         for (; i + 1 < sp; i += 2)\n            stbtt__csctx_rline_to(c, s[i], s[i+1]);\n         break;\n\n      // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical\n      // starting from a different place.\n\n      case 0x07: // vlineto\n         if (sp < 1) return STBTT__CSERR(\"vlineto stack\");\n         goto vlineto;\n      case 0x06: // hlineto\n         if (sp < 1) return STBTT__CSERR(\"hlineto stack\");\n         for (;;) {\n            if (i >= sp) break;\n            stbtt__csctx_rline_to(c, s[i], 0);\n            i++;\n      vlineto:\n            if (i >= sp) break;\n            stbtt__csctx_rline_to(c, 0, s[i]);\n            i++;\n         }\n         break;\n\n      case 0x1F: // hvcurveto\n         if (sp < 4) return STBTT__CSERR(\"hvcurveto stack\");\n         goto hvcurveto;\n      case 0x1E: // vhcurveto\n         if (sp < 4) return STBTT__CSERR(\"vhcurveto stack\");\n         for (;;) {\n            if (i + 3 >= sp) break;\n            stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);\n            i += 4;\n      hvcurveto:\n            if (i + 3 >= sp) break;\n            stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);\n            i += 4;\n         }\n         break;\n\n      case 0x08: // rrcurveto\n         if (sp < 6) return STBTT__CSERR(\"rcurveline stack\");\n         for (; i + 5 < sp; i += 6)\n            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);\n         break;\n\n      case 0x18: // rcurveline\n         if (sp < 8) return STBTT__CSERR(\"rcurveline stack\");\n         for (; i + 5 < sp - 2; i += 6)\n            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);\n         if (i + 1 >= sp) return STBTT__CSERR(\"rcurveline stack\");\n         stbtt__csctx_rline_to(c, s[i], s[i+1]);\n         break;\n\n      case 0x19: // rlinecurve\n         if (sp < 8) return STBTT__CSERR(\"rlinecurve stack\");\n         for (; i + 1 < sp - 6; i += 2)\n            stbtt__csctx_rline_to(c, s[i], s[i+1]);\n         if (i + 5 >= sp) return STBTT__CSERR(\"rlinecurve stack\");\n         stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);\n         break;\n\n      case 0x1A: // vvcurveto\n      case 0x1B: // hhcurveto\n         if (sp < 4) return STBTT__CSERR(\"(vv|hh)curveto stack\");\n         f = 0.0;\n         if (sp & 1) { f = s[i]; i++; }\n         for (; i + 3 < sp; i += 4) {\n            if (b0 == 0x1B)\n               stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);\n            else\n               stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);\n            f = 0.0;\n         }\n         break;\n\n      case 0x0A: // callsubr\n         if (!has_subrs) {\n            if (info->fdselect.size)\n               subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);\n            has_subrs = 1;\n         }\n         // FALLTHROUGH\n      case 0x1D: // callgsubr\n         if (sp < 1) return STBTT__CSERR(\"call(g|)subr stack\");\n         v = (int) s[--sp];\n         if (subr_stack_height >= 10) return STBTT__CSERR(\"recursion limit\");\n         subr_stack[subr_stack_height++] = b;\n         b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);\n         if (b.size == 0) return STBTT__CSERR(\"subr not found\");\n         b.cursor = 0;\n         clear_stack = 0;\n         break;\n\n      case 0x0B: // return\n         if (subr_stack_height <= 0) return STBTT__CSERR(\"return outside subr\");\n         b = subr_stack[--subr_stack_height];\n         clear_stack = 0;\n         break;\n\n      case 0x0E: // endchar\n         stbtt__csctx_close_shape(c);\n         return 1;\n\n      case 0x0C: { // two-byte escape\n         float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;\n         float dx, dy;\n         int b1 = stbtt__buf_get8(&b);\n         switch (b1) {\n         // @TODO These \"flex\" implementations ignore the flex-depth and resolution,\n         // and always draw beziers.\n         case 0x22: // hflex\n            if (sp < 7) return STBTT__CSERR(\"hflex stack\");\n            dx1 = s[0];\n            dx2 = s[1];\n            dy2 = s[2];\n            dx3 = s[3];\n            dx4 = s[4];\n            dx5 = s[5];\n            dx6 = s[6];\n            stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);\n            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);\n            break;\n\n         case 0x23: // flex\n            if (sp < 13) return STBTT__CSERR(\"flex stack\");\n            dx1 = s[0];\n            dy1 = s[1];\n            dx2 = s[2];\n            dy2 = s[3];\n            dx3 = s[4];\n            dy3 = s[5];\n            dx4 = s[6];\n            dy4 = s[7];\n            dx5 = s[8];\n            dy5 = s[9];\n            dx6 = s[10];\n            dy6 = s[11];\n            //fd is s[12]\n            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);\n            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);\n            break;\n\n         case 0x24: // hflex1\n            if (sp < 9) return STBTT__CSERR(\"hflex1 stack\");\n            dx1 = s[0];\n            dy1 = s[1];\n            dx2 = s[2];\n            dy2 = s[3];\n            dx3 = s[4];\n            dx4 = s[5];\n            dx5 = s[6];\n            dy5 = s[7];\n            dx6 = s[8];\n            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);\n            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));\n            break;\n\n         case 0x25: // flex1\n            if (sp < 11) return STBTT__CSERR(\"flex1 stack\");\n            dx1 = s[0];\n            dy1 = s[1];\n            dx2 = s[2];\n            dy2 = s[3];\n            dx3 = s[4];\n            dy3 = s[5];\n            dx4 = s[6];\n            dy4 = s[7];\n            dx5 = s[8];\n            dy5 = s[9];\n            dx6 = dy6 = s[10];\n            dx = dx1+dx2+dx3+dx4+dx5;\n            dy = dy1+dy2+dy3+dy4+dy5;\n            if (STBTT_fabs(dx) > STBTT_fabs(dy))\n               dy6 = -dy;\n            else\n               dx6 = -dx;\n            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);\n            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);\n            break;\n\n         default:\n            return STBTT__CSERR(\"unimplemented\");\n         }\n      } break;\n\n      default:\n         if (b0 != 255 && b0 != 28 && b0 < 32)\n            return STBTT__CSERR(\"reserved operator\");\n\n         // push immediate\n         if (b0 == 255) {\n            f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;\n         } else {\n            stbtt__buf_skip(&b, -1);\n            f = (float)(stbtt_int16)stbtt__cff_int(&b);\n         }\n         if (sp >= 48) return STBTT__CSERR(\"push stack overflow\");\n         s[sp++] = f;\n         clear_stack = 0;\n         break;\n      }\n      if (clear_stack) sp = 0;\n   }\n   return STBTT__CSERR(\"no endchar\");\n\n#undef STBTT__CSERR\n}\n\nstatic int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)\n{\n   // runs the charstring twice, once to count and once to output (to avoid realloc)\n   stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1);\n   stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0);\n   if (stbtt__run_charstring(info, glyph_index, &count_ctx)) {\n      *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata);\n      output_ctx.pvertices = *pvertices;\n      if (stbtt__run_charstring(info, glyph_index, &output_ctx)) {\n         STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices);\n         return output_ctx.num_vertices;\n      }\n   }\n   *pvertices = NULL;\n   return 0;\n}\n\nstatic int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)\n{\n   stbtt__csctx c = STBTT__CSCTX_INIT(1);\n   int r = stbtt__run_charstring(info, glyph_index, &c);\n   if (x0)  *x0 = r ? c.min_x : 0;\n   if (y0)  *y0 = r ? c.min_y : 0;\n   if (x1)  *x1 = r ? c.max_x : 0;\n   if (y1)  *y1 = r ? c.max_y : 0;\n   return r ? c.num_vertices : 0;\n}\n\nSTBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)\n{\n   if (!info->cff.size)\n      return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices);\n   else\n      return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices);\n}\n\nSTBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)\n{\n   stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);\n   if (glyph_index < numOfLongHorMetrics) {\n      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*glyph_index);\n      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);\n   } else {\n      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));\n      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));\n   }\n}\n\nSTBTT_DEF int  stbtt_GetKerningTableLength(const stbtt_fontinfo *info)\n{\n   stbtt_uint8 *data = info->data + info->kern;\n\n   // we only look at the first table. it must be 'horizontal' and format 0.\n   if (!info->kern)\n      return 0;\n   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1\n      return 0;\n   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format\n      return 0;\n\n   return ttUSHORT(data+10);\n}\n\nSTBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length)\n{\n   stbtt_uint8 *data = info->data + info->kern;\n   int k, length;\n\n   // we only look at the first table. it must be 'horizontal' and format 0.\n   if (!info->kern)\n      return 0;\n   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1\n      return 0;\n   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format\n      return 0;\n\n   length = ttUSHORT(data+10);\n   if (table_length < length)\n      length = table_length;\n\n   for (k = 0; k < length; k++)\n   {\n      table[k].glyph1 = ttUSHORT(data+18+(k*6));\n      table[k].glyph2 = ttUSHORT(data+20+(k*6));\n      table[k].advance = ttSHORT(data+22+(k*6));\n   }\n\n   return length;\n}\n\nstatic int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)\n{\n   stbtt_uint8 *data = info->data + info->kern;\n   stbtt_uint32 needle, straw;\n   int l, r, m;\n\n   // we only look at the first table. it must be 'horizontal' and format 0.\n   if (!info->kern)\n      return 0;\n   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1\n      return 0;\n   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format\n      return 0;\n\n   l = 0;\n   r = ttUSHORT(data+10) - 1;\n   needle = glyph1 << 16 | glyph2;\n   while (l <= r) {\n      m = (l + r) >> 1;\n      straw = ttULONG(data+18+(m*6)); // note: unaligned read\n      if (needle < straw)\n         r = m - 1;\n      else if (needle > straw)\n         l = m + 1;\n      else\n         return ttSHORT(data+22+(m*6));\n   }\n   return 0;\n}\n\nstatic stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)\n{\n   stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);\n   switch (coverageFormat) {\n      case 1: {\n         stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);\n\n         // Binary search.\n         stbtt_int32 l=0, r=glyphCount-1, m;\n         int straw, needle=glyph;\n         while (l <= r) {\n            stbtt_uint8 *glyphArray = coverageTable + 4;\n            stbtt_uint16 glyphID;\n            m = (l + r) >> 1;\n            glyphID = ttUSHORT(glyphArray + 2 * m);\n            straw = glyphID;\n            if (needle < straw)\n               r = m - 1;\n            else if (needle > straw)\n               l = m + 1;\n            else {\n               return m;\n            }\n         }\n         break;\n      }\n\n      case 2: {\n         stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);\n         stbtt_uint8 *rangeArray = coverageTable + 4;\n\n         // Binary search.\n         stbtt_int32 l=0, r=rangeCount-1, m;\n         int strawStart, strawEnd, needle=glyph;\n         while (l <= r) {\n            stbtt_uint8 *rangeRecord;\n            m = (l + r) >> 1;\n            rangeRecord = rangeArray + 6 * m;\n            strawStart = ttUSHORT(rangeRecord);\n            strawEnd = ttUSHORT(rangeRecord + 2);\n            if (needle < strawStart)\n               r = m - 1;\n            else if (needle > strawEnd)\n               l = m + 1;\n            else {\n               stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);\n               return startCoverageIndex + glyph - strawStart;\n            }\n         }\n         break;\n      }\n\n      default: return -1; // unsupported\n   }\n\n   return -1;\n}\n\nstatic stbtt_int32  stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)\n{\n   stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);\n   switch (classDefFormat)\n   {\n      case 1: {\n         stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);\n         stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);\n         stbtt_uint8 *classDef1ValueArray = classDefTable + 6;\n\n         if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)\n            return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));\n         break;\n      }\n\n      case 2: {\n         stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);\n         stbtt_uint8 *classRangeRecords = classDefTable + 4;\n\n         // Binary search.\n         stbtt_int32 l=0, r=classRangeCount-1, m;\n         int strawStart, strawEnd, needle=glyph;\n         while (l <= r) {\n            stbtt_uint8 *classRangeRecord;\n            m = (l + r) >> 1;\n            classRangeRecord = classRangeRecords + 6 * m;\n            strawStart = ttUSHORT(classRangeRecord);\n            strawEnd = ttUSHORT(classRangeRecord + 2);\n            if (needle < strawStart)\n               r = m - 1;\n            else if (needle > strawEnd)\n               l = m + 1;\n            else\n               return (stbtt_int32)ttUSHORT(classRangeRecord + 4);\n         }\n         break;\n      }\n\n      default:\n         return -1; // Unsupported definition type, return an error.\n   }\n\n   // \"All glyphs not assigned to a class fall into class 0\". (OpenType spec)\n   return 0;\n}\n\n// Define to STBTT_assert(x) if you want to break on unimplemented formats.\n#define STBTT_GPOS_TODO_assert(x)\n\nstatic stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)\n{\n   stbtt_uint16 lookupListOffset;\n   stbtt_uint8 *lookupList;\n   stbtt_uint16 lookupCount;\n   stbtt_uint8 *data;\n   stbtt_int32 i, sti;\n\n   if (!info->gpos) return 0;\n\n   data = info->data + info->gpos;\n\n   if (ttUSHORT(data+0) != 1) return 0; // Major version 1\n   if (ttUSHORT(data+2) != 0) return 0; // Minor version 0\n\n   lookupListOffset = ttUSHORT(data+8);\n   lookupList = data + lookupListOffset;\n   lookupCount = ttUSHORT(lookupList);\n\n   for (i=0; i<lookupCount; ++i) {\n      stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);\n      stbtt_uint8 *lookupTable = lookupList + lookupOffset;\n\n      stbtt_uint16 lookupType = ttUSHORT(lookupTable);\n      stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);\n      stbtt_uint8 *subTableOffsets = lookupTable + 6;\n      if (lookupType != 2) // Pair Adjustment Positioning Subtable\n         continue;\n\n      for (sti=0; sti<subTableCount; sti++) {\n         stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);\n         stbtt_uint8 *table = lookupTable + subtableOffset;\n         stbtt_uint16 posFormat = ttUSHORT(table);\n         stbtt_uint16 coverageOffset = ttUSHORT(table + 2);\n         stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);\n         if (coverageIndex == -1) continue;\n\n         switch (posFormat) {\n            case 1: {\n               stbtt_int32 l, r, m;\n               int straw, needle;\n               stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);\n               stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);\n               if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?\n                  stbtt_int32 valueRecordPairSizeInBytes = 2;\n                  stbtt_uint16 pairSetCount = ttUSHORT(table + 8);\n                  stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);\n                  stbtt_uint8 *pairValueTable = table + pairPosOffset;\n                  stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);\n                  stbtt_uint8 *pairValueArray = pairValueTable + 2;\n\n                  if (coverageIndex >= pairSetCount) return 0;\n\n                  needle=glyph2;\n                  r=pairValueCount-1;\n                  l=0;\n\n                  // Binary search.\n                  while (l <= r) {\n                     stbtt_uint16 secondGlyph;\n                     stbtt_uint8 *pairValue;\n                     m = (l + r) >> 1;\n                     pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;\n                     secondGlyph = ttUSHORT(pairValue);\n                     straw = secondGlyph;\n                     if (needle < straw)\n                        r = m - 1;\n                     else if (needle > straw)\n                        l = m + 1;\n                     else {\n                        stbtt_int16 xAdvance = ttSHORT(pairValue + 2);\n                        return xAdvance;\n                     }\n                  }\n               } else\n                  return 0;\n               break;\n            }\n\n            case 2: {\n               stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);\n               stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);\n               if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?\n                  stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);\n                  stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);\n                  int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);\n                  int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);\n\n                  stbtt_uint16 class1Count = ttUSHORT(table + 12);\n                  stbtt_uint16 class2Count = ttUSHORT(table + 14);\n                  stbtt_uint8 *class1Records, *class2Records;\n                  stbtt_int16 xAdvance;\n\n                  if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed\n                  if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed\n\n                  class1Records = table + 16;\n                  class2Records = class1Records + 2 * (glyph1class * class2Count);\n                  xAdvance = ttSHORT(class2Records + 2 * glyph2class);\n                  return xAdvance;\n               } else\n                  return 0;\n               break;\n            }\n\n            default:\n               return 0; // Unsupported position format\n         }\n      }\n   }\n\n   return 0;\n}\n\nSTBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)\n{\n   int xAdvance = 0;\n\n   if (info->gpos)\n      xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);\n   else if (info->kern)\n      xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);\n\n   return xAdvance;\n}\n\nSTBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)\n{\n   if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs\n      return 0;\n   return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));\n}\n\nSTBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)\n{\n   stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);\n}\n\nSTBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)\n{\n   if (ascent ) *ascent  = ttSHORT(info->data+info->hhea + 4);\n   if (descent) *descent = ttSHORT(info->data+info->hhea + 6);\n   if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);\n}\n\nSTBTT_DEF int  stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap)\n{\n   int tab = stbtt__find_table(info->data, info->fontstart, \"OS/2\");\n   if (!tab)\n      return 0;\n   if (typoAscent ) *typoAscent  = ttSHORT(info->data+tab + 68);\n   if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70);\n   if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72);\n   return 1;\n}\n\nSTBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)\n{\n   *x0 = ttSHORT(info->data + info->head + 36);\n   *y0 = ttSHORT(info->data + info->head + 38);\n   *x1 = ttSHORT(info->data + info->head + 40);\n   *y1 = ttSHORT(info->data + info->head + 42);\n}\n\nSTBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)\n{\n   int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);\n   return (float) height / fheight;\n}\n\nSTBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)\n{\n   int unitsPerEm = ttUSHORT(info->data + info->head + 18);\n   return pixels / unitsPerEm;\n}\n\nSTBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)\n{\n   STBTT_free(v, info->userdata);\n}\n\nSTBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl)\n{\n   int i;\n   stbtt_uint8 *data = info->data;\n   stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info);\n\n   int numEntries = ttUSHORT(svg_doc_list);\n   stbtt_uint8 *svg_docs = svg_doc_list + 2;\n\n   for(i=0; i<numEntries; i++) {\n      stbtt_uint8 *svg_doc = svg_docs + (12 * i);\n      if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2)))\n         return svg_doc;\n   }\n   return 0;\n}\n\nSTBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg)\n{\n   stbtt_uint8 *data = info->data;\n   stbtt_uint8 *svg_doc;\n\n   if (info->svg == 0)\n      return 0;\n\n   svg_doc = stbtt_FindSVGDoc(info, gl);\n   if (svg_doc != NULL) {\n      *svg = (char *) data + info->svg + ttULONG(svg_doc + 4);\n      return ttULONG(svg_doc + 8);\n   } else {\n      return 0;\n   }\n}\n\nSTBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg)\n{\n   return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// antialiasing software rasterizer\n//\n\nSTBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning\n   if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {\n      // e.g. space character\n      if (ix0) *ix0 = 0;\n      if (iy0) *iy0 = 0;\n      if (ix1) *ix1 = 0;\n      if (iy1) *iy1 = 0;\n   } else {\n      // move to integral bboxes (treating pixels as little squares, what pixels get touched)?\n      if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);\n      if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);\n      if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);\n      if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);\n   }\n}\n\nSTBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);\n}\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);\n}\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  Rasterizer\n\ntypedef struct stbtt__hheap_chunk\n{\n   struct stbtt__hheap_chunk *next;\n} stbtt__hheap_chunk;\n\ntypedef struct stbtt__hheap\n{\n   struct stbtt__hheap_chunk *head;\n   void   *first_free;\n   int    num_remaining_in_head_chunk;\n} stbtt__hheap;\n\nstatic void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)\n{\n   if (hh->first_free) {\n      void *p = hh->first_free;\n      hh->first_free = * (void **) p;\n      return p;\n   } else {\n      if (hh->num_remaining_in_head_chunk == 0) {\n         int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);\n         stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);\n         if (c == NULL)\n            return NULL;\n         c->next = hh->head;\n         hh->head = c;\n         hh->num_remaining_in_head_chunk = count;\n      }\n      --hh->num_remaining_in_head_chunk;\n      return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;\n   }\n}\n\nstatic void stbtt__hheap_free(stbtt__hheap *hh, void *p)\n{\n   *(void **) p = hh->first_free;\n   hh->first_free = p;\n}\n\nstatic void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)\n{\n   stbtt__hheap_chunk *c = hh->head;\n   while (c) {\n      stbtt__hheap_chunk *n = c->next;\n      STBTT_free(c, userdata);\n      c = n;\n   }\n}\n\ntypedef struct stbtt__edge {\n   float x0,y0, x1,y1;\n   int invert;\n} stbtt__edge;\n\n\ntypedef struct stbtt__active_edge\n{\n   struct stbtt__active_edge *next;\n   #if STBTT_RASTERIZER_VERSION==1\n   int x,dx;\n   float ey;\n   int direction;\n   #elif STBTT_RASTERIZER_VERSION==2\n   float fx,fdx,fdy;\n   float direction;\n   float sy;\n   float ey;\n   #else\n   #error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n   #endif\n} stbtt__active_edge;\n\n#if STBTT_RASTERIZER_VERSION == 1\n#define STBTT_FIXSHIFT   10\n#define STBTT_FIX        (1 << STBTT_FIXSHIFT)\n#define STBTT_FIXMASK    (STBTT_FIX-1)\n\nstatic stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)\n{\n   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);\n   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);\n   STBTT_assert(z != NULL);\n   if (!z) return z;\n\n   // round dx down to avoid overshooting\n   if (dxdy < 0)\n      z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);\n   else\n      z->dx = STBTT_ifloor(STBTT_FIX * dxdy);\n\n   z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount\n   z->x -= off_x * STBTT_FIX;\n\n   z->ey = e->y1;\n   z->next = 0;\n   z->direction = e->invert ? 1 : -1;\n   return z;\n}\n#elif STBTT_RASTERIZER_VERSION == 2\nstatic stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)\n{\n   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);\n   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);\n   STBTT_assert(z != NULL);\n   //STBTT_assert(e->y0 <= start_point);\n   if (!z) return z;\n   z->fdx = dxdy;\n   z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;\n   z->fx = e->x0 + dxdy * (start_point - e->y0);\n   z->fx -= off_x;\n   z->direction = e->invert ? 1.0f : -1.0f;\n   z->sy = e->y0;\n   z->ey = e->y1;\n   z->next = 0;\n   return z;\n}\n#else\n#error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n#endif\n\n#if STBTT_RASTERIZER_VERSION == 1\n// note: this routine clips fills that extend off the edges... ideally this\n// wouldn't happen, but it could happen if the truetype glyph bounding boxes\n// are wrong, or if the user supplies a too-small bitmap\nstatic void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)\n{\n   // non-zero winding fill\n   int x0=0, w=0;\n\n   while (e) {\n      if (w == 0) {\n         // if we're currently at zero, we need to record the edge start point\n         x0 = e->x; w += e->direction;\n      } else {\n         int x1 = e->x; w += e->direction;\n         // if we went to zero, we need to draw\n         if (w == 0) {\n            int i = x0 >> STBTT_FIXSHIFT;\n            int j = x1 >> STBTT_FIXSHIFT;\n\n            if (i < len && j >= 0) {\n               if (i == j) {\n                  // x0,x1 are the same pixel, so compute combined coverage\n                  scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);\n               } else {\n                  if (i >= 0) // add antialiasing for x0\n                     scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);\n                  else\n                     i = -1; // clip\n\n                  if (j < len) // add antialiasing for x1\n                     scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);\n                  else\n                     j = len; // clip\n\n                  for (++i; i < j; ++i) // fill pixels between x0 and x1\n                     scanline[i] = scanline[i] + (stbtt_uint8) max_weight;\n               }\n            }\n         }\n      }\n\n      e = e->next;\n   }\n}\n\nstatic void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)\n{\n   stbtt__hheap hh = { 0, 0, 0 };\n   stbtt__active_edge *active = NULL;\n   int y,j=0;\n   int max_weight = (255 / vsubsample);  // weight per vertical scanline\n   int s; // vertical subsample index\n   unsigned char scanline_data[512], *scanline;\n\n   if (result->w > 512)\n      scanline = (unsigned char *) STBTT_malloc(result->w, userdata);\n   else\n      scanline = scanline_data;\n\n   y = off_y * vsubsample;\n   e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;\n\n   while (j < result->h) {\n      STBTT_memset(scanline, 0, result->w);\n      for (s=0; s < vsubsample; ++s) {\n         // find center of pixel for this scanline\n         float scan_y = y + 0.5f;\n         stbtt__active_edge **step = &active;\n\n         // update all active edges;\n         // remove all active edges that terminate before the center of this scanline\n         while (*step) {\n            stbtt__active_edge * z = *step;\n            if (z->ey <= scan_y) {\n               *step = z->next; // delete from list\n               STBTT_assert(z->direction);\n               z->direction = 0;\n               stbtt__hheap_free(&hh, z);\n            } else {\n               z->x += z->dx; // advance to position for current scanline\n               step = &((*step)->next); // advance through list\n            }\n         }\n\n         // resort the list if needed\n         for(;;) {\n            int changed=0;\n            step = &active;\n            while (*step && (*step)->next) {\n               if ((*step)->x > (*step)->next->x) {\n                  stbtt__active_edge *t = *step;\n                  stbtt__active_edge *q = t->next;\n\n                  t->next = q->next;\n                  q->next = t;\n                  *step = q;\n                  changed = 1;\n               }\n               step = &(*step)->next;\n            }\n            if (!changed) break;\n         }\n\n         // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline\n         while (e->y0 <= scan_y) {\n            if (e->y1 > scan_y) {\n               stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);\n               if (z != NULL) {\n                  // find insertion point\n                  if (active == NULL)\n                     active = z;\n                  else if (z->x < active->x) {\n                     // insert at front\n                     z->next = active;\n                     active = z;\n                  } else {\n                     // find thing to insert AFTER\n                     stbtt__active_edge *p = active;\n                     while (p->next && p->next->x < z->x)\n                        p = p->next;\n                     // at this point, p->next->x is NOT < z->x\n                     z->next = p->next;\n                     p->next = z;\n                  }\n               }\n            }\n            ++e;\n         }\n\n         // now process all active edges in XOR fashion\n         if (active)\n            stbtt__fill_active_edges(scanline, result->w, active, max_weight);\n\n         ++y;\n      }\n      STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);\n      ++j;\n   }\n\n   stbtt__hheap_cleanup(&hh, userdata);\n\n   if (scanline != scanline_data)\n      STBTT_free(scanline, userdata);\n}\n\n#elif STBTT_RASTERIZER_VERSION == 2\n\n// the edge passed in here does not cross the vertical line at x or the vertical line at x+1\n// (i.e. it has already been clipped to those)\nstatic void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)\n{\n   if (y0 == y1) return;\n   STBTT_assert(y0 < y1);\n   STBTT_assert(e->sy <= e->ey);\n   if (y0 > e->ey) return;\n   if (y1 < e->sy) return;\n   if (y0 < e->sy) {\n      x0 += (x1-x0) * (e->sy - y0) / (y1-y0);\n      y0 = e->sy;\n   }\n   if (y1 > e->ey) {\n      x1 += (x1-x0) * (e->ey - y1) / (y1-y0);\n      y1 = e->ey;\n   }\n\n   if (x0 == x)\n      STBTT_assert(x1 <= x+1);\n   else if (x0 == x+1)\n      STBTT_assert(x1 >= x);\n   else if (x0 <= x)\n      STBTT_assert(x1 <= x);\n   else if (x0 >= x+1)\n      STBTT_assert(x1 >= x+1);\n   else\n      STBTT_assert(x1 >= x && x1 <= x+1);\n\n   if (x0 <= x && x1 <= x)\n      scanline[x] += e->direction * (y1-y0);\n   else if (x0 >= x+1 && x1 >= x+1)\n      ;\n   else {\n      STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);\n      scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position\n   }\n}\n\nstatic float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)\n{\n   STBTT_assert(top_width >= 0);\n   STBTT_assert(bottom_width >= 0);\n   return (top_width + bottom_width) / 2.0f * height;\n}\n\nstatic float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)\n{\n   return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);\n}\n\nstatic float stbtt__sized_triangle_area(float height, float width)\n{\n   return height * width / 2;\n}\n\nstatic void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)\n{\n   float y_bottom = y_top+1;\n\n   while (e) {\n      // brute force every pixel\n\n      // compute intersection points with top & bottom\n      STBTT_assert(e->ey >= y_top);\n\n      if (e->fdx == 0) {\n         float x0 = e->fx;\n         if (x0 < len) {\n            if (x0 >= 0) {\n               stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);\n               stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);\n            } else {\n               stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);\n            }\n         }\n      } else {\n         float x0 = e->fx;\n         float dx = e->fdx;\n         float xb = x0 + dx;\n         float x_top, x_bottom;\n         float sy0,sy1;\n         float dy = e->fdy;\n         STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);\n\n         // compute endpoints of line segment clipped to this scanline (if the\n         // line segment starts on this scanline. x0 is the intersection of the\n         // line with y_top, but that may be off the line segment.\n         if (e->sy > y_top) {\n            x_top = x0 + dx * (e->sy - y_top);\n            sy0 = e->sy;\n         } else {\n            x_top = x0;\n            sy0 = y_top;\n         }\n         if (e->ey < y_bottom) {\n            x_bottom = x0 + dx * (e->ey - y_top);\n            sy1 = e->ey;\n         } else {\n            x_bottom = xb;\n            sy1 = y_bottom;\n         }\n\n         if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {\n            // from here on, we don't have to range check x values\n\n            if ((int) x_top == (int) x_bottom) {\n               float height;\n               // simple case, only spans one pixel\n               int x = (int) x_top;\n               height = (sy1 - sy0) * e->direction;\n               STBTT_assert(x >= 0 && x < len);\n               scanline[x]      += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);\n               scanline_fill[x] += height; // everything right of this pixel is filled\n            } else {\n               int x,x1,x2;\n               float y_crossing, y_final, step, sign, area;\n               // covers 2+ pixels\n               if (x_top > x_bottom) {\n                  // flip scanline vertically; signed area is the same\n                  float t;\n                  sy0 = y_bottom - (sy0 - y_top);\n                  sy1 = y_bottom - (sy1 - y_top);\n                  t = sy0, sy0 = sy1, sy1 = t;\n                  t = x_bottom, x_bottom = x_top, x_top = t;\n                  dx = -dx;\n                  dy = -dy;\n                  t = x0, x0 = xb, xb = t;\n               }\n               STBTT_assert(dy >= 0);\n               STBTT_assert(dx >= 0);\n\n               x1 = (int) x_top;\n               x2 = (int) x_bottom;\n               // compute intersection with y axis at x1+1\n               y_crossing = y_top + dy * (x1+1 - x0);\n\n               // compute intersection with y axis at x2\n               y_final = y_top + dy * (x2 - x0);\n\n               //           x1    x_top                            x2    x_bottom\n               //     y_top  +------|-----+------------+------------+--------|---+------------+\n               //            |            |            |            |            |            |\n               //            |            |            |            |            |            |\n               //       sy0  |      Txxxxx|............|............|............|............|\n               // y_crossing |            *xxxxx.......|............|............|............|\n               //            |            |     xxxxx..|............|............|............|\n               //            |            |     /-   xx*xxxx........|............|............|\n               //            |            | dy <       |    xxxxxx..|............|............|\n               //   y_final  |            |     \\-     |          xx*xxx.........|............|\n               //       sy1  |            |            |            |   xxxxxB...|............|\n               //            |            |            |            |            |            |\n               //            |            |            |            |            |            |\n               //  y_bottom  +------------+------------+------------+------------+------------+\n               //\n               // goal is to measure the area covered by '.' in each pixel\n\n               // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057\n               // @TODO: maybe test against sy1 rather than y_bottom?\n               if (y_crossing > y_bottom)\n                  y_crossing = y_bottom;\n\n               sign = e->direction;\n\n               // area of the rectangle covered from sy0..y_crossing\n               area = sign * (y_crossing-sy0);\n\n               // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)\n               scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);\n\n               // check if final y_crossing is blown up; no test case for this\n               if (y_final > y_bottom) {\n                  y_final = y_bottom;\n                  dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom\n               }\n\n               // in second pixel, area covered by line segment found in first pixel\n               // is always a rectangle 1 wide * the height of that line segment; this\n               // is exactly what the variable 'area' stores. it also gets a contribution\n               // from the line segment within it. the THIRD pixel will get the first\n               // pixel's rectangle contribution, the second pixel's rectangle contribution,\n               // and its own contribution. the 'own contribution' is the same in every pixel except\n               // the leftmost and rightmost, a trapezoid that slides down in each pixel.\n               // the second pixel's contribution to the third pixel will be the\n               // rectangle 1 wide times the height change in the second pixel, which is dy.\n\n               step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,\n               // which multiplied by 1-pixel-width is how much pixel area changes for each step in x\n               // so the area advances by 'step' every time\n\n               for (x = x1+1; x < x2; ++x) {\n                  scanline[x] += area + step/2; // area of trapezoid is 1*step/2\n                  area += step;\n               }\n               STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down\n               STBTT_assert(sy1 > y_final-0.01f);\n\n               // area covered in the last pixel is the rectangle from all the pixels to the left,\n               // plus the trapezoid filled by the line segment in this pixel all the way to the right edge\n               scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);\n\n               // the rest of the line is filled based on the total height of the line segment in this pixel\n               scanline_fill[x2] += sign * (sy1-sy0);\n            }\n         } else {\n            // if edge goes outside of box we're drawing, we require\n            // clipping logic. since this does not match the intended use\n            // of this library, we use a different, very slow brute\n            // force implementation\n            // note though that this does happen some of the time because\n            // x_top and x_bottom can be extrapolated at the top & bottom of\n            // the shape and actually lie outside the bounding box\n            int x;\n            for (x=0; x < len; ++x) {\n               // cases:\n               //\n               // there can be up to two intersections with the pixel. any intersection\n               // with left or right edges can be handled by splitting into two (or three)\n               // regions. intersections with top & bottom do not necessitate case-wise logic.\n               //\n               // the old way of doing this found the intersections with the left & right edges,\n               // then used some simple logic to produce up to three segments in sorted order\n               // from top-to-bottom. however, this had a problem: if an x edge was epsilon\n               // across the x border, then the corresponding y position might not be distinct\n               // from the other y segment, and it might ignored as an empty segment. to avoid\n               // that, we need to explicitly produce segments based on x positions.\n\n               // rename variables to clearly-defined pairs\n               float y0 = y_top;\n               float x1 = (float) (x);\n               float x2 = (float) (x+1);\n               float x3 = xb;\n               float y3 = y_bottom;\n\n               // x = e->x + e->dx * (y-y_top)\n               // (y-y_top) = (x - e->x) / e->dx\n               // y = (x - e->x) / e->dx + y_top\n               float y1 = (x - x0) / dx + y_top;\n               float y2 = (x+1 - x0) / dx + y_top;\n\n               if (x0 < x1 && x3 > x2) {         // three segments descending down-right\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);\n               } else if (x3 < x1 && x0 > x2) {  // three segments descending down-left\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);\n               } else if (x0 < x1 && x3 > x1) {  // two segments across x, down-right\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);\n               } else if (x3 < x1 && x0 > x1) {  // two segments across x, down-left\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);\n               } else if (x0 < x2 && x3 > x2) {  // two segments across x+1, down-right\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);\n               } else if (x3 < x2 && x0 > x2) {  // two segments across x+1, down-left\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);\n               } else {  // one segment\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);\n               }\n            }\n         }\n      }\n      e = e->next;\n   }\n}\n\n// directly AA rasterize edges w/o supersampling\nstatic void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)\n{\n   stbtt__hheap hh = { 0, 0, 0 };\n   stbtt__active_edge *active = NULL;\n   int y,j=0, i;\n   float scanline_data[129], *scanline, *scanline2;\n\n   STBTT__NOTUSED(vsubsample);\n\n   if (result->w > 64)\n      scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);\n   else\n      scanline = scanline_data;\n\n   scanline2 = scanline + result->w;\n\n   y = off_y;\n   e[n].y0 = (float) (off_y + result->h) + 1;\n\n   while (j < result->h) {\n      // find center of pixel for this scanline\n      float scan_y_top    = y + 0.0f;\n      float scan_y_bottom = y + 1.0f;\n      stbtt__active_edge **step = &active;\n\n      STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));\n      STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));\n\n      // update all active edges;\n      // remove all active edges that terminate before the top of this scanline\n      while (*step) {\n         stbtt__active_edge * z = *step;\n         if (z->ey <= scan_y_top) {\n            *step = z->next; // delete from list\n            STBTT_assert(z->direction);\n            z->direction = 0;\n            stbtt__hheap_free(&hh, z);\n         } else {\n            step = &((*step)->next); // advance through list\n         }\n      }\n\n      // insert all edges that start before the bottom of this scanline\n      while (e->y0 <= scan_y_bottom) {\n         if (e->y0 != e->y1) {\n            stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);\n            if (z != NULL) {\n               if (j == 0 && off_y != 0) {\n                  if (z->ey < scan_y_top) {\n                     // this can happen due to subpixel positioning and some kind of fp rounding error i think\n                     z->ey = scan_y_top;\n                  }\n               }\n               STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds\n               // insert at front\n               z->next = active;\n               active = z;\n            }\n         }\n         ++e;\n      }\n\n      // now process all active edges\n      if (active)\n         stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);\n\n      {\n         float sum = 0;\n         for (i=0; i < result->w; ++i) {\n            float k;\n            int m;\n            sum += scanline2[i];\n            k = scanline[i] + sum;\n            k = (float) STBTT_fabs(k)*255 + 0.5f;\n            m = (int) k;\n            if (m > 255) m = 255;\n            result->pixels[j*result->stride + i] = (unsigned char) m;\n         }\n      }\n      // advance all the edges\n      step = &active;\n      while (*step) {\n         stbtt__active_edge *z = *step;\n         z->fx += z->fdx; // advance to position for current scanline\n         step = &((*step)->next); // advance through list\n      }\n\n      ++y;\n      ++j;\n   }\n\n   stbtt__hheap_cleanup(&hh, userdata);\n\n   if (scanline != scanline_data)\n      STBTT_free(scanline, userdata);\n}\n#else\n#error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n#endif\n\n#define STBTT__COMPARE(a,b)  ((a)->y0 < (b)->y0)\n\nstatic void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)\n{\n   int i,j;\n   for (i=1; i < n; ++i) {\n      stbtt__edge t = p[i], *a = &t;\n      j = i;\n      while (j > 0) {\n         stbtt__edge *b = &p[j-1];\n         int c = STBTT__COMPARE(a,b);\n         if (!c) break;\n         p[j] = p[j-1];\n         --j;\n      }\n      if (i != j)\n         p[j] = t;\n   }\n}\n\nstatic void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)\n{\n   /* threshold for transitioning to insertion sort */\n   while (n > 12) {\n      stbtt__edge t;\n      int c01,c12,c,m,i,j;\n\n      /* compute median of three */\n      m = n >> 1;\n      c01 = STBTT__COMPARE(&p[0],&p[m]);\n      c12 = STBTT__COMPARE(&p[m],&p[n-1]);\n      /* if 0 >= mid >= end, or 0 < mid < end, then use mid */\n      if (c01 != c12) {\n         /* otherwise, we'll need to swap something else to middle */\n         int z;\n         c = STBTT__COMPARE(&p[0],&p[n-1]);\n         /* 0>mid && mid<n:  0>n => n; 0<n => 0 */\n         /* 0<mid && mid>n:  0>n => 0; 0<n => n */\n         z = (c == c12) ? 0 : n-1;\n         t = p[z];\n         p[z] = p[m];\n         p[m] = t;\n      }\n      /* now p[m] is the median-of-three */\n      /* swap it to the beginning so it won't move around */\n      t = p[0];\n      p[0] = p[m];\n      p[m] = t;\n\n      /* partition loop */\n      i=1;\n      j=n-1;\n      for(;;) {\n         /* handling of equality is crucial here */\n         /* for sentinels & efficiency with duplicates */\n         for (;;++i) {\n            if (!STBTT__COMPARE(&p[i], &p[0])) break;\n         }\n         for (;;--j) {\n            if (!STBTT__COMPARE(&p[0], &p[j])) break;\n         }\n         /* make sure we haven't crossed */\n         if (i >= j) break;\n         t = p[i];\n         p[i] = p[j];\n         p[j] = t;\n\n         ++i;\n         --j;\n      }\n      /* recurse on smaller side, iterate on larger */\n      if (j < (n-i)) {\n         stbtt__sort_edges_quicksort(p,j);\n         p = p+i;\n         n = n-i;\n      } else {\n         stbtt__sort_edges_quicksort(p+i, n-i);\n         n = j;\n      }\n   }\n}\n\nstatic void stbtt__sort_edges(stbtt__edge *p, int n)\n{\n   stbtt__sort_edges_quicksort(p, n);\n   stbtt__sort_edges_ins_sort(p, n);\n}\n\ntypedef struct\n{\n   float x,y;\n} stbtt__point;\n\nstatic void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)\n{\n   float y_scale_inv = invert ? -scale_y : scale_y;\n   stbtt__edge *e;\n   int n,i,j,k,m;\n#if STBTT_RASTERIZER_VERSION == 1\n   int vsubsample = result->h < 8 ? 15 : 5;\n#elif STBTT_RASTERIZER_VERSION == 2\n   int vsubsample = 1;\n#else\n   #error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n#endif\n   // vsubsample should divide 255 evenly; otherwise we won't reach full opacity\n\n   // now we have to blow out the windings into explicit edge lists\n   n = 0;\n   for (i=0; i < windings; ++i)\n      n += wcount[i];\n\n   e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel\n   if (e == 0) return;\n   n = 0;\n\n   m=0;\n   for (i=0; i < windings; ++i) {\n      stbtt__point *p = pts + m;\n      m += wcount[i];\n      j = wcount[i]-1;\n      for (k=0; k < wcount[i]; j=k++) {\n         int a=k,b=j;\n         // skip the edge if horizontal\n         if (p[j].y == p[k].y)\n            continue;\n         // add edge from j to k to the list\n         e[n].invert = 0;\n         if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {\n            e[n].invert = 1;\n            a=j,b=k;\n         }\n         e[n].x0 = p[a].x * scale_x + shift_x;\n         e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;\n         e[n].x1 = p[b].x * scale_x + shift_x;\n         e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;\n         ++n;\n      }\n   }\n\n   // now sort the edges by their highest point (should snap to integer, and then by x)\n   //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);\n   stbtt__sort_edges(e, n);\n\n   // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule\n   stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);\n\n   STBTT_free(e, userdata);\n}\n\nstatic void stbtt__add_point(stbtt__point *points, int n, float x, float y)\n{\n   if (!points) return; // during first pass, it's unallocated\n   points[n].x = x;\n   points[n].y = y;\n}\n\n// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching\nstatic int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)\n{\n   // midpoint\n   float mx = (x0 + 2*x1 + x2)/4;\n   float my = (y0 + 2*y1 + y2)/4;\n   // versus directly drawn line\n   float dx = (x0+x2)/2 - mx;\n   float dy = (y0+y2)/2 - my;\n   if (n > 16) // 65536 segments on one curve better be enough!\n      return 1;\n   if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA\n      stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);\n      stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);\n   } else {\n      stbtt__add_point(points, *num_points,x2,y2);\n      *num_points = *num_points+1;\n   }\n   return 1;\n}\n\nstatic void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)\n{\n   // @TODO this \"flatness\" calculation is just made-up nonsense that seems to work well enough\n   float dx0 = x1-x0;\n   float dy0 = y1-y0;\n   float dx1 = x2-x1;\n   float dy1 = y2-y1;\n   float dx2 = x3-x2;\n   float dy2 = y3-y2;\n   float dx = x3-x0;\n   float dy = y3-y0;\n   float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2));\n   float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy);\n   float flatness_squared = longlen*longlen-shortlen*shortlen;\n\n   if (n > 16) // 65536 segments on one curve better be enough!\n      return;\n\n   if (flatness_squared > objspace_flatness_squared) {\n      float x01 = (x0+x1)/2;\n      float y01 = (y0+y1)/2;\n      float x12 = (x1+x2)/2;\n      float y12 = (y1+y2)/2;\n      float x23 = (x2+x3)/2;\n      float y23 = (y2+y3)/2;\n\n      float xa = (x01+x12)/2;\n      float ya = (y01+y12)/2;\n      float xb = (x12+x23)/2;\n      float yb = (y12+y23)/2;\n\n      float mx = (xa+xb)/2;\n      float my = (ya+yb)/2;\n\n      stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);\n      stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);\n   } else {\n      stbtt__add_point(points, *num_points,x3,y3);\n      *num_points = *num_points+1;\n   }\n}\n\n// returns number of contours\nstatic stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)\n{\n   stbtt__point *points=0;\n   int num_points=0;\n\n   float objspace_flatness_squared = objspace_flatness * objspace_flatness;\n   int i,n=0,start=0, pass;\n\n   // count how many \"moves\" there are to get the contour count\n   for (i=0; i < num_verts; ++i)\n      if (vertices[i].type == STBTT_vmove)\n         ++n;\n\n   *num_contours = n;\n   if (n == 0) return 0;\n\n   *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);\n\n   if (*contour_lengths == 0) {\n      *num_contours = 0;\n      return 0;\n   }\n\n   // make two passes through the points so we don't need to realloc\n   for (pass=0; pass < 2; ++pass) {\n      float x=0,y=0;\n      if (pass == 1) {\n         points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);\n         if (points == NULL) goto error;\n      }\n      num_points = 0;\n      n= -1;\n      for (i=0; i < num_verts; ++i) {\n         switch (vertices[i].type) {\n            case STBTT_vmove:\n               // start the next contour\n               if (n >= 0)\n                  (*contour_lengths)[n] = num_points - start;\n               ++n;\n               start = num_points;\n\n               x = vertices[i].x, y = vertices[i].y;\n               stbtt__add_point(points, num_points++, x,y);\n               break;\n            case STBTT_vline:\n               x = vertices[i].x, y = vertices[i].y;\n               stbtt__add_point(points, num_points++, x, y);\n               break;\n            case STBTT_vcurve:\n               stbtt__tesselate_curve(points, &num_points, x,y,\n                                        vertices[i].cx, vertices[i].cy,\n                                        vertices[i].x,  vertices[i].y,\n                                        objspace_flatness_squared, 0);\n               x = vertices[i].x, y = vertices[i].y;\n               break;\n            case STBTT_vcubic:\n               stbtt__tesselate_cubic(points, &num_points, x,y,\n                                        vertices[i].cx, vertices[i].cy,\n                                        vertices[i].cx1, vertices[i].cy1,\n                                        vertices[i].x,  vertices[i].y,\n                                        objspace_flatness_squared, 0);\n               x = vertices[i].x, y = vertices[i].y;\n               break;\n         }\n      }\n      (*contour_lengths)[n] = num_points - start;\n   }\n\n   return points;\nerror:\n   STBTT_free(points, userdata);\n   STBTT_free(*contour_lengths, userdata);\n   *contour_lengths = 0;\n   *num_contours = 0;\n   return NULL;\n}\n\nSTBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)\n{\n   float scale            = scale_x > scale_y ? scale_y : scale_x;\n   int winding_count      = 0;\n   int *winding_lengths   = NULL;\n   stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);\n   if (windings) {\n      stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);\n      STBTT_free(winding_lengths, userdata);\n      STBTT_free(windings, userdata);\n   }\n}\n\nSTBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)\n{\n   STBTT_free(bitmap, userdata);\n}\n\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)\n{\n   int ix0,iy0,ix1,iy1;\n   stbtt__bitmap gbm;\n   stbtt_vertex *vertices;\n   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);\n\n   if (scale_x == 0) scale_x = scale_y;\n   if (scale_y == 0) {\n      if (scale_x == 0) {\n         STBTT_free(vertices, info->userdata);\n         return NULL;\n      }\n      scale_y = scale_x;\n   }\n\n   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);\n\n   // now we get the size\n   gbm.w = (ix1 - ix0);\n   gbm.h = (iy1 - iy0);\n   gbm.pixels = NULL; // in case we error\n\n   if (width ) *width  = gbm.w;\n   if (height) *height = gbm.h;\n   if (xoff  ) *xoff   = ix0;\n   if (yoff  ) *yoff   = iy0;\n\n   if (gbm.w && gbm.h) {\n      gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);\n      if (gbm.pixels) {\n         gbm.stride = gbm.w;\n\n         stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);\n      }\n   }\n   STBTT_free(vertices, info->userdata);\n   return gbm.pixels;\n}\n\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);\n}\n\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)\n{\n   int ix0,iy0;\n   stbtt_vertex *vertices;\n   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);\n   stbtt__bitmap gbm;\n\n   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);\n   gbm.pixels = output;\n   gbm.w = out_w;\n   gbm.h = out_h;\n   gbm.stride = out_stride;\n\n   if (gbm.w && gbm.h)\n      stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);\n\n   STBTT_free(vertices, info->userdata);\n}\n\nSTBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)\n{\n   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);\n}\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);\n}\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint)\n{\n   stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint));\n}\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)\n{\n   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));\n}\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);\n}\n\nSTBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)\n{\n   stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// bitmap baking\n//\n// This is SUPER-CRAPPY packing to keep source code small\n\nstatic int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)\n                                float pixel_height,                     // height of font in pixels\n                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in\n                                int first_char, int num_chars,          // characters to bake\n                                stbtt_bakedchar *chardata)\n{\n   float scale;\n   int x,y,bottom_y, i;\n   stbtt_fontinfo f;\n   f.userdata = NULL;\n   if (!stbtt_InitFont(&f, data, offset))\n      return -1;\n   STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels\n   x=y=1;\n   bottom_y = 1;\n\n   scale = stbtt_ScaleForPixelHeight(&f, pixel_height);\n\n   for (i=0; i < num_chars; ++i) {\n      int advance, lsb, x0,y0,x1,y1,gw,gh;\n      int g = stbtt_FindGlyphIndex(&f, first_char + i);\n      stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);\n      stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);\n      gw = x1-x0;\n      gh = y1-y0;\n      if (x + gw + 1 >= pw)\n         y = bottom_y, x = 1; // advance to next row\n      if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row\n         return -i;\n      STBTT_assert(x+gw < pw);\n      STBTT_assert(y+gh < ph);\n      stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);\n      chardata[i].x0 = (stbtt_int16) x;\n      chardata[i].y0 = (stbtt_int16) y;\n      chardata[i].x1 = (stbtt_int16) (x + gw);\n      chardata[i].y1 = (stbtt_int16) (y + gh);\n      chardata[i].xadvance = scale * advance;\n      chardata[i].xoff     = (float) x0;\n      chardata[i].yoff     = (float) y0;\n      x = x + gw + 1;\n      if (y+gh+1 > bottom_y)\n         bottom_y = y+gh+1;\n   }\n   return bottom_y;\n}\n\nSTBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)\n{\n   float d3d_bias = opengl_fillrule ? 0 : -0.5f;\n   float ipw = 1.0f / pw, iph = 1.0f / ph;\n   const stbtt_bakedchar *b = chardata + char_index;\n   int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);\n   int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);\n\n   q->x0 = round_x + d3d_bias;\n   q->y0 = round_y + d3d_bias;\n   q->x1 = round_x + b->x1 - b->x0 + d3d_bias;\n   q->y1 = round_y + b->y1 - b->y0 + d3d_bias;\n\n   q->s0 = b->x0 * ipw;\n   q->t0 = b->y0 * iph;\n   q->s1 = b->x1 * ipw;\n   q->t1 = b->y1 * iph;\n\n   *xpos += b->xadvance;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// rectangle packing replacement routines if you don't have stb_rect_pack.h\n//\n\n#ifndef STB_RECT_PACK_VERSION\n\ntypedef int stbrp_coord;\n\n////////////////////////////////////////////////////////////////////////////////////\n//                                                                                //\n//                                                                                //\n// COMPILER WARNING ?!?!?                                                         //\n//                                                                                //\n//                                                                                //\n// if you get a compile warning due to these symbols being defined more than      //\n// once, move #include \"stb_rect_pack.h\" before #include \"stb_truetype.h\"         //\n//                                                                                //\n////////////////////////////////////////////////////////////////////////////////////\n\ntypedef struct\n{\n   int width,height;\n   int x,y,bottom_y;\n} stbrp_context;\n\ntypedef struct\n{\n   unsigned char x;\n} stbrp_node;\n\nstruct stbrp_rect\n{\n   stbrp_coord x,y;\n   int id,w,h,was_packed;\n};\n\nstatic void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)\n{\n   con->width  = pw;\n   con->height = ph;\n   con->x = 0;\n   con->y = 0;\n   con->bottom_y = 0;\n   STBTT__NOTUSED(nodes);\n   STBTT__NOTUSED(num_nodes);\n}\n\nstatic void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)\n{\n   int i;\n   for (i=0; i < num_rects; ++i) {\n      if (con->x + rects[i].w > con->width) {\n         con->x = 0;\n         con->y = con->bottom_y;\n      }\n      if (con->y + rects[i].h > con->height)\n         break;\n      rects[i].x = con->x;\n      rects[i].y = con->y;\n      rects[i].was_packed = 1;\n      con->x += rects[i].w;\n      if (con->y + rects[i].h > con->bottom_y)\n         con->bottom_y = con->y + rects[i].h;\n   }\n   for (   ; i < num_rects; ++i)\n      rects[i].was_packed = 0;\n}\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// bitmap baking\n//\n// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If\n// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.\n\nSTBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)\n{\n   stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context)            ,alloc_context);\n   int            num_nodes = pw - padding;\n   stbrp_node    *nodes   = (stbrp_node    *) STBTT_malloc(sizeof(*nodes  ) * num_nodes,alloc_context);\n\n   if (context == NULL || nodes == NULL) {\n      if (context != NULL) STBTT_free(context, alloc_context);\n      if (nodes   != NULL) STBTT_free(nodes  , alloc_context);\n      return 0;\n   }\n\n   spc->user_allocator_context = alloc_context;\n   spc->width = pw;\n   spc->height = ph;\n   spc->pixels = pixels;\n   spc->pack_info = context;\n   spc->nodes = nodes;\n   spc->padding = padding;\n   spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;\n   spc->h_oversample = 1;\n   spc->v_oversample = 1;\n   spc->skip_missing = 0;\n\n   stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);\n\n   if (pixels)\n      STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels\n\n   return 1;\n}\n\nSTBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc)\n{\n   STBTT_free(spc->nodes    , spc->user_allocator_context);\n   STBTT_free(spc->pack_info, spc->user_allocator_context);\n}\n\nSTBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)\n{\n   STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);\n   STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);\n   if (h_oversample <= STBTT_MAX_OVERSAMPLE)\n      spc->h_oversample = h_oversample;\n   if (v_oversample <= STBTT_MAX_OVERSAMPLE)\n      spc->v_oversample = v_oversample;\n}\n\nSTBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip)\n{\n   spc->skip_missing = skip;\n}\n\n#define STBTT__OVER_MASK  (STBTT_MAX_OVERSAMPLE-1)\n\nstatic void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)\n{\n   unsigned char buffer[STBTT_MAX_OVERSAMPLE];\n   int safe_w = w - kernel_width;\n   int j;\n   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze\n   for (j=0; j < h; ++j) {\n      int i;\n      unsigned int total;\n      STBTT_memset(buffer, 0, kernel_width);\n\n      total = 0;\n\n      // make kernel_width a constant in common cases so compiler can optimize out the divide\n      switch (kernel_width) {\n         case 2:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 2);\n            }\n            break;\n         case 3:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 3);\n            }\n            break;\n         case 4:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 4);\n            }\n            break;\n         case 5:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 5);\n            }\n            break;\n         default:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / kernel_width);\n            }\n            break;\n      }\n\n      for (; i < w; ++i) {\n         STBTT_assert(pixels[i] == 0);\n         total -= buffer[i & STBTT__OVER_MASK];\n         pixels[i] = (unsigned char) (total / kernel_width);\n      }\n\n      pixels += stride_in_bytes;\n   }\n}\n\nstatic void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)\n{\n   unsigned char buffer[STBTT_MAX_OVERSAMPLE];\n   int safe_h = h - kernel_width;\n   int j;\n   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze\n   for (j=0; j < w; ++j) {\n      int i;\n      unsigned int total;\n      STBTT_memset(buffer, 0, kernel_width);\n\n      total = 0;\n\n      // make kernel_width a constant in common cases so compiler can optimize out the divide\n      switch (kernel_width) {\n         case 2:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 2);\n            }\n            break;\n         case 3:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 3);\n            }\n            break;\n         case 4:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 4);\n            }\n            break;\n         case 5:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 5);\n            }\n            break;\n         default:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);\n            }\n            break;\n      }\n\n      for (; i < h; ++i) {\n         STBTT_assert(pixels[i*stride_in_bytes] == 0);\n         total -= buffer[i & STBTT__OVER_MASK];\n         pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);\n      }\n\n      pixels += 1;\n   }\n}\n\nstatic float stbtt__oversample_shift(int oversample)\n{\n   if (!oversample)\n      return 0.0f;\n\n   // The prefilter is a box filter of width \"oversample\",\n   // which shifts phase by (oversample - 1)/2 pixels in\n   // oversampled space. We want to shift in the opposite\n   // direction to counter this.\n   return (float)-(oversample - 1) / (2.0f * (float)oversample);\n}\n\n// rects array must be big enough to accommodate all characters in the given ranges\nSTBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)\n{\n   int i,j,k;\n   int missing_glyph_added = 0;\n\n   k=0;\n   for (i=0; i < num_ranges; ++i) {\n      float fh = ranges[i].font_size;\n      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);\n      ranges[i].h_oversample = (unsigned char) spc->h_oversample;\n      ranges[i].v_oversample = (unsigned char) spc->v_oversample;\n      for (j=0; j < ranges[i].num_chars; ++j) {\n         int x0,y0,x1,y1;\n         int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];\n         int glyph = stbtt_FindGlyphIndex(info, codepoint);\n         if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) {\n            rects[k].w = rects[k].h = 0;\n         } else {\n            stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,\n                                            scale * spc->h_oversample,\n                                            scale * spc->v_oversample,\n                                            0,0,\n                                            &x0,&y0,&x1,&y1);\n            rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);\n            rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);\n            if (glyph == 0)\n               missing_glyph_added = 1;\n         }\n         ++k;\n      }\n   }\n\n   return k;\n}\n\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph)\n{\n   stbtt_MakeGlyphBitmapSubpixel(info,\n                                 output,\n                                 out_w - (prefilter_x - 1),\n                                 out_h - (prefilter_y - 1),\n                                 out_stride,\n                                 scale_x,\n                                 scale_y,\n                                 shift_x,\n                                 shift_y,\n                                 glyph);\n\n   if (prefilter_x > 1)\n      stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x);\n\n   if (prefilter_y > 1)\n      stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y);\n\n   *sub_x = stbtt__oversample_shift(prefilter_x);\n   *sub_y = stbtt__oversample_shift(prefilter_y);\n}\n\n// rects array must be big enough to accommodate all characters in the given ranges\nSTBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)\n{\n   int i,j,k, missing_glyph = -1, return_value = 1;\n\n   // save current values\n   int old_h_over = spc->h_oversample;\n   int old_v_over = spc->v_oversample;\n\n   k = 0;\n   for (i=0; i < num_ranges; ++i) {\n      float fh = ranges[i].font_size;\n      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);\n      float recip_h,recip_v,sub_x,sub_y;\n      spc->h_oversample = ranges[i].h_oversample;\n      spc->v_oversample = ranges[i].v_oversample;\n      recip_h = 1.0f / spc->h_oversample;\n      recip_v = 1.0f / spc->v_oversample;\n      sub_x = stbtt__oversample_shift(spc->h_oversample);\n      sub_y = stbtt__oversample_shift(spc->v_oversample);\n      for (j=0; j < ranges[i].num_chars; ++j) {\n         stbrp_rect *r = &rects[k];\n         if (r->was_packed && r->w != 0 && r->h != 0) {\n            stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];\n            int advance, lsb, x0,y0,x1,y1;\n            int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];\n            int glyph = stbtt_FindGlyphIndex(info, codepoint);\n            stbrp_coord pad = (stbrp_coord) spc->padding;\n\n            // pad on left and top\n            r->x += pad;\n            r->y += pad;\n            r->w -= pad;\n            r->h -= pad;\n            stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);\n            stbtt_GetGlyphBitmapBox(info, glyph,\n                                    scale * spc->h_oversample,\n                                    scale * spc->v_oversample,\n                                    &x0,&y0,&x1,&y1);\n            stbtt_MakeGlyphBitmapSubpixel(info,\n                                          spc->pixels + r->x + r->y*spc->stride_in_bytes,\n                                          r->w - spc->h_oversample+1,\n                                          r->h - spc->v_oversample+1,\n                                          spc->stride_in_bytes,\n                                          scale * spc->h_oversample,\n                                          scale * spc->v_oversample,\n                                          0,0,\n                                          glyph);\n\n            if (spc->h_oversample > 1)\n               stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,\n                                  r->w, r->h, spc->stride_in_bytes,\n                                  spc->h_oversample);\n\n            if (spc->v_oversample > 1)\n               stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,\n                                  r->w, r->h, spc->stride_in_bytes,\n                                  spc->v_oversample);\n\n            bc->x0       = (stbtt_int16)  r->x;\n            bc->y0       = (stbtt_int16)  r->y;\n            bc->x1       = (stbtt_int16) (r->x + r->w);\n            bc->y1       = (stbtt_int16) (r->y + r->h);\n            bc->xadvance =                scale * advance;\n            bc->xoff     =       (float)  x0 * recip_h + sub_x;\n            bc->yoff     =       (float)  y0 * recip_v + sub_y;\n            bc->xoff2    =                (x0 + r->w) * recip_h + sub_x;\n            bc->yoff2    =                (y0 + r->h) * recip_v + sub_y;\n\n            if (glyph == 0)\n               missing_glyph = j;\n         } else if (spc->skip_missing) {\n            return_value = 0;\n         } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) {\n            ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph];\n         } else {\n            return_value = 0; // if any fail, report failure\n         }\n\n         ++k;\n      }\n   }\n\n   // restore original values\n   spc->h_oversample = old_h_over;\n   spc->v_oversample = old_v_over;\n\n   return return_value;\n}\n\nSTBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)\n{\n   stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);\n}\n\nSTBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)\n{\n   stbtt_fontinfo info;\n   int i,j,n, return_value = 1;\n   //stbrp_context *context = (stbrp_context *) spc->pack_info;\n   stbrp_rect    *rects;\n\n   // flag all characters as NOT packed\n   for (i=0; i < num_ranges; ++i)\n      for (j=0; j < ranges[i].num_chars; ++j)\n         ranges[i].chardata_for_range[j].x0 =\n         ranges[i].chardata_for_range[j].y0 =\n         ranges[i].chardata_for_range[j].x1 =\n         ranges[i].chardata_for_range[j].y1 = 0;\n\n   n = 0;\n   for (i=0; i < num_ranges; ++i)\n      n += ranges[i].num_chars;\n\n   rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);\n   if (rects == NULL)\n      return 0;\n\n   info.userdata = spc->user_allocator_context;\n   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));\n\n   n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);\n\n   stbtt_PackFontRangesPackRects(spc, rects, n);\n\n   return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);\n\n   STBTT_free(rects, spc->user_allocator_context);\n   return return_value;\n}\n\nSTBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,\n            int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)\n{\n   stbtt_pack_range range;\n   range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;\n   range.array_of_unicode_codepoints = NULL;\n   range.num_chars                   = num_chars_in_range;\n   range.chardata_for_range          = chardata_for_range;\n   range.font_size                   = font_size;\n   return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);\n}\n\nSTBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap)\n{\n   int i_ascent, i_descent, i_lineGap;\n   float scale;\n   stbtt_fontinfo info;\n   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index));\n   scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size);\n   stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap);\n   *ascent  = (float) i_ascent  * scale;\n   *descent = (float) i_descent * scale;\n   *lineGap = (float) i_lineGap * scale;\n}\n\nSTBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)\n{\n   float ipw = 1.0f / pw, iph = 1.0f / ph;\n   const stbtt_packedchar *b = chardata + char_index;\n\n   if (align_to_integer) {\n      float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);\n      float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);\n      q->x0 = x;\n      q->y0 = y;\n      q->x1 = x + b->xoff2 - b->xoff;\n      q->y1 = y + b->yoff2 - b->yoff;\n   } else {\n      q->x0 = *xpos + b->xoff;\n      q->y0 = *ypos + b->yoff;\n      q->x1 = *xpos + b->xoff2;\n      q->y1 = *ypos + b->yoff2;\n   }\n\n   q->s0 = b->x0 * ipw;\n   q->t0 = b->y0 * iph;\n   q->s1 = b->x1 * ipw;\n   q->t1 = b->y1 * iph;\n\n   *xpos += b->xadvance;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// sdf computation\n//\n\n#define STBTT_min(a,b)  ((a) < (b) ? (a) : (b))\n#define STBTT_max(a,b)  ((a) < (b) ? (b) : (a))\n\nstatic int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2])\n{\n   float q0perp = q0[1]*ray[0] - q0[0]*ray[1];\n   float q1perp = q1[1]*ray[0] - q1[0]*ray[1];\n   float q2perp = q2[1]*ray[0] - q2[0]*ray[1];\n   float roperp = orig[1]*ray[0] - orig[0]*ray[1];\n\n   float a = q0perp - 2*q1perp + q2perp;\n   float b = q1perp - q0perp;\n   float c = q0perp - roperp;\n\n   float s0 = 0., s1 = 0.;\n   int num_s = 0;\n\n   if (a != 0.0) {\n      float discr = b*b - a*c;\n      if (discr > 0.0) {\n         float rcpna = -1 / a;\n         float d = (float) STBTT_sqrt(discr);\n         s0 = (b+d) * rcpna;\n         s1 = (b-d) * rcpna;\n         if (s0 >= 0.0 && s0 <= 1.0)\n            num_s = 1;\n         if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) {\n            if (num_s == 0) s0 = s1;\n            ++num_s;\n         }\n      }\n   } else {\n      // 2*b*s + c = 0\n      // s = -c / (2*b)\n      s0 = c / (-2 * b);\n      if (s0 >= 0.0 && s0 <= 1.0)\n         num_s = 1;\n   }\n\n   if (num_s == 0)\n      return 0;\n   else {\n      float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]);\n      float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2;\n\n      float q0d =   q0[0]*rayn_x +   q0[1]*rayn_y;\n      float q1d =   q1[0]*rayn_x +   q1[1]*rayn_y;\n      float q2d =   q2[0]*rayn_x +   q2[1]*rayn_y;\n      float rod = orig[0]*rayn_x + orig[1]*rayn_y;\n\n      float q10d = q1d - q0d;\n      float q20d = q2d - q0d;\n      float q0rd = q0d - rod;\n\n      hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d;\n      hits[0][1] = a*s0+b;\n\n      if (num_s > 1) {\n         hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d;\n         hits[1][1] = a*s1+b;\n         return 2;\n      } else {\n         return 1;\n      }\n   }\n}\n\nstatic int equal(float *a, float *b)\n{\n   return (a[0] == b[0] && a[1] == b[1]);\n}\n\nstatic int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)\n{\n   int i;\n   float orig[2], ray[2] = { 1, 0 };\n   float y_frac;\n   int winding = 0;\n\n   // make sure y never passes through a vertex of the shape\n   y_frac = (float) STBTT_fmod(y, 1.0f);\n   if (y_frac < 0.01f)\n      y += 0.01f;\n   else if (y_frac > 0.99f)\n      y -= 0.01f;\n\n   orig[0] = x;\n   orig[1] = y;\n\n   // test a ray from (-infinity,y) to (x,y)\n   for (i=0; i < nverts; ++i) {\n      if (verts[i].type == STBTT_vline) {\n         int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y;\n         int x1 = (int) verts[i  ].x, y1 = (int) verts[i  ].y;\n         if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {\n            float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;\n            if (x_inter < x)\n               winding += (y0 < y1) ? 1 : -1;\n         }\n      }\n      if (verts[i].type == STBTT_vcurve) {\n         int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ;\n         int x1 = (int) verts[i  ].cx, y1 = (int) verts[i  ].cy;\n         int x2 = (int) verts[i  ].x , y2 = (int) verts[i  ].y ;\n         int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2));\n         int by = STBTT_max(y0,STBTT_max(y1,y2));\n         if (y > ay && y < by && x > ax) {\n            float q0[2],q1[2],q2[2];\n            float hits[2][2];\n            q0[0] = (float)x0;\n            q0[1] = (float)y0;\n            q1[0] = (float)x1;\n            q1[1] = (float)y1;\n            q2[0] = (float)x2;\n            q2[1] = (float)y2;\n            if (equal(q0,q1) || equal(q1,q2)) {\n               x0 = (int)verts[i-1].x;\n               y0 = (int)verts[i-1].y;\n               x1 = (int)verts[i  ].x;\n               y1 = (int)verts[i  ].y;\n               if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {\n                  float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;\n                  if (x_inter < x)\n                     winding += (y0 < y1) ? 1 : -1;\n               }\n            } else {\n               int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits);\n               if (num_hits >= 1)\n                  if (hits[0][0] < 0)\n                     winding += (hits[0][1] < 0 ? -1 : 1);\n               if (num_hits >= 2)\n                  if (hits[1][0] < 0)\n                     winding += (hits[1][1] < 0 ? -1 : 1);\n            }\n         }\n      }\n   }\n   return winding;\n}\n\nstatic float stbtt__cuberoot( float x )\n{\n   if (x<0)\n      return -(float) STBTT_pow(-x,1.0f/3.0f);\n   else\n      return  (float) STBTT_pow( x,1.0f/3.0f);\n}\n\n// x^3 + a*x^2 + b*x + c = 0\nstatic int stbtt__solve_cubic(float a, float b, float c, float* r)\n{\n   float s = -a / 3;\n   float p = b - a*a / 3;\n   float q = a * (2*a*a - 9*b) / 27 + c;\n   float p3 = p*p*p;\n   float d = q*q + 4*p3 / 27;\n   if (d >= 0) {\n      float z = (float) STBTT_sqrt(d);\n      float u = (-q + z) / 2;\n      float v = (-q - z) / 2;\n      u = stbtt__cuberoot(u);\n      v = stbtt__cuberoot(v);\n      r[0] = s + u + v;\n      return 1;\n   } else {\n      float u = (float) STBTT_sqrt(-p/3);\n      float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative\n      float m = (float) STBTT_cos(v);\n      float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;\n      r[0] = s + u * 2 * m;\n      r[1] = s - u * (m + n);\n      r[2] = s - u * (m - n);\n\n      //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f);  // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?\n      //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);\n      //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);\n      return 3;\n   }\n}\n\nSTBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)\n{\n   float scale_x = scale, scale_y = scale;\n   int ix0,iy0,ix1,iy1;\n   int w,h;\n   unsigned char *data;\n\n   if (scale == 0) return NULL;\n\n   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);\n\n   // if empty, return NULL\n   if (ix0 == ix1 || iy0 == iy1)\n      return NULL;\n\n   ix0 -= padding;\n   iy0 -= padding;\n   ix1 += padding;\n   iy1 += padding;\n\n   w = (ix1 - ix0);\n   h = (iy1 - iy0);\n\n   if (width ) *width  = w;\n   if (height) *height = h;\n   if (xoff  ) *xoff   = ix0;\n   if (yoff  ) *yoff   = iy0;\n\n   // invert for y-downwards bitmaps\n   scale_y = -scale_y;\n\n   {\n      // distance from singular values (in the same units as the pixel grid)\n      const float eps = 1./1024, eps2 = eps*eps;\n      int x,y,i,j;\n      float *precompute;\n      stbtt_vertex *verts;\n      int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);\n      data = (unsigned char *) STBTT_malloc(w * h, info->userdata);\n      precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata);\n\n      for (i=0,j=num_verts-1; i < num_verts; j=i++) {\n         if (verts[i].type == STBTT_vline) {\n            float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;\n            float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;\n            float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));\n            precompute[i] = (dist < eps) ? 0.0f : 1.0f / dist;\n         } else if (verts[i].type == STBTT_vcurve) {\n            float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;\n            float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;\n            float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;\n            float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;\n            float len2 = bx*bx + by*by;\n            if (len2 >= eps2)\n               precompute[i] = 1.0f / len2;\n            else\n               precompute[i] = 0.0f;\n         } else\n            precompute[i] = 0.0f;\n      }\n\n      for (y=iy0; y < iy1; ++y) {\n         for (x=ix0; x < ix1; ++x) {\n            float val;\n            float min_dist = 999999.0f;\n            float sx = (float) x + 0.5f;\n            float sy = (float) y + 0.5f;\n            float x_gspace = (sx / scale_x);\n            float y_gspace = (sy / scale_y);\n\n            int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path\n\n            for (i=0; i < num_verts; ++i) {\n               float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;\n\n               if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) {\n                  float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;\n\n                  float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);\n                  if (dist2 < min_dist*min_dist)\n                     min_dist = (float) STBTT_sqrt(dist2);\n\n                  // coarse culling against bbox\n                  //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&\n                  //    sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)\n                  dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];\n                  STBTT_assert(i != 0);\n                  if (dist < min_dist) {\n                     // check position along line\n                     // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0)\n                     // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy)\n                     float dx = x1-x0, dy = y1-y0;\n                     float px = x0-sx, py = y0-sy;\n                     // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy\n                     // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve\n                     float t = -(px*dx + py*dy) / (dx*dx + dy*dy);\n                     if (t >= 0.0f && t <= 1.0f)\n                        min_dist = dist;\n                  }\n               } else if (verts[i].type == STBTT_vcurve) {\n                  float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y;\n                  float x1 = verts[i  ].cx*scale_x, y1 = verts[i  ].cy*scale_y;\n                  float box_x0 = STBTT_min(STBTT_min(x0,x1),x2);\n                  float box_y0 = STBTT_min(STBTT_min(y0,y1),y2);\n                  float box_x1 = STBTT_max(STBTT_max(x0,x1),x2);\n                  float box_y1 = STBTT_max(STBTT_max(y0,y1),y2);\n                  // coarse culling against bbox to avoid computing cubic unnecessarily\n                  if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) {\n                     int num=0;\n                     float ax = x1-x0, ay = y1-y0;\n                     float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;\n                     float mx = x0 - sx, my = y0 - sy;\n                     float res[3] = {0.f,0.f,0.f};\n                     float px,py,t,it,dist2;\n                     float a_inv = precompute[i];\n                     if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula\n                        float a = 3*(ax*bx + ay*by);\n                        float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);\n                        float c = mx*ax+my*ay;\n                        if (STBTT_fabs(a) < eps2) { // if a is 0, it's linear\n                           if (STBTT_fabs(b) >= eps2) {\n                              res[num++] = -c/b;\n                           }\n                        } else {\n                           float discriminant = b*b - 4*a*c;\n                           if (discriminant < 0)\n                              num = 0;\n                           else {\n                              float root = (float) STBTT_sqrt(discriminant);\n                              res[0] = (-b - root)/(2*a);\n                              res[1] = (-b + root)/(2*a);\n                              num = 2; // don't bother distinguishing 1-solution case, as code below will still work\n                           }\n                        }\n                     } else {\n                        float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point\n                        float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv;\n                        float d = (mx*ax+my*ay) * a_inv;\n                        num = stbtt__solve_cubic(b, c, d, res);\n                     }\n                     dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);\n                     if (dist2 < min_dist*min_dist)\n                        min_dist = (float) STBTT_sqrt(dist2);\n\n                     if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {\n                        t = res[0], it = 1.0f - t;\n                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;\n                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;\n                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);\n                        if (dist2 < min_dist * min_dist)\n                           min_dist = (float) STBTT_sqrt(dist2);\n                     }\n                     if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) {\n                        t = res[1], it = 1.0f - t;\n                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;\n                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;\n                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);\n                        if (dist2 < min_dist * min_dist)\n                           min_dist = (float) STBTT_sqrt(dist2);\n                     }\n                     if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) {\n                        t = res[2], it = 1.0f - t;\n                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;\n                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;\n                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);\n                        if (dist2 < min_dist * min_dist)\n                           min_dist = (float) STBTT_sqrt(dist2);\n                     }\n                  }\n               }\n            }\n            if (winding == 0)\n               min_dist = -min_dist;  // if outside the shape, value is negative\n            val = onedge_value + pixel_dist_scale * min_dist;\n            if (val < 0)\n               val = 0;\n            else if (val > 255)\n               val = 255;\n            data[(y-iy0)*w+(x-ix0)] = (unsigned char) val;\n         }\n      }\n      STBTT_free(precompute, info->userdata);\n      STBTT_free(verts, info->userdata);\n   }\n   return data;\n}\n\nSTBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff);\n}\n\nSTBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)\n{\n   STBTT_free(bitmap, userdata);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// font name matching -- recommended not to use this\n//\n\n// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string\nstatic stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)\n{\n   stbtt_int32 i=0;\n\n   // convert utf16 to utf8 and compare the results while converting\n   while (len2) {\n      stbtt_uint16 ch = s2[0]*256 + s2[1];\n      if (ch < 0x80) {\n         if (i >= len1) return -1;\n         if (s1[i++] != ch) return -1;\n      } else if (ch < 0x800) {\n         if (i+1 >= len1) return -1;\n         if (s1[i++] != 0xc0 + (ch >> 6)) return -1;\n         if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;\n      } else if (ch >= 0xd800 && ch < 0xdc00) {\n         stbtt_uint32 c;\n         stbtt_uint16 ch2 = s2[2]*256 + s2[3];\n         if (i+3 >= len1) return -1;\n         c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;\n         if (s1[i++] != 0xf0 + (c >> 18)) return -1;\n         if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;\n         if (s1[i++] != 0x80 + ((c >>  6) & 0x3f)) return -1;\n         if (s1[i++] != 0x80 + ((c      ) & 0x3f)) return -1;\n         s2 += 2; // plus another 2 below\n         len2 -= 2;\n      } else if (ch >= 0xdc00 && ch < 0xe000) {\n         return -1;\n      } else {\n         if (i+2 >= len1) return -1;\n         if (s1[i++] != 0xe0 + (ch >> 12)) return -1;\n         if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;\n         if (s1[i++] != 0x80 + ((ch     ) & 0x3f)) return -1;\n      }\n      s2 += 2;\n      len2 -= 2;\n   }\n   return i;\n}\n\nstatic int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)\n{\n   return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);\n}\n\n// returns results in whatever encoding you request... but note that 2-byte encodings\n// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare\nSTBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)\n{\n   stbtt_int32 i,count,stringOffset;\n   stbtt_uint8 *fc = font->data;\n   stbtt_uint32 offset = font->fontstart;\n   stbtt_uint32 nm = stbtt__find_table(fc, offset, \"name\");\n   if (!nm) return NULL;\n\n   count = ttUSHORT(fc+nm+2);\n   stringOffset = nm + ttUSHORT(fc+nm+4);\n   for (i=0; i < count; ++i) {\n      stbtt_uint32 loc = nm + 6 + 12 * i;\n      if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)\n          && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {\n         *length = ttUSHORT(fc+loc+8);\n         return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));\n      }\n   }\n   return NULL;\n}\n\nstatic int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)\n{\n   stbtt_int32 i;\n   stbtt_int32 count = ttUSHORT(fc+nm+2);\n   stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);\n\n   for (i=0; i < count; ++i) {\n      stbtt_uint32 loc = nm + 6 + 12 * i;\n      stbtt_int32 id = ttUSHORT(fc+loc+6);\n      if (id == target_id) {\n         // find the encoding\n         stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);\n\n         // is this a Unicode encoding?\n         if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {\n            stbtt_int32 slen = ttUSHORT(fc+loc+8);\n            stbtt_int32 off = ttUSHORT(fc+loc+10);\n\n            // check if there's a prefix match\n            stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);\n            if (matchlen >= 0) {\n               // check for target_id+1 immediately following, with same encoding & language\n               if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {\n                  slen = ttUSHORT(fc+loc+12+8);\n                  off = ttUSHORT(fc+loc+12+10);\n                  if (slen == 0) {\n                     if (matchlen == nlen)\n                        return 1;\n                  } else if (matchlen < nlen && name[matchlen] == ' ') {\n                     ++matchlen;\n                     if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))\n                        return 1;\n                  }\n               } else {\n                  // if nothing immediately following\n                  if (matchlen == nlen)\n                     return 1;\n               }\n            }\n         }\n\n         // @TODO handle other encodings\n      }\n   }\n   return 0;\n}\n\nstatic int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)\n{\n   stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);\n   stbtt_uint32 nm,hd;\n   if (!stbtt__isfont(fc+offset)) return 0;\n\n   // check italics/bold/underline flags in macStyle...\n   if (flags) {\n      hd = stbtt__find_table(fc, offset, \"head\");\n      if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;\n   }\n\n   nm = stbtt__find_table(fc, offset, \"name\");\n   if (!nm) return 0;\n\n   if (flags) {\n      // if we checked the macStyle flags, then just check the family and ignore the subfamily\n      if (stbtt__matchpair(fc, nm, name, nlen, 16, -1))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  1, -1))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;\n   } else {\n      if (stbtt__matchpair(fc, nm, name, nlen, 16, 17))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  1,  2))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;\n   }\n\n   return 0;\n}\n\nstatic int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags)\n{\n   stbtt_int32 i;\n   for (i=0;;++i) {\n      stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);\n      if (off < 0) return off;\n      if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))\n         return off;\n   }\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nSTBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,\n                                float pixel_height, unsigned char *pixels, int pw, int ph,\n                                int first_char, int num_chars, stbtt_bakedchar *chardata)\n{\n   return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata);\n}\n\nSTBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)\n{\n   return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);\n}\n\nSTBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)\n{\n   return stbtt_GetNumberOfFonts_internal((unsigned char *) data);\n}\n\nSTBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset)\n{\n   return stbtt_InitFont_internal(info, (unsigned char *) data, offset);\n}\n\nSTBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags)\n{\n   return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags);\n}\n\nSTBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)\n{\n   return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2);\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\n#endif // STB_TRUETYPE_IMPLEMENTATION\n\n\n// FULL VERSION HISTORY\n//\n//   1.25 (2021-07-11) many fixes\n//   1.24 (2020-02-05) fix warning\n//   1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)\n//   1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined\n//   1.21 (2019-02-25) fix warning\n//   1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()\n//   1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod\n//   1.18 (2018-01-29) add missing function\n//   1.17 (2017-07-23) make more arguments const; doc fix\n//   1.16 (2017-07-12) SDF support\n//   1.15 (2017-03-03) make more arguments const\n//   1.14 (2017-01-16) num-fonts-in-TTC function\n//   1.13 (2017-01-02) support OpenType fonts, certain Apple fonts\n//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual\n//   1.11 (2016-04-02) fix unused-variable warning\n//   1.10 (2016-04-02) allow user-defined fabs() replacement\n//                     fix memory leak if fontsize=0.0\n//                     fix warning from duplicate typedef\n//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges\n//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges\n//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;\n//                     allow PackFontRanges to pack and render in separate phases;\n//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);\n//                     fixed an assert() bug in the new rasterizer\n//                     replace assert() with STBTT_assert() in new rasterizer\n//   1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)\n//                     also more precise AA rasterizer, except if shapes overlap\n//                     remove need for STBTT_sort\n//   1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC\n//   1.04 (2015-04-15) typo in example\n//   1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes\n//   1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++\n//   1.01 (2014-12-08) fix subpixel position when oversampling to exactly match\n//                        non-oversampled; STBTT_POINT_SIZE for packed case only\n//   1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling\n//   0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)\n//   0.9  (2014-08-07) support certain mac/iOS fonts without an MS platformID\n//   0.8b (2014-07-07) fix a warning\n//   0.8  (2014-05-25) fix a few more warnings\n//   0.7  (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back\n//   0.6c (2012-07-24) improve documentation\n//   0.6b (2012-07-20) fix a few more warnings\n//   0.6  (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,\n//                        stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty\n//   0.5  (2011-12-09) bugfixes:\n//                        subpixel glyph renderer computed wrong bounding box\n//                        first vertex of shape can be off-curve (FreeSans)\n//   0.4b (2011-12-03) fixed an error in the font baking example\n//   0.4  (2011-12-01) kerning, subpixel rendering (tor)\n//                    bugfixes for:\n//                        codepoint-to-glyph conversion using table fmt=12\n//                        codepoint-to-glyph conversion using table fmt=4\n//                        stbtt_GetBakedQuad with non-square texture (Zer)\n//                    updated Hello World! sample to use kerning and subpixel\n//                    fixed some warnings\n//   0.3  (2009-06-24) cmap fmt=12, compound shapes (MM)\n//                    userdata, malloc-from-userdata, non-zero fill (stb)\n//   0.2  (2009-03-11) Fix unsigned/signed char warnings\n//   0.1  (2009-03-09) First public release\n//\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "shared/CMakeLists.txt",
    "content": "# Box2D code shared by samples, benchmarks, and unit tests\n\nset(BOX2D_SHARED_FILES\n\tbenchmarks.c\n\tbenchmarks.h\n\tdeterminism.c\n\tdeterminism.h\n\thuman.c\n\thuman.h\n\trandom.c\n\trandom.h\n)\n\nadd_library(shared STATIC ${BOX2D_SHARED_FILES})\n\nset_target_properties(shared PROPERTIES\n\tC_STANDARD 17\n)\n\nif (BOX2D_COMPILE_WARNING_AS_ERROR)\n\tset_target_properties(shared PROPERTIES COMPILE_WARNING_AS_ERROR ON)\nendif()\n\ntarget_include_directories(shared PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\ntarget_link_libraries(shared PRIVATE box2d)\n\nsource_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX \"\" FILES ${BOX2D_SHARED_FILES})"
  },
  {
    "path": "shared/benchmarks.c",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"benchmarks.h\"\n\n#include \"human.h\"\n\n#include \"box2d/box2d.h\"\n\n#include <assert.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef NDEBUG\n#define BENCHMARK_DEBUG 0\n#else\n#define BENCHMARK_DEBUG 1\n#endif\n\nvoid CreateJointGrid( b2WorldId worldId )\n{\n\tb2World_EnableSleeping( worldId, false );\n\n\tint N = BENCHMARK_DEBUG ? 10 : 100;\n\n\t// Allocate to avoid huge stack usage\n\tb2BodyId* bodies = malloc( N * N * sizeof( b2BodyId ) );\n\tint index = 0;\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.density = 1.0f;\n\tshapeDef.filter.categoryBits = 2;\n\tshapeDef.filter.maskBits = ~2u;\n\n\tb2Circle circle = { { 0.0f, 0.0f }, 0.4f };\n\n\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\tjointDef.base.drawScale = 0.4f;\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\n\tfor ( int k = 0; k < N; ++k )\n\t{\n\t\tfor ( int i = 0; i < N; ++i )\n\t\t{\n\t\t\tfloat fk = (float)k;\n\t\t\tfloat fi = (float)i;\n\n\t\t\tif ( k >= N / 2 - 3 && k <= N / 2 + 3 && i == 0 )\n\t\t\t{\n\t\t\t\tbodyDef.type = b2_staticBody;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t\t}\n\n\t\t\tbodyDef.position = (b2Vec2){ fk, -fi };\n\n\t\t\tb2BodyId body = b2CreateBody( worldId, &bodyDef );\n\n\t\t\tb2CreateCircleShape( body, &shapeDef, &circle );\n\n\t\t\tif ( i > 0 )\n\t\t\t{\n\t\t\t\tjointDef.base.bodyIdA = bodies[index - 1];\n\t\t\t\tjointDef.base.bodyIdB = body;\n\t\t\t\tjointDef.base.localFrameA.p = (b2Vec2){ 0.0f, -0.5f };\n\t\t\t\tjointDef.base.localFrameB.p = (b2Vec2){ 0.0f, 0.5f };\n\t\t\t\tb2CreateRevoluteJoint( worldId, &jointDef );\n\t\t\t}\n\n\t\t\tif ( k > 0 )\n\t\t\t{\n\t\t\t\tjointDef.base.bodyIdA = bodies[index - N];\n\t\t\t\tjointDef.base.bodyIdB = body;\n\t\t\t\tjointDef.base.localFrameA.p = (b2Vec2){ 0.5f, 0.0f };\n\t\t\t\tjointDef.base.localFrameB.p = (b2Vec2){ -0.5f, 0.0f };\n\t\t\t\tb2CreateRevoluteJoint( worldId, &jointDef );\n\t\t\t}\n\n\t\t\tbodies[index++] = body;\n\t\t}\n\t}\n\n\tfree( bodies );\n}\n\nvoid CreateLargePyramid( b2WorldId worldId )\n{\n\tb2World_EnableSleeping( worldId, false );\n\n\tint baseCount = BENCHMARK_DEBUG ? 20 : 100;\n\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.position = (b2Vec2){ 0.0f, -1.0f };\n\t\tb2BodyId groundId = b2CreateBody( worldId, &bodyDef );\n\n\t\tb2Polygon box = b2MakeBox( 100.0f, 1.0f );\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t}\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\t// bodyDef.enableSleep = false;\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.density = 1.0f;\n\n\tfloat a = 0.5f;\n\tb2Polygon box = b2MakeSquare( a );\n\n\tfloat shift = 1.0f * a;\n\n\tfor ( int i = 0; i < baseCount; ++i )\n\t{\n\t\tfloat y = ( 2.0f * i + 1.0f ) * shift;\n\n\t\tfor ( int j = i; j < baseCount; ++j )\n\t\t{\n\t\t\tfloat x = ( i + 1.0f ) * shift + 2.0f * ( j - i ) * shift - a * baseCount;\n\n\t\t\tbodyDef.position = (b2Vec2){ x, y };\n\n\t\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n}\n\nstatic void CreateSmallPyramid( b2WorldId worldId, int baseCount, float extent, float centerX, float baseY )\n{\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\tb2Polygon box = b2MakeSquare( extent );\n\n\tfor ( int i = 0; i < baseCount; ++i )\n\t{\n\t\tfloat y = ( 2.0f * i + 1.0f ) * extent + baseY;\n\n\t\tfor ( int j = i; j < baseCount; ++j )\n\t\t{\n\t\t\tfloat x = ( i + 1.0f ) * extent + 2.0f * ( j - i ) * extent + centerX - 0.5f;\n\t\t\tbodyDef.position = (b2Vec2){ x, y };\n\n\t\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n}\n\nvoid CreateManyPyramids( b2WorldId worldId )\n{\n\tb2World_EnableSleeping( worldId, false );\n\n\tint baseCount = 10;\n\tfloat extent = 0.5f;\n\tint rowCount = BENCHMARK_DEBUG ? 5 : 20;\n\tint columnCount = BENCHMARK_DEBUG ? 5 : 20;\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tb2BodyId groundId = b2CreateBody( worldId, &bodyDef );\n\n\tfloat groundDeltaY = 2.0f * extent * ( baseCount + 1.0f );\n\tfloat groundWidth = 2.0f * extent * columnCount * ( baseCount + 1.0f );\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\tfloat groundY = 0.0f;\n\n\tfor ( int i = 0; i < rowCount; ++i )\n\t{\n\t\tb2Segment segment = { { -0.5f * 2.0f * groundWidth, groundY }, { 0.5f * 2.0f * groundWidth, groundY } };\n\t\tb2CreateSegmentShape( groundId, &shapeDef, &segment );\n\t\tgroundY += groundDeltaY;\n\t}\n\n\tfloat baseWidth = 2.0f * extent * baseCount;\n\tfloat baseY = 0.0f;\n\n\tfor ( int i = 0; i < rowCount; ++i )\n\t{\n\t\tfor ( int j = 0; j < columnCount; ++j )\n\t\t{\n\t\t\tfloat centerX = -0.5f * groundWidth + j * ( baseWidth + 2.0f * extent ) + extent;\n\t\t\tCreateSmallPyramid( worldId, baseCount, extent, centerX, baseY );\n\t\t}\n\n\t\tbaseY += groundDeltaY;\n\t}\n}\n\n#ifdef NDEBUG\nenum RainConstants\n{\n\tRAIN_ROW_COUNT = 5,\n\tRAIN_COLUMN_COUNT = 40,\n\tRAIN_GROUP_SIZE = 5,\n};\n#else\nenum RainConstants\n{\n\tRAIN_ROW_COUNT = 3,\n\tRAIN_COLUMN_COUNT = 10,\n\tRAIN_GROUP_SIZE = 2,\n};\n#endif\n\ntypedef struct Group\n{\n\tHuman humans[RAIN_GROUP_SIZE];\n} Group;\n\ntypedef struct RainData\n{\n\tGroup groups[RAIN_ROW_COUNT * RAIN_COLUMN_COUNT];\n\tfloat gridSize;\n\tint gridCount;\n\tint columnCount;\n\tint columnIndex;\n} RainData;\n\nRainData g_rainData;\n\nvoid CreateRain( b2WorldId worldId )\n{\n\tmemset( &g_rainData, 0, sizeof( g_rainData ) );\n\n\tg_rainData.gridSize = 0.5f;\n\tg_rainData.gridCount = BENCHMARK_DEBUG ? 200 : 500;\n\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tb2BodyId groundId = b2CreateBody( worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tfloat y = 0.0f;\n\t\tfloat width = g_rainData.gridSize;\n\t\tfloat height = g_rainData.gridSize;\n\n\t\tfor ( int i = 0; i < RAIN_ROW_COUNT; ++i )\n\t\t{\n\t\t\tfloat x = -0.5f * g_rainData.gridCount * g_rainData.gridSize;\n\t\t\tfor ( int j = 0; j <= g_rainData.gridCount; ++j )\n\t\t\t{\n\t\t\t\tb2Polygon box = b2MakeOffsetBox( 0.5f * width, 0.5f * height, (b2Vec2){ x, y }, b2Rot_identity );\n\t\t\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\n\t\t\t\t// b2Segment segment = { { x - 0.5f * width, y }, { x + 0.5f * width, y } };\n\t\t\t\t// b2CreateSegmentShape( groundId, &shapeDef, &segment );\n\n\t\t\t\tx += g_rainData.gridSize;\n\t\t\t}\n\n\t\t\ty += 45.0f;\n\t\t}\n\t}\n\n\tg_rainData.columnCount = 0;\n\tg_rainData.columnIndex = 0;\n}\n\nvoid CreateGroup( b2WorldId worldId, int rowIndex, int columnIndex )\n{\n\tassert( rowIndex < RAIN_ROW_COUNT && columnIndex < RAIN_COLUMN_COUNT );\n\n\tint groupIndex = rowIndex * RAIN_COLUMN_COUNT + columnIndex;\n\n\tfloat span = g_rainData.gridCount * g_rainData.gridSize;\n\tfloat groupDistance = 1.0f * span / RAIN_COLUMN_COUNT;\n\n\tb2Vec2 position;\n\tposition.x = -0.5f * span + groupDistance * ( columnIndex + 0.5f );\n\tposition.y = 40.0f + 45.0f * rowIndex;\n\n\tfloat scale = 1.0f;\n\tfloat jointFriction = 0.05f;\n\tfloat jointHertz = 5.0f;\n\tfloat jointDamping = 0.5f;\n\n\tfor ( int i = 0; i < RAIN_GROUP_SIZE; ++i )\n\t{\n\t\tHuman* human = g_rainData.groups[groupIndex].humans + i;\n\t\tCreateHuman( human, worldId, position, scale, jointFriction, jointHertz, jointDamping, i + 1, NULL, false );\n\t\tposition.x += 0.5f;\n\t}\n}\n\nvoid DestroyGroup( int rowIndex, int columnIndex )\n{\n\tassert( rowIndex < RAIN_ROW_COUNT && columnIndex < RAIN_COLUMN_COUNT );\n\n\tint groupIndex = rowIndex * RAIN_COLUMN_COUNT + columnIndex;\n\n\tfor ( int i = 0; i < RAIN_GROUP_SIZE; ++i )\n\t{\n\t\tDestroyHuman( g_rainData.groups[groupIndex].humans + i );\n\t}\n}\n\nfloat StepRain( b2WorldId worldId, int stepCount )\n{\n\tint delay = BENCHMARK_DEBUG ? 0x1F : 0x7;\n\n\tif ( ( stepCount & delay ) == 0 )\n\t{\n\t\tif ( g_rainData.columnCount < RAIN_COLUMN_COUNT )\n\t\t{\n\t\t\tfor ( int i = 0; i < RAIN_ROW_COUNT; ++i )\n\t\t\t{\n\t\t\t\tCreateGroup( worldId, i, g_rainData.columnCount );\n\t\t\t}\n\n\t\t\tg_rainData.columnCount += 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor ( int i = 0; i < RAIN_ROW_COUNT; ++i )\n\t\t\t{\n\t\t\t\tDestroyGroup( i, g_rainData.columnIndex );\n\t\t\t\tCreateGroup( worldId, i, g_rainData.columnIndex );\n\t\t\t}\n\n\t\t\tg_rainData.columnIndex = ( g_rainData.columnIndex + 1 ) % RAIN_COLUMN_COUNT;\n\t\t}\n\t}\n\n\treturn 0.0f;\n}\n\n#define SPINNER_POINT_COUNT 360\n\ntypedef struct\n{\n\tb2JointId spinnerId;\n} SpinnerData;\n\nSpinnerData g_spinnerData;\n\nvoid CreateSpinner( b2WorldId worldId )\n{\n\tb2BodyId groundId;\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tgroundId = b2CreateBody( worldId, &bodyDef );\n\n\t\tb2Vec2 points[SPINNER_POINT_COUNT];\n\n\t\tb2Rot q = b2MakeRot( -2.0f * B2_PI / SPINNER_POINT_COUNT );\n\t\tb2Vec2 p = { 40.0f, 0.0f };\n\t\tfor ( int i = 0; i < SPINNER_POINT_COUNT; ++i )\n\t\t{\n\t\t\tpoints[i] = (b2Vec2){ p.x, p.y + 32.0f };\n\t\t\tp = b2RotateVector( q, p );\n\t\t}\n\n\t\tb2SurfaceMaterial material = { 0 };\n\t\tmaterial.friction = 0.1f;\n\n\t\tb2ChainDef chainDef = b2DefaultChainDef();\n\t\tchainDef.points = points;\n\t\tchainDef.count = SPINNER_POINT_COUNT;\n\t\tchainDef.isLoop = true;\n\t\tchainDef.materials = &material;\n\t\tchainDef.materialCount = 1;\n\n\t\tb2CreateChain( groundId, &chainDef );\n\t}\n\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = (b2Vec2){ 0.0, 12.0f };\n\t\tbodyDef.enableSleep = false;\n\n\t\tb2BodyId spinnerId = b2CreateBody( worldId, &bodyDef );\n\n\t\tb2Polygon box = b2MakeRoundedBox( 0.4f, 20.0f, 0.2f );\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.material.friction = 0.0f;\n\t\tb2CreatePolygonShape( spinnerId, &shapeDef, &box );\n\n\t\tfloat motorSpeed = 5.0f;\n\t\t// float maxMotorTorque = 100.0f * 40000.0f;\n\t\tfloat maxMotorTorque = FLT_MAX;\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = groundId;\n\t\tjointDef.base.bodyIdB = spinnerId;\n\t\tjointDef.base.localFrameA.p = bodyDef.position;\n\t\tjointDef.enableMotor = true;\n\t\tjointDef.motorSpeed = motorSpeed;\n\t\tjointDef.maxMotorTorque = maxMotorTorque;\n\n\t\tg_spinnerData.spinnerId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\tb2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f };\n\tb2Circle circle = { { 0.0f, 0.0f }, 0.35f };\n\tb2Polygon square = b2MakeSquare( 0.35f );\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.material.friction = 0.1f;\n\tshapeDef.material.restitution = 0.1f;\n\tshapeDef.density = 0.25f;\n\n\tint bodyCount = BENCHMARK_DEBUG ? 499 : 2 * 3038;\n\n\tfloat x = -23.0f, y = 2.0f;\n\tfor ( int i = 0; i < bodyCount; ++i )\n\t{\n\t\tbodyDef.position = (b2Vec2){ x, y };\n\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t\tint remainder = i % 3;\n\t\tif ( remainder == 0 )\n\t\t{\n\t\t\tb2CreateCapsuleShape( bodyId, &shapeDef, &capsule );\n\t\t}\n\t\telse if ( remainder == 1 )\n\t\t{\n\t\t\tb2CreateCircleShape( bodyId, &shapeDef, &circle );\n\t\t}\n\t\telse if ( remainder == 2 )\n\t\t{\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &square );\n\t\t}\n\n\t\tx += 0.5f;\n\n\t\tif ( x >= 23.0f )\n\t\t{\n\t\t\tx = -23.0f;\n\t\t\ty += 0.5f;\n\t\t}\n\t}\n}\n\nfloat StepSpinner( b2WorldId worldId, int stepCount )\n{\n\t(void)worldId;\n\t(void)stepCount;\n\n\treturn b2RevoluteJoint_GetAngle( g_spinnerData.spinnerId );\n}\n\nvoid CreateSmash( b2WorldId worldId )\n{\n\tb2World_SetGravity( worldId, b2Vec2_zero );\n\n\t{\n\t\tb2Polygon box = b2MakeBox( 4.0f, 4.0f );\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = (b2Vec2){ -20.0f, 0.0f };\n\t\tbodyDef.linearVelocity = (b2Vec2){ 40.0f, 0.0f };\n\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 8.0f;\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t}\n\n\tfloat d = 0.4f;\n\tb2Polygon box = b2MakeSquare( 0.5f * d );\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tbodyDef.isAwake = false;\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\tint columns = BENCHMARK_DEBUG ? 20 : 120;\n\tint rows = BENCHMARK_DEBUG ? 10 : 80;\n\n\tfor ( int i = 0; i < columns; ++i )\n\t{\n\t\tfor ( int j = 0; j < rows; ++j )\n\t\t{\n\t\t\tbodyDef.position.x = i * d + 30.0f;\n\t\t\tbodyDef.position.y = ( j - rows / 2.0f ) * d;\n\t\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\t\t}\n\t}\n}\n\nvoid CreateTumbler( b2WorldId worldId )\n{\n\tb2BodyId groundId;\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tgroundId = b2CreateBody( worldId, &bodyDef );\n\t}\n\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.type = b2_dynamicBody;\n\t\tbodyDef.position = (b2Vec2){ 0.0f, 10.0f };\n\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tshapeDef.density = 50.0f;\n\n\t\tb2Polygon polygon;\n\t\tpolygon = b2MakeOffsetBox( 0.5f, 10.0f, (b2Vec2){ 10.0f, 0.0f }, b2Rot_identity );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\tpolygon = b2MakeOffsetBox( 0.5f, 10.0f, (b2Vec2){ -10.0f, 0.0f }, b2Rot_identity );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\tpolygon = b2MakeOffsetBox( 10.0f, 0.5f, (b2Vec2){ 0.0f, 10.0f }, b2Rot_identity );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\tpolygon = b2MakeOffsetBox( 10.0f, 0.5f, (b2Vec2){ 0.0f, -10.0f }, b2Rot_identity );\n\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\n\t\tfloat motorSpeed = 25.0f;\n\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = groundId;\n\t\tjointDef.base.bodyIdB = bodyId;\n\t\tjointDef.base.localFrameA.p = (b2Vec2){ 0.0f, 10.0f };\n\t\tjointDef.base.localFrameB.p = (b2Vec2){ 0.0f, 0.0f };\n\t\tjointDef.motorSpeed = ( B2_PI / 180.0f ) * motorSpeed;\n\t\tjointDef.maxMotorTorque = 1e8f;\n\t\tjointDef.enableMotor = true;\n\n\t\tb2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\tint gridCount = BENCHMARK_DEBUG ? 20 : 45;\n\n\tb2Polygon polygon = b2MakeBox( 0.125f, 0.125f );\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\tfloat y = -0.2f * gridCount + 10.0f;\n\tfor ( int i = 0; i < gridCount; ++i )\n\t{\n\t\tfloat x = -0.2f * gridCount;\n\n\t\tfor ( int j = 0; j < gridCount; ++j )\n\t\t{\n\t\t\tbodyDef.position = (b2Vec2){ x, y };\n\t\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\n\t\t\tx += 0.4f;\n\t\t}\n\n\t\ty += 0.4f;\n\t}\n}\n\nvoid CreateWasher( b2WorldId worldId )\n{\n\tbool kinematic = true;\n\n\tb2BodyId groundId;\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tgroundId = b2CreateBody( worldId, &bodyDef );\n\t}\n\n\t{\n\t\tfloat motorSpeed = 25.0f;\n\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.position = (b2Vec2){ 0.0f, 10.0f };\n\n\t\tif (kinematic == true)\n\t\t{\n\t\t\tbodyDef.type = b2_kinematicBody;\n\t\t\tbodyDef.angularVelocity = ( B2_PI / 180.0f ) * motorSpeed;\n\t\t\tbodyDef.linearVelocity = (b2Vec2){ 0.001f, -0.002f };\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbodyDef.type = b2_dynamicBody;\n\t\t}\n\n\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t\tfloat r0 = 14.0f;\n\t\tfloat r1 = 16.0f;\n\t\tfloat r2 = 18.0f;\n\n\t\tfloat angle = B2_PI / 18.0f;\n\t\tb2Rot q = { cosf( angle ), sinf(angle) };\n\t\tb2Rot qo = { cosf( 0.1f * angle ), sinf(0.1f * angle) };\n\t\tb2Vec2 u1 = { 1.0f, 0.0f };\n\t\tfor ( int i = 0; i < 36; ++i )\n\t\t{\n\t\t\tb2Vec2 u2;\n\t\t\tif (i == 35)\n\t\t\t{\n\t\t\t\tu2 = (b2Vec2){ 1.0f, 0.0f };\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tu2 = b2RotateVector( q, u1 );\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tb2Vec2 a1 = b2InvRotateVector( qo, u1 );\n\t\t\t\tb2Vec2 a2 = b2RotateVector( qo, u2 );\n\n\t\t\t\tb2Vec2 p1 = b2MulSV( r1, a1 );\n\t\t\t\tb2Vec2 p2 = b2MulSV( r2, a1 );\n\t\t\t\tb2Vec2 p3 = b2MulSV( r1, a2 );\n\t\t\t\tb2Vec2 p4 = b2MulSV( r2, a2 );\n\n\t\t\t\tb2Vec2 points[4] = { p1, p2, p3, p4 };\n\t\t\t\tb2Hull hull = b2ComputeHull( points, 4 );\n\n\t\t\t\tb2Polygon polygon = b2MakePolygon( &hull, 0.0f );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\t\t}\n\n\t\t\tif ( i % 9 == 0 )\n\t\t\t{\n\t\t\t\tb2Vec2 p1 = b2MulSV( r0, u1 );\n\t\t\t\tb2Vec2 p2 = b2MulSV( r1, u1 );\n\t\t\t\tb2Vec2 p3 = b2MulSV( r0, u2 );\n\t\t\t\tb2Vec2 p4 = b2MulSV( r1, u2 );\n\n\t\t\t\tb2Vec2 points[4] = { p1, p2, p3, p4 };\n\t\t\t\tb2Hull hull = b2ComputeHull( points, 4 );\n\n\t\t\t\tb2Polygon polygon = b2MakePolygon( &hull, 0.0f );\n\t\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\t\t\t}\n\n\t\t\tu1 = u2;\n\t\t}\n\n\t\tif ( kinematic == false )\n\t\t{\n\t\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\t\tjointDef.base.bodyIdA = groundId;\n\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\tjointDef.base.localFrameA.p = (b2Vec2){ 0.0f, 10.0f };\n\t\t\tjointDef.base.localFrameB.p = (b2Vec2){ 0.0f, 0.0f };\n\t\t\tjointDef.motorSpeed = ( B2_PI / 180.0f ) * motorSpeed;\n\t\t\tjointDef.maxMotorTorque = 1e8f;\n\t\t\tjointDef.enableMotor = true;\n\n\t\t\tb2CreateRevoluteJoint( worldId, &jointDef );\n\t\t}\n\t}\n\n\tint gridCount = BENCHMARK_DEBUG ? 20 : 90;\n\tfloat a = 0.1f;\n\n\tb2Polygon polygon = b2MakeSquare( a );\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\tfloat y = -1.1f * a * gridCount + 10.0f;\n\tfor ( int i = 0; i < gridCount; ++i )\n\t{\n\t\tfloat x = -1.1f * a * gridCount;\n\n\t\tfor ( int j = 0; j < gridCount; ++j )\n\t\t{\n\t\t\tbodyDef.position = (b2Vec2){ x, y };\n\t\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &polygon );\n\n\t\t\tx += 2.1f * a;\n\t\t}\n\n\t\ty += 2.1f * a;\n\t}\n}\n"
  },
  {
    "path": "shared/benchmarks.h",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n#pragma once\n\n#include \"box2d/id.h\"\n\n#include <stdbool.h>\n\n// This allows benchmarks to be tested on the benchmark app and also visualized in the samples app\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\nvoid CreateJointGrid( b2WorldId worldId );\nvoid CreateLargePyramid( b2WorldId worldId );\nvoid CreateManyPyramids( b2WorldId worldId );\nvoid CreateRain( b2WorldId worldId );\nfloat StepRain( b2WorldId worldId, int stepCount );\nvoid CreateSpinner( b2WorldId worldId );\nfloat StepSpinner( b2WorldId worldId, int stepCount );\nvoid CreateSmash( b2WorldId worldId );\nvoid CreateTumbler( b2WorldId worldId );\nvoid CreateWasher( b2WorldId worldId );\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "shared/determinism.c",
    "content": "// SPDX-FileCopyrightText: 2022 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"determinism.h\"\n\n#include \"box2d/box2d.h\"\n\n#include <assert.h>\n#include <stdlib.h>\n\nFallingHingeData CreateFallingHinges( b2WorldId worldId )\n{\n\t{\n\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\tbodyDef.position = (b2Vec2){ 0.0f, -1.0f };\n\t\tb2BodyId groundId = b2CreateBody( worldId, &bodyDef );\n\n\t\tb2Polygon box = b2MakeBox( 20.0f, 1.0f );\n\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\tb2CreatePolygonShape( groundId, &shapeDef, &box );\n\t}\n\n\tint columnCount = 4;\n\tint rowCount = 30;\n\tint bodyCount = rowCount * columnCount;\n\n\tb2BodyId* bodyIds = calloc( bodyCount, sizeof( b2BodyId ) );\n\n\tfloat h = 0.25f;\n\tfloat r = 0.1f * h;\n\tb2Polygon box = b2MakeRoundedBox( h - r, h - r, r );\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.material.friction = 0.3f;\n\n\tfloat offset = 0.4f * h;\n\tfloat dx = 10.0f * h;\n\tfloat xroot = -0.5f * dx * ( columnCount - 1.0f );\n\n\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\tjointDef.enableLimit = true;\n\tjointDef.lowerAngle = -0.1f * B2_PI;\n\tjointDef.upperAngle = 0.2f * B2_PI;\n\tjointDef.enableSpring = true;\n\tjointDef.hertz = 0.5f;\n\tjointDef.dampingRatio = 0.5f;\n\tjointDef.base.localFrameA.p = (b2Vec2){ h, h };\n\tjointDef.base.localFrameB.p = (b2Vec2){ offset, -h };\n\tjointDef.base.constraintHertz = 60.0f;\n\tjointDef.base.constraintDampingRatio = 0.0f;\n\tjointDef.base.drawScale = 0.5f;\n\n\tint bodyIndex = 0;\n\n\tfor ( int j = 0; j < columnCount; ++j )\n\t{\n\t\tfloat x = xroot + j * dx;\n\n\t\tb2BodyId prevBodyId = b2_nullBodyId;\n\n\t\tfor ( int i = 0; i < rowCount; ++i )\n\t\t{\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tbodyDef.type = b2_dynamicBody;\n\n\t\t\tbodyDef.position.x = x + offset * i;\n\t\t\tbodyDef.position.y = h + 2.0f * h * i;\n\n\t\t\t// this tests the deterministic cosine and sine functions\n\t\t\tbodyDef.rotation = b2MakeRot( 0.1f * i - 1.0f );\n\n\t\t\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t\t\tif ( ( i & 1 ) == 0 )\n\t\t\t{\n\t\t\t\tprevBodyId = bodyId;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tjointDef.base.bodyIdA = prevBodyId;\n\t\t\t\tjointDef.base.bodyIdB = bodyId;\n\t\t\t\tb2CreateRevoluteJoint( worldId, &jointDef );\n\t\t\t\tprevBodyId = b2_nullBodyId;\n\t\t\t}\n\n\t\t\tb2CreatePolygonShape( bodyId, &shapeDef, &box );\n\n\t\t\tassert( bodyIndex < bodyCount );\n\t\t\tbodyIds[bodyIndex] = bodyId;\n\n\t\t\tbodyIndex += 1;\n\t\t}\n\t}\n\n\tassert( bodyIndex == bodyCount );\n\n\tFallingHingeData data = {\n\t\t.bodyIds = bodyIds,\n\t\t.bodyCount = bodyCount,\n\t\t.stepCount = 0,\n\t\t.sleepStep = -1,\n\t\t.hash = 0,\n\t};\n\treturn data;\n}\n\nbool UpdateFallingHinges( b2WorldId worldId, FallingHingeData* data )\n{\n\tif ( data->hash == 0 )\n\t{\n\t\tb2BodyEvents bodyEvents = b2World_GetBodyEvents( worldId );\n\n\t\tif ( bodyEvents.moveCount == 0 )\n\t\t{\n\t\t\tint awakeCount = b2World_GetAwakeBodyCount( worldId );\n\t\t\tassert( awakeCount == 0 );\n\n\t\t\tdata->hash = B2_HASH_INIT;\n\t\t\tfor ( int i = 0; i < data->bodyCount; ++i )\n\t\t\t{\n\t\t\t\tb2Transform xf = b2Body_GetTransform( data->bodyIds[i] );\n\t\t\t\tdata->hash = b2Hash( data->hash, (uint8_t*)( &xf ), sizeof( b2Transform ) );\n\t\t\t}\n\n\t\t\tdata->sleepStep = data->stepCount;\n\t\t}\n\t}\n\n\tdata->stepCount += 1;\n\n\treturn data->hash != 0;\n}\n\nvoid DestroyFallingHinges( FallingHingeData* data )\n{\n\tfree( data->bodyIds );\n\tdata->bodyIds = NULL;\n}\n"
  },
  {
    "path": "shared/determinism.h",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n#pragma once\n\n#include \"box2d/id.h\"\n\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\ntypedef struct FallingHingeData\n{\n\tb2BodyId* bodyIds;\n\tint bodyCount;\n\tint stepCount;\n\tint sleepStep;\n\tuint32_t hash;\n} FallingHingeData;\n\nFallingHingeData CreateFallingHinges( b2WorldId worldId );\nbool UpdateFallingHinges( b2WorldId worldId, FallingHingeData* data );\nvoid DestroyFallingHinges( FallingHingeData* data );\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "shared/human.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"human.h\"\n\n#include \"random.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/math_functions.h\"\n\n#include <assert.h>\n\nvoid CreateHuman( Human* human, b2WorldId worldId, b2Vec2 position, float scale, float frictionTorque, float hertz,\n\t\t\t\t  float dampingRatio, int groupIndex, void* userData, bool colorize )\n{\n\tassert( human->isSpawned == false );\n\n\tfor ( int i = 0; i < bone_count; ++i )\n\t{\n\t\thuman->bones[i].bodyId = b2_nullBodyId;\n\t\thuman->bones[i].jointId = b2_nullJointId;\n\t\thuman->bones[i].frictionScale = 1.0f;\n\t\thuman->bones[i].parentIndex = -1;\n\t}\n\n\thuman->originalScale = scale;\n\thuman->scale = scale;\n\thuman->frictionTorque = frictionTorque;\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tbodyDef.sleepThreshold = 0.1f;\n\tbodyDef.userData = userData;\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.material.friction = 0.2f;\n\tshapeDef.filter.groupIndex = -groupIndex;\n\tshapeDef.filter.categoryBits = 2;\n\tshapeDef.filter.maskBits = ( 1 | 2 );\n\n\tb2ShapeDef footShapeDef = shapeDef;\n\tfootShapeDef.material.friction = 0.05f;\n\n\t// feet don't collide with ragdolls\n\tfootShapeDef.filter.categoryBits = 2;\n\tfootShapeDef.filter.maskBits = 1;\n\n\tif ( colorize )\n\t{\n\t\tfootShapeDef.material.customColor = b2_colorSaddleBrown;\n\t}\n\n\tfloat s = scale;\n\tfloat maxTorque = frictionTorque * s;\n\tbool enableMotor = true;\n\tbool enableLimit = true;\n\tfloat drawSize = 0.05f;\n\n\tb2HexColor shirtColor = b2_colorMediumTurquoise;\n\tb2HexColor pantColor = b2_colorDodgerBlue;\n\n\tb2HexColor skinColors[4] = { b2_colorNavajoWhite, b2_colorLightYellow, b2_colorPeru, b2_colorTan };\n\tb2HexColor skinColor = skinColors[groupIndex % 4];\n\n\t// hip\n\t{\n\t\tBone* bone = human->bones + bone_hip;\n\t\tbone->parentIndex = -1;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 0.95f * s }, position );\n\t\tbodyDef.linearDamping = 0.0f;\n\t\tbodyDef.name = \"hip\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = pantColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.02f * s }, { 0.0f, 0.02f * s }, 0.095f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\t}\n\n\t// torso\n\t{\n\t\tBone* bone = human->bones + bone_torso;\n\t\tbone->parentIndex = bone_hip;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 1.2f * s }, position );\n\t\tbodyDef.linearDamping = 0.0f;\n\t\tbodyDef.name = \"torso\";\n\n\t\t// bodyDef.type = b2_staticBody;\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\t\tbone->frictionScale = 0.5f;\n\t\tbodyDef.type = b2_dynamicBody;\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = shirtColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.135f * s }, { 0.0f, 0.135f * s }, 0.09f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 1.0f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.25f * B2_PI;\n\t\tjointDef.upperAngle = 0.0f;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\t// head\n\t{\n\t\tBone* bone = human->bones + bone_head;\n\t\tbone->parentIndex = bone_torso;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f * s, 1.475f * s }, position );\n\t\tbodyDef.linearDamping = 0.1f;\n\t\tbodyDef.name = \"head\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\t\tbone->frictionScale = 0.25f;\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = skinColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.038f * s }, { 0.0f, 0.039f * s }, 0.075f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\t//// neck\n\t\t// capsule = { { 0.0f, -0.12f * s }, { 0.0f, -0.08f * s }, 0.05f * s };\n\t\t// b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 1.4f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.3f * B2_PI;\n\t\tjointDef.upperAngle = 0.1f * B2_PI;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\t// upper left leg\n\t{\n\t\tBone* bone = human->bones + bone_upperLeftLeg;\n\t\tbone->parentIndex = bone_hip;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 0.775f * s }, position );\n\t\tbodyDef.linearDamping = 0.0f;\n\t\tbodyDef.name = \"upper_left_leg\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\t\tbone->frictionScale = 1.0f;\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = pantColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.06f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 0.9f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.05f * B2_PI;\n\t\tjointDef.upperAngle = 0.4f * B2_PI;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\tb2Vec2 points[4] = {\n\t\t{ -0.03f * s, -0.185f * s },\n\t\t{ 0.11f * s, -0.185f * s },\n\t\t{ 0.11f * s, -0.16f * s },\n\t\t{ -0.03f * s, -0.14f * s },\n\t};\n\n\tb2Hull footHull = b2ComputeHull( points, 4 );\n\tb2Polygon footPolygon = b2MakePolygon( &footHull, 0.015f * s );\n\n\t// lower left leg\n\t{\n\t\tBone* bone = human->bones + bone_lowerLeftLeg;\n\t\tbone->parentIndex = bone_upperLeftLeg;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 0.475f * s }, position );\n\t\tbodyDef.linearDamping = 0.0f;\n\t\tbodyDef.name = \"lower_left_leg\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\t\tbone->frictionScale = 0.5f;\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = pantColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.155f * s }, { 0.0f, 0.125f * s }, 0.045f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\t// b2Polygon box = b2MakeOffsetBox(0.1f * s, 0.03f * s, {0.05f * s, -0.175f * s}, 0.0f);\n\t\t// b2CreatePolygonShape(bone->bodyId, &shapeDef, &box);\n\n\t\t// capsule = { { -0.02f * s, -0.175f * s }, { 0.13f * s, -0.175f * s }, 0.03f * s };\n\t\t// b2CreateCapsuleShape( bone->bodyId, &footShapeDef, &capsule );\n\n\t\tb2CreatePolygonShape( bone->bodyId, &footShapeDef, &footPolygon );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 0.625f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.5f * B2_PI;\n\t\tjointDef.upperAngle = -0.02f * B2_PI;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\t// upper right leg\n\t{\n\t\tBone* bone = human->bones + bone_upperRightLeg;\n\t\tbone->parentIndex = bone_hip;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 0.775f * s }, position );\n\t\tbodyDef.linearDamping = 0.0f;\n\t\tbodyDef.name = \"upper_right_leg\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\t\tbone->frictionScale = 1.0f;\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = pantColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.06f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 0.9f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.05f * B2_PI;\n\t\tjointDef.upperAngle = 0.4f * B2_PI;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\t// lower right leg\n\t{\n\t\tBone* bone = human->bones + bone_lowerRightLeg;\n\t\tbone->parentIndex = bone_upperRightLeg;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 0.475f * s }, position );\n\t\tbodyDef.linearDamping = 0.0f;\n\t\tbodyDef.name = \"lower_right_leg\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\t\tbone->frictionScale = 0.5f;\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = pantColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.155f * s }, { 0.0f, 0.125f * s }, 0.045f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\t// b2Polygon box = b2MakeOffsetBox(0.1f * s, 0.03f * s, {0.05f * s, -0.175f * s}, 0.0f);\n\t\t// b2CreatePolygonShape(bone->bodyId, &shapeDef, &box);\n\n\t\t// capsule = { { -0.02f * s, -0.175f * s }, { 0.13f * s, -0.175f * s }, 0.03f * s };\n\t\t// b2CreateCapsuleShape( bone->bodyId, &footShapeDef, &capsule );\n\n\t\tb2CreatePolygonShape( bone->bodyId, &footShapeDef, &footPolygon );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 0.625f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.5f * B2_PI;\n\t\tjointDef.upperAngle = -0.02f * B2_PI;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\t// upper left arm\n\t{\n\t\tBone* bone = human->bones + bone_upperLeftArm;\n\t\tbone->parentIndex = bone_torso;\n\t\tbone->frictionScale = 0.5f;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 1.225f * s }, position );\n\t\tbodyDef.linearDamping = 0.0f;\n\t\tbodyDef.name = \"upper_left_arm\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = shirtColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.035f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 1.35f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.1f * B2_PI;\n\t\tjointDef.upperAngle = 0.8f * B2_PI;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\t// lower left arm\n\t{\n\t\tBone* bone = human->bones + bone_lowerLeftArm;\n\t\tbone->parentIndex = bone_upperLeftArm;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 0.975f * s }, position );\n\t\tbodyDef.linearDamping = 0.1f;\n\t\tbodyDef.name = \"lower_left_arm\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\t\tbone->frictionScale = 0.1f;\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = skinColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.03f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 1.1f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameA.q = b2MakeRot(0.25f * B2_PI);\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.2f * B2_PI;\n\t\tjointDef.upperAngle = 0.3f * B2_PI;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\t// upper right arm\n\t{\n\t\tBone* bone = human->bones + bone_upperRightArm;\n\t\tbone->parentIndex = bone_torso;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 1.225f * s }, position );\n\t\tbodyDef.linearDamping = 0.0f;\n\t\tbodyDef.name = \"upper_right_arm\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\t\tbone->frictionScale = 0.5f;\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = shirtColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.035f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 1.35f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.1f * B2_PI;\n\t\tjointDef.upperAngle = 0.8f * B2_PI;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\t// lower right arm\n\t{\n\t\tBone* bone = human->bones + bone_lowerRightArm;\n\t\tbone->parentIndex = bone_upperRightArm;\n\n\t\tbodyDef.position = b2Add( (b2Vec2){ 0.0f, 0.975f * s }, position );\n\t\tbodyDef.linearDamping = 0.1f;\n\t\tbodyDef.name = \"lower_right_arm\";\n\n\t\tbone->bodyId = b2CreateBody( worldId, &bodyDef );\n\t\tbone->frictionScale = 0.1f;\n\n\t\tif ( colorize )\n\t\t{\n\t\t\tshapeDef.material.customColor = skinColor;\n\t\t}\n\n\t\tb2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.03f * s };\n\t\tb2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule );\n\n\t\tb2Vec2 pivot = b2Add( (b2Vec2){ 0.0f, 1.1f * s }, position );\n\t\tb2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();\n\t\tjointDef.base.bodyIdA = human->bones[bone->parentIndex].bodyId;\n\t\tjointDef.base.bodyIdB = bone->bodyId;\n\t\tjointDef.base.localFrameA.p = b2Body_GetLocalPoint( jointDef.base.bodyIdA, pivot );\n\t\tjointDef.base.localFrameA.q = b2MakeRot( 0.25f * B2_PI );\n\t\tjointDef.base.localFrameB.p = b2Body_GetLocalPoint( jointDef.base.bodyIdB, pivot );\n\t\tjointDef.enableLimit = enableLimit;\n\t\tjointDef.lowerAngle = -0.2f * B2_PI;\n\t\tjointDef.upperAngle = 0.3f * B2_PI;\n\t\tjointDef.enableMotor = enableMotor;\n\t\tjointDef.maxMotorTorque = bone->frictionScale * maxTorque;\n\t\tjointDef.enableSpring = hertz > 0.0f;\n\t\tjointDef.hertz = hertz;\n\t\tjointDef.dampingRatio = dampingRatio;\n\t\tjointDef.base.drawScale = drawSize;\n\n\t\tbone->jointId = b2CreateRevoluteJoint( worldId, &jointDef );\n\t}\n\n\thuman->isSpawned = true;\n}\n\nvoid DestroyHuman( Human* human )\n{\n\tassert( human->isSpawned == true );\n\n\tfor ( int i = 0; i < bone_count; ++i )\n\t{\n\t\tif ( B2_IS_NULL( human->bones[i].jointId ) )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2DestroyJoint( human->bones[i].jointId, false );\n\t\thuman->bones[i].jointId = b2_nullJointId;\n\t}\n\n\tfor ( int i = 0; i < bone_count; ++i )\n\t{\n\t\tif ( B2_IS_NULL( human->bones[i].bodyId ) )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2DestroyBody( human->bones[i].bodyId );\n\t\thuman->bones[i].bodyId = b2_nullBodyId;\n\t}\n\n\thuman->isSpawned = false;\n}\n\nvoid Human_SetVelocity( Human* human, b2Vec2 velocity )\n{\n\tfor ( int i = 0; i < bone_count; ++i )\n\t{\n\t\tb2BodyId bodyId = human->bones[i].bodyId;\n\n\t\tif ( B2_IS_NULL( bodyId ) )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2Body_SetLinearVelocity( bodyId, velocity );\n\t}\n}\n\nvoid Human_ApplyRandomAngularImpulse( Human* human, float magnitude )\n{\n\tassert( human->isSpawned == true );\n\tfloat impulse = RandomFloatRange( -magnitude, magnitude );\n\tb2Body_ApplyAngularImpulse( human->bones[bone_torso].bodyId, impulse, true );\n}\n\nvoid Human_SetJointFrictionTorque( Human* human, float torque )\n{\n\tassert( human->isSpawned == true );\n\tif ( torque == 0.0f )\n\t{\n\t\tfor ( int i = 1; i < bone_count; ++i )\n\t\t{\n\t\t\tb2RevoluteJoint_EnableMotor( human->bones[i].jointId, false );\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor ( int i = 1; i < bone_count; ++i )\n\t\t{\n\t\t\tb2RevoluteJoint_EnableMotor( human->bones[i].jointId, true );\n\t\t\tfloat scale = human->scale * human->bones[i].frictionScale;\n\t\t\tb2RevoluteJoint_SetMaxMotorTorque( human->bones[i].jointId, scale * torque );\n\t\t}\n\t}\n}\n\nvoid Human_SetJointSpringHertz( Human* human, float hertz )\n{\n\tassert( human->isSpawned == true );\n\tif ( hertz == 0.0f )\n\t{\n\t\tfor ( int i = 1; i < bone_count; ++i )\n\t\t{\n\t\t\tb2RevoluteJoint_EnableSpring( human->bones[i].jointId, false );\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor ( int i = 1; i < bone_count; ++i )\n\t\t{\n\t\t\tb2RevoluteJoint_EnableSpring( human->bones[i].jointId, true );\n\t\t\tb2RevoluteJoint_SetSpringHertz( human->bones[i].jointId, hertz );\n\t\t}\n\t}\n}\n\nvoid Human_SetJointDampingRatio( Human* human, float dampingRatio )\n{\n\tassert( human->isSpawned == true );\n\tfor ( int i = 1; i < bone_count; ++i )\n\t{\n\t\tb2RevoluteJoint_SetSpringDampingRatio( human->bones[i].jointId, dampingRatio );\n\t}\n}\n\nvoid Human_EnableSensorEvents( Human* human, bool enable )\n{\n\tassert( human->isSpawned == true );\n\tb2BodyId bodyId = human->bones[bone_torso].bodyId;\n\n\tb2ShapeId shapeId;\n\tint count = b2Body_GetShapes( bodyId, &shapeId, 1 );\n\tif ( count == 1 )\n\t{\n\t\tb2Shape_EnableSensorEvents( shapeId, enable );\n\t}\n}\n\nvoid Human_SetScale( Human* human, float scale )\n{\n\tassert( human->isSpawned == true );\n\tassert( 0.01f < scale && scale < 100.0f );\n\tassert( 0.0f < human->scale );\n\n\tfloat ratio = scale / human->scale;\n\n\t// Torque scales by pow(length, 4) due to mass change and length change. However, gravity is also a factor\n\t// so I'm using pow(length, 3)\n\tfloat originalRatio = scale / human->originalScale;\n\tfloat frictionTorque = ( originalRatio * originalRatio * originalRatio ) * human->frictionTorque;\n\n\tb2Vec2 origin = b2Body_GetPosition( human->bones[0].bodyId );\n\n\tfor ( int boneIndex = 0; boneIndex < bone_count; ++boneIndex )\n\t{\n\t\tBone* bone = human->bones + boneIndex;\n\n\t\tif ( boneIndex > 0 )\n\t\t{\n\t\t\tb2Transform transform = b2Body_GetTransform( bone->bodyId );\n\t\t\ttransform.p = b2MulAdd( origin, ratio, b2Sub( transform.p, origin ) );\n\t\t\tb2Body_SetTransform( bone->bodyId, transform.p, transform.q );\n\n\t\t\tb2Transform localFrameA = b2Joint_GetLocalFrameA( bone->jointId );\n\t\t\tb2Transform localFrameB = b2Joint_GetLocalFrameB( bone->jointId );\n\t\t\tlocalFrameA.p = b2MulSV( ratio, localFrameA.p );\n\t\t\tlocalFrameB.p = b2MulSV( ratio, localFrameB.p );\n\t\t\tb2Joint_SetLocalFrameA( bone->jointId, localFrameA );\n\t\t\tb2Joint_SetLocalFrameB( bone->jointId, localFrameB );\n\n\t\t\tb2JointType type = b2Joint_GetType( bone->jointId );\n\t\t\tif ( type == b2_revoluteJoint )\n\t\t\t{\n\t\t\t\tb2RevoluteJoint_SetMaxMotorTorque( bone->jointId, bone->frictionScale * frictionTorque );\n\t\t\t}\n\t\t}\n\n\t\tb2ShapeId shapeIds[2];\n\t\tint shapeCount = b2Body_GetShapes( bone->bodyId, shapeIds, 2 );\n\t\tfor ( int shapeIndex = 0; shapeIndex < shapeCount; ++shapeIndex )\n\t\t{\n\t\t\tb2ShapeType type = b2Shape_GetType( shapeIds[shapeIndex] );\n\t\t\tif ( type == b2_capsuleShape )\n\t\t\t{\n\t\t\t\tb2Capsule capsule = b2Shape_GetCapsule( shapeIds[shapeIndex] );\n\t\t\t\tcapsule.center1 = b2MulSV( ratio, capsule.center1 );\n\t\t\t\tcapsule.center2 = b2MulSV( ratio, capsule.center2 );\n\t\t\t\tcapsule.radius *= ratio;\n\t\t\t\tb2Shape_SetCapsule( shapeIds[shapeIndex], &capsule );\n\t\t\t}\n\t\t\telse if ( type == b2_polygonShape )\n\t\t\t{\n\t\t\t\tb2Polygon polygon = b2Shape_GetPolygon( shapeIds[shapeIndex] );\n\t\t\t\tfor ( int pointIndex = 0; pointIndex < polygon.count; ++pointIndex )\n\t\t\t\t{\n\t\t\t\t\tpolygon.vertices[pointIndex] = b2MulSV( ratio, polygon.vertices[pointIndex] );\n\t\t\t\t}\n\n\t\t\t\tpolygon.centroid = b2MulSV( ratio, polygon.centroid );\n\t\t\t\tpolygon.radius *= ratio;\n\n\t\t\t\tb2Shape_SetPolygon( shapeIds[shapeIndex], &polygon );\n\t\t\t}\n\t\t}\n\n\t\tb2Body_ApplyMassFromShapes( bone->bodyId );\n\t}\n\n\thuman->scale = scale;\n}\n"
  },
  {
    "path": "shared/human.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"box2d/types.h\"\n\ntypedef enum BoneId\n{\n\tbone_hip = 0,\n\tbone_torso = 1,\n\tbone_head = 2,\n\tbone_upperLeftLeg = 3,\n\tbone_lowerLeftLeg = 4,\n\tbone_upperRightLeg = 5,\n\tbone_lowerRightLeg = 6,\n\tbone_upperLeftArm = 7,\n\tbone_lowerLeftArm = 8,\n\tbone_upperRightArm = 9,\n\tbone_lowerRightArm = 10,\n\tbone_count = 11,\n} BoneId;\n\ntypedef struct Bone\n{\n\tb2BodyId bodyId;\n\tb2JointId jointId;\n\tfloat frictionScale;\n\tfloat maxTorque;\n\tint parentIndex;\n} Bone;\n\ntypedef struct Human\n{\n\tBone bones[bone_count];\n\tfloat frictionTorque;\n\tfloat originalScale;\n\tfloat scale;\n\tbool isSpawned;\n} Human;\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\nvoid CreateHuman( Human* human, b2WorldId worldId, b2Vec2 position, float scale, float frictionTorque, float hertz,\n\t\t\t\t  float dampingRatio, int groupIndex, void* userData, bool colorize );\n\nvoid DestroyHuman( Human* human );\n\nvoid Human_SetVelocity( Human* human, b2Vec2 velocity );\nvoid Human_ApplyRandomAngularImpulse( Human* human, float magnitude );\nvoid Human_SetJointFrictionTorque( Human* human, float torque );\nvoid Human_SetJointSpringHertz( Human* human, float hertz );\nvoid Human_SetJointDampingRatio( Human* human, float dampingRatio );\nvoid Human_EnableSensorEvents( Human* human, bool enable );\nvoid Human_SetScale( Human* human, float scale );\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "shared/random.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"random.h\"\n\nuint32_t g_randomSeed = RAND_SEED;\n\nb2Polygon RandomPolygon( float extent )\n{\n\tb2Vec2 points[B2_MAX_POLYGON_VERTICES];\n\tint count = 3 + RandomInt() % 6;\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tpoints[i] = RandomVec2( -extent, extent );\n\t}\n\n\tb2Hull hull = b2ComputeHull( points, count );\n\tif ( hull.count > 0 )\n\t{\n\t\treturn b2MakePolygon( &hull, 0.0f );\n\t}\n\n\treturn b2MakeSquare( extent );\n}\n"
  },
  {
    "path": "shared/random.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#define RAND_LIMIT 32767\n#define RAND_SEED 12345\n\n// Global seed for simple random number generator.\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\nextern uint32_t g_randomSeed;\nb2Polygon RandomPolygon( float extent );\n\n#ifdef __cplusplus\n}\n#endif\n\n// Simple random number generator. Using this instead of rand() for cross-platform determinism.\nB2_INLINE int RandomInt()\n{\n\t// XorShift32 algorithm\n\tuint32_t x = g_randomSeed;\n\tx ^= x << 13;\n\tx ^= x >> 17;\n\tx ^= x << 5;\n\tg_randomSeed = x;\n\n\t// Map the 32-bit value to the range 0 to RAND_LIMIT\n\treturn (int)( x % ( RAND_LIMIT + 1 ) );\n}\n\n// Random integer in range [lo, hi]\nB2_INLINE int RandomIntRange( int lo, int hi )\n{\n\treturn lo + RandomInt() % ( hi - lo + 1 );\n}\n\n// Random number in range [-1,1]\nB2_INLINE float RandomFloat()\n{\n\tfloat r = (float)( RandomInt() & ( RAND_LIMIT ) );\n\tr /= RAND_LIMIT;\n\tr = 2.0f * r - 1.0f;\n\treturn r;\n}\n\n// Random floating point number in range [lo, hi]\nB2_INLINE float RandomFloatRange( float lo, float hi )\n{\n\tfloat r = (float)( RandomInt() & ( RAND_LIMIT ) );\n\tr /= RAND_LIMIT;\n\tr = ( hi - lo ) * r + lo;\n\treturn r;\n}\n\n// Random vector with coordinates in range [lo, hi]\nB2_INLINE b2Vec2 RandomVec2( float lo, float hi )\n{\n\tb2Vec2 v;\n\tv.x = RandomFloatRange( lo, hi );\n\tv.y = RandomFloatRange( lo, hi );\n\treturn v;\n}\n\n// Random rotation with angle in range [-pi, pi]\nB2_INLINE b2Rot RandomRot( void )\n{\n\tfloat angle = RandomFloatRange( -B2_PI, B2_PI );\n\treturn b2MakeRot( angle );\n}\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "include(GNUInstallDirs)\n\nset(BOX2D_SOURCE_FILES\n\taabb.c\n\taabb.h\n\tarena_allocator.c\n\tarena_allocator.h\n\tarray.c\n\tarray.h\n\tatomic.h\n\tbitset.c\n\tbitset.h\n\tbody.c\n\tbody.h\n\tbroad_phase.c\n\tbroad_phase.h\n\tconstants.h\n\tconstraint_graph.c\n\tconstraint_graph.h\n\tcontact.c\n\tcontact.h\n\tcontact_solver.c\n\tcontact_solver.h\n\tcore.c\n\tcore.h\n\tctz.h\n\tdistance.c\n\tdistance_joint.c\n\tdynamic_tree.c\n\tgeometry.c\n\thull.c\n\tid_pool.c\n\tid_pool.h\n\tisland.c\n\tisland.h\n\tjoint.c\n\tjoint.h\n\tmanifold.c\n\tmath_functions.c\n\tmotor_joint.c\n\tmover.c\n\tphysics_world.c\n\tphysics_world.h\n\tprismatic_joint.c\n\trevolute_joint.c\n\tsensor.c\n\tsensor.h\n\tshape.c\n\tshape.h\n\tsolver.c\n\tsolver.h\n\tsolver_set.c\n\tsolver_set.h\n\ttable.c\n\ttable.h\n\ttimer.c\n\ttypes.c\n\tweld_joint.c\n\twheel_joint.c\n)\n\nset(BOX2D_API_FILES\n\t../include/box2d/base.h\n\t../include/box2d/box2d.h\n\t../include/box2d/collision.h\n\t../include/box2d/id.h\n\t../include/box2d/math_functions.h\n\t../include/box2d/types.h\n)\n\n# Hide internal functions\n# https://gcc.gnu.org/wiki/Visibility\nset(CMAKE_C_VISIBILITY_PRESET hidden)\nset(CMAKE_VISIBILITY_INLINES_HIDDEN 1)\n\nadd_library(box2d ${BOX2D_SOURCE_FILES} ${BOX2D_API_FILES})\n\ntarget_include_directories(box2d\n  PUBLIC\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>\n    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>\n  PRIVATE\n    ${CMAKE_CURRENT_SOURCE_DIR}\n)\n\nset(CMAKE_DEBUG_POSTFIX \"d\")\n\n# Box2D uses C17 for _Static_assert and anonymous unions\nset_target_properties(box2d PROPERTIES\n\tC_STANDARD 17\n    C_STANDARD_REQUIRED YES\n    C_EXTENSIONS YES\n    VERSION ${PROJECT_VERSION}\n    SOVERSION ${PROJECT_VERSION_MAJOR}\n\tDEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}\n)\n\nif (BOX2D_COMPILE_WARNING_AS_ERROR)\n\tset_target_properties(box2d PROPERTIES COMPILE_WARNING_AS_ERROR ON)\nendif()\n\nif (BOX2D_PROFILE)\n\ttarget_compile_definitions(box2d PRIVATE BOX2D_PROFILE)\n\n\tFetchContent_Declare(\n\t\ttracy\n\t\tGIT_REPOSITORY https://github.com/wolfpld/tracy.git\n\t\tGIT_TAG v0.11.1\n\t\tGIT_SHALLOW TRUE\n\t\tGIT_PROGRESS TRUE\n\t)\n\tFetchContent_MakeAvailable(tracy)\n\n\ttarget_link_libraries(box2d PUBLIC TracyClient)\nendif()\n\nif (BOX2D_VALIDATE)\n\tmessage(STATUS \"Box2D validation ON\")\t\n\ttarget_compile_definitions(box2d PRIVATE BOX2D_VALIDATE)\nendif()\n\nif (BOX2D_DISABLE_SIMD)\n\tmessage(STATUS \"Box2D SIMD disabled\")\t\n\ttarget_compile_definitions(box2d PRIVATE BOX2D_DISABLE_SIMD)\nendif()\n\nif (MSVC)\n\tmessage(STATUS \"Box2D on MSVC\")\t\n\tif (BUILD_SHARED_LIBS)\n\t\t# this is needed by DLL users to import Box2D symbols\n\t\ttarget_compile_definitions(box2d INTERFACE BOX2D_DLL)\n\tendif()\n\n\t# Visual Studio won't load the natvis unless it is in the project\n\ttarget_sources(box2d PRIVATE box2d.natvis)\n\n\t# Enable asserts in release with debug info\n\ttarget_compile_definitions(box2d PUBLIC \"$<$<CONFIG:RELWITHDEBINFO>:B2_ENABLE_ASSERT>\")\n\n\t# Warnings\n\t# 4710 - warn about inline functions that are not inlined\n\ttarget_compile_options(box2d PRIVATE /Wall /wd4820 /wd5045 /wd4061 /wd4711 /wd4710)\n\n\tif (BOX2D_AVX2)\n\t\tmessage(STATUS \"Box2D using AVX2\")\t\n\t\ttarget_compile_definitions(box2d PRIVATE BOX2D_AVX2)\n\t\ttarget_compile_options(box2d PRIVATE /arch:AVX2)\n\tendif()\n\n\tif (${CMAKE_CXX_COMPILER_ID} STREQUAL \"Clang\")\n\t\ttarget_compile_options(box2d PRIVATE -Wmissing-prototypes)\n\tendif()\n\nelseif (MINGW)\n\tmessage(STATUS \"Box2D on MinGW\")\n\tif (BOX2D_AVX2)\n\t\tmessage(STATUS \"Box2D using AVX2\")\t\n\t\ttarget_compile_definitions(box2d PRIVATE BOX2D_AVX2)\n\t\ttarget_compile_options(box2d PRIVATE -mavx2)\n\telse()\n\tendif()\nelseif (APPLE)\n\tmessage(STATUS \"Box2D on Apple\")\n\ttarget_compile_options(box2d PRIVATE -Wmissing-prototypes -Wall -Wextra -pedantic)\nelseif (EMSCRIPTEN)\n\tmessage(STATUS \"Box2D on Emscripten\")\n\tif (NOT BOX2D_DISABLE_SIMD)\n\t\ttarget_compile_options(box2d PRIVATE -msimd128 -msse2)\n\tendif()\nelseif (UNIX)\n\tmessage(STATUS \"Box2D using Unix\")\n\ttarget_compile_options(box2d PRIVATE -Wmissing-prototypes -Wall -Wextra -pedantic -Wno-unused-value)\n\tif (\"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL \"aarch64\")\n\t\t# raspberry pi\n\t\t# -mfpu=neon\n\t\t# target_compile_options(box2d PRIVATE)\n\telse()\n\t\tif (BOX2D_AVX2)\n\t\t\tmessage(STATUS \"Box2D using AVX2\")\n\t\t\ttarget_compile_definitions(box2d PRIVATE BOX2D_AVX2)\n\t\t\ttarget_compile_options(box2d PRIVATE -mavx2)\n\t\telse()\n\t\tendif()\n\tendif()\nendif()\n\nsource_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"src\" FILES ${BOX2D_SOURCE_FILES})\nsource_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}/../include\" PREFIX \"include\" FILES ${BOX2D_API_FILES})\n\nadd_library(box2d::box2d ALIAS box2d)\n\ninstall(\n  TARGETS box2d\n  EXPORT box2dConfig\n  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}\n  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\n)\n\ninstall(\n  EXPORT box2dConfig\n  NAMESPACE box2d::\n  DESTINATION \"${CMAKE_INSTALL_LIBDIR}/cmake/box2d\"\n)\n\ninstall(\n  FILES ${BOX2D_API_FILES}\n  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/box2d\n)\n\ninclude(CMakePackageConfigHelpers)\n\nwrite_basic_package_version_file(\n  \"${CMAKE_CURRENT_BINARY_DIR}/box2dConfigVersion.cmake\"\n  VERSION ${BOX2D_VERSION}\n  COMPATIBILITY SameMajorVersion\n)\n\ninstall(\n  FILES \"${CMAKE_CURRENT_BINARY_DIR}/box2dConfigVersion.cmake\"\n  DESTINATION \"${CMAKE_INSTALL_LIBDIR}/cmake/box2d\"\n)\n"
  },
  {
    "path": "src/aabb.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"aabb.h\"\n\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n\nbool b2IsValidAABB( b2AABB a )\n{\n\tb2Vec2 d = b2Sub( a.upperBound, a.lowerBound );\n\tbool valid = d.x >= 0.0f && d.y >= 0.0f;\n\tvalid = valid && b2IsValidVec2( a.lowerBound ) && b2IsValidVec2( a.upperBound );\n\treturn valid;\n}\n\n// From Real-time Collision Detection, p179.\nb2CastOutput b2AABB_RayCast( b2AABB a, b2Vec2 p1, b2Vec2 p2 )\n{\n\t// Radius not handled\n\tb2CastOutput output = { 0 };\n\n\tfloat tmin = -FLT_MAX;\n\tfloat tmax = FLT_MAX;\n\n\tb2Vec2 p = p1;\n\tb2Vec2 d = b2Sub( p2, p1 );\n\tb2Vec2 absD = b2Abs( d );\n\n\tb2Vec2 normal = b2Vec2_zero;\n\n\t// x-coordinate\n\tif ( absD.x < FLT_EPSILON )\n\t{\n\t\t// parallel\n\t\tif ( p.x < a.lowerBound.x || a.upperBound.x < p.x )\n\t\t{\n\t\t\treturn output;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfloat inv_d = 1.0f / d.x;\n\t\tfloat t1 = ( a.lowerBound.x - p.x ) * inv_d;\n\t\tfloat t2 = ( a.upperBound.x - p.x ) * inv_d;\n\n\t\t// Sign of the normal vector.\n\t\tfloat s = -1.0f;\n\n\t\tif ( t1 > t2 )\n\t\t{\n\t\t\tfloat tmp = t1;\n\t\t\tt1 = t2;\n\t\t\tt2 = tmp;\n\t\t\ts = 1.0f;\n\t\t}\n\n\t\t// Push the min up\n\t\tif ( t1 > tmin )\n\t\t{\n\t\t\tnormal.y = 0.0f;\n\t\t\tnormal.x = s;\n\t\t\ttmin = t1;\n\t\t}\n\n\t\t// Pull the max down\n\t\ttmax = b2MinFloat( tmax, t2 );\n\n\t\tif ( tmin > tmax )\n\t\t{\n\t\t\treturn output;\n\t\t}\n\t}\n\n\t// y-coordinate\n\tif ( absD.y < FLT_EPSILON )\n\t{\n\t\t// parallel\n\t\tif ( p.y < a.lowerBound.y || a.upperBound.y < p.y )\n\t\t{\n\t\t\treturn output;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfloat inv_d = 1.0f / d.y;\n\t\tfloat t1 = ( a.lowerBound.y - p.y ) * inv_d;\n\t\tfloat t2 = ( a.upperBound.y - p.y ) * inv_d;\n\n\t\t// Sign of the normal vector.\n\t\tfloat s = -1.0f;\n\n\t\tif ( t1 > t2 )\n\t\t{\n\t\t\tfloat tmp = t1;\n\t\t\tt1 = t2;\n\t\t\tt2 = tmp;\n\t\t\ts = 1.0f;\n\t\t}\n\n\t\t// Push the min up\n\t\tif ( t1 > tmin )\n\t\t{\n\t\t\tnormal.x = 0.0f;\n\t\t\tnormal.y = s;\n\t\t\ttmin = t1;\n\t\t}\n\n\t\t// Pull the max down\n\t\ttmax = b2MinFloat( tmax, t2 );\n\n\t\tif ( tmin > tmax )\n\t\t{\n\t\t\treturn output;\n\t\t}\n\t}\n\n\t// Does the ray start inside the box?\n\tif ( tmin < 0.0f )\n\t{\n\t\treturn output;\n\t}\n\n\t// Does the ray intersect beyond the segment length?\n\tif ( 1.0f < tmin )\n\t{\n\t\treturn output;\n\t}\n\n\t// Intersection.\n\toutput.fraction = tmin;\n\toutput.normal = normal;\n\toutput.point = b2Lerp( p1, p2, tmin );\n\toutput.hit = true;\n\treturn output;\n}\n"
  },
  {
    "path": "src/aabb.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"box2d/types.h\"\n\n// Ray cast an AABB\nb2CastOutput b2AABB_RayCast( b2AABB a, b2Vec2 p1, b2Vec2 p2 );\n\n// Get surface area of an AABB (the perimeter length)\nstatic inline float b2Perimeter( b2AABB a )\n{\n\tfloat wx = a.upperBound.x - a.lowerBound.x;\n\tfloat wy = a.upperBound.y - a.lowerBound.y;\n\treturn 2.0f * ( wx + wy );\n}\n\n/// Enlarge a to contain b\n/// @return true if the AABB grew\nstatic inline bool b2EnlargeAABB( b2AABB* a, b2AABB b )\n{\n\tbool changed = false;\n\tif ( b.lowerBound.x < a->lowerBound.x )\n\t{\n\t\ta->lowerBound.x = b.lowerBound.x;\n\t\tchanged = true;\n\t}\n\n\tif ( b.lowerBound.y < a->lowerBound.y )\n\t{\n\t\ta->lowerBound.y = b.lowerBound.y;\n\t\tchanged = true;\n\t}\n\n\tif ( a->upperBound.x < b.upperBound.x )\n\t{\n\t\ta->upperBound.x = b.upperBound.x;\n\t\tchanged = true;\n\t}\n\n\tif ( a->upperBound.y < b.upperBound.y )\n\t{\n\t\ta->upperBound.y = b.upperBound.y;\n\t\tchanged = true;\n\t}\n\n\treturn changed;\n}\n"
  },
  {
    "path": "src/arena_allocator.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"arena_allocator.h\"\n\n#include \"array.h\"\n#include \"core.h\"\n\n#include <stdbool.h>\n#include <stddef.h>\n\nB2_ARRAY_SOURCE( b2ArenaEntry, b2ArenaEntry )\n\nb2ArenaAllocator b2CreateArenaAllocator( int capacity )\n{\n\tB2_ASSERT( capacity >= 0 );\n\tb2ArenaAllocator allocator = { 0 };\n\tallocator.capacity = capacity;\n\tallocator.data = b2Alloc( capacity );\n\tallocator.allocation = 0;\n\tallocator.maxAllocation = 0;\n\tallocator.index = 0;\n\tallocator.entries = b2ArenaEntryArray_Create( 32 );\n\treturn allocator;\n}\n\nvoid b2DestroyArenaAllocator( b2ArenaAllocator* allocator )\n{\n\tb2ArenaEntryArray_Destroy( &allocator->entries );\n\tb2Free( allocator->data, allocator->capacity );\n}\n\nvoid* b2AllocateArenaItem( b2ArenaAllocator* alloc, int size, const char* name )\n{\n\t// ensure allocation is 32 byte aligned to support 256-bit SIMD\n\tint size32 = ( ( size - 1 ) | 0x1F ) + 1;\n\n\tb2ArenaEntry entry;\n\tentry.size = size32;\n\tentry.name = name;\n\tif ( alloc->index + size32 > alloc->capacity )\n\t{\n\t\t// fall back to the heap (undesirable)\n\t\tentry.data = b2Alloc( size32 );\n\t\tentry.usedMalloc = true;\n\n\t\tB2_ASSERT( ( (uintptr_t)entry.data & 0x1F ) == 0 );\n\t}\n\telse\n\t{\n\t\tentry.data = alloc->data + alloc->index;\n\t\tentry.usedMalloc = false;\n\t\talloc->index += size32;\n\n\t\tB2_ASSERT( ( (uintptr_t)entry.data & 0x1F ) == 0 );\n\t}\n\n\talloc->allocation += size32;\n\tif ( alloc->allocation > alloc->maxAllocation )\n\t{\n\t\talloc->maxAllocation = alloc->allocation;\n\t}\n\n\tb2ArenaEntryArray_Push( &alloc->entries, entry );\n\treturn entry.data;\n}\n\nvoid b2FreeArenaItem( b2ArenaAllocator* alloc, void* mem )\n{\n\tint entryCount = alloc->entries.count;\n\tB2_ASSERT( entryCount > 0 );\n\tb2ArenaEntry* entry = alloc->entries.data + ( entryCount - 1 );\n\tB2_ASSERT( mem == entry->data );\n\tif ( entry->usedMalloc )\n\t{\n\t\tb2Free( mem, entry->size );\n\t}\n\telse\n\t{\n\t\talloc->index -= entry->size;\n\t}\n\talloc->allocation -= entry->size;\n\tb2ArenaEntryArray_Pop( &alloc->entries );\n}\n\nvoid b2GrowArena( b2ArenaAllocator* alloc )\n{\n\t// Stack must not be in use\n\tB2_ASSERT( alloc->allocation == 0 );\n\n\tif ( alloc->maxAllocation > alloc->capacity )\n\t{\n\t\tb2Free( alloc->data, alloc->capacity );\n\t\talloc->capacity = alloc->maxAllocation + alloc->maxAllocation / 2;\n\t\talloc->data = b2Alloc( alloc->capacity );\n\t}\n}\n\nint b2GetArenaCapacity( b2ArenaAllocator* alloc )\n{\n\treturn alloc->capacity;\n}\n\nint b2GetArenaAllocation( b2ArenaAllocator* alloc )\n{\n\treturn alloc->allocation;\n}\n\nint b2GetMaxArenaAllocation( b2ArenaAllocator* alloc )\n{\n\treturn alloc->maxAllocation;\n}\n"
  },
  {
    "path": "src/arena_allocator.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n\n#include <stdbool.h>\n\nB2_ARRAY_DECLARE( b2ArenaEntry, b2ArenaEntry );\n\ntypedef struct b2ArenaEntry\n{\n\tchar* data;\n\tconst char* name;\n\tint size;\n\tbool usedMalloc;\n} b2ArenaEntry;\n\n// This is a stack-like arena allocator used for fast per step allocations.\n// You must nest allocate/free pairs. The code will B2_ASSERT\n// if you try to interleave multiple allocate/free pairs.\n// This allocator uses the heap if space is insufficient.\n// I could remove the need to free entries individually.\ntypedef struct b2ArenaAllocator\n{\n\tchar* data;\n\tint capacity;\n\tint index;\n\n\tint allocation;\n\tint maxAllocation;\n\n\tb2ArenaEntryArray entries;\n} b2ArenaAllocator;\n\nb2ArenaAllocator b2CreateArenaAllocator( int capacity );\nvoid b2DestroyArenaAllocator( b2ArenaAllocator* allocator );\n\nvoid* b2AllocateArenaItem( b2ArenaAllocator* alloc, int size, const char* name );\nvoid b2FreeArenaItem( b2ArenaAllocator* alloc, void* mem );\n\n// Grow the arena based on usage\nvoid b2GrowArena( b2ArenaAllocator* alloc );\n\nint b2GetArenaCapacity( b2ArenaAllocator* alloc );\nint b2GetArenaAllocation( b2ArenaAllocator* alloc );\nint b2GetMaxArenaAllocation( b2ArenaAllocator* alloc );\n\nB2_ARRAY_INLINE( b2ArenaEntry, b2ArenaEntry )\n"
  },
  {
    "path": "src/array.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"array.h\"\n\n#include <stddef.h>\n\nB2_ARRAY_SOURCE( int, b2Int )\n"
  },
  {
    "path": "src/array.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"core.h\"\n\n// Macro generated functions for dynamic arrays\n// Pros\n// - type safe\n// - array data debuggable (visible count and capacity)\n// - bounds checking\n// - forward declaration\n// - simple implementation\n// - generates functions (like C++ templates)\n// - functions have https://en.wikipedia.org/wiki/Sequence_point\n// - avoids stretchy buffer dropped pointer update bugs\n// Cons\n// - cannot debug\n// - breaks code navigation\n\n// The fragmentation problem with factor 2:\n// When you double capacity, the new allocation is larger than the sum of all previous allocations:\n//\n// 1st allocation: 8 bytes\n// 2nd allocation: 16 bytes\n// 3rd allocation: 32 bytes (larger than 8 + 16 = 24)\n//\n// This means the memory freed from previous allocations can never be reused for future expansions\n// of the same array. The allocator must always find fresh memory.\n\n// todo_erin consider code-gen: https://github.com/IbrahimHindawi/haikal\n\n// Array declaration that doesn't need the type T to be defined\n#define B2_ARRAY_DECLARE( T, PREFIX )                                                                                            \\\n\ttypedef struct                                                                                                               \\\n\t{                                                                                                                            \\\n\t\tstruct T* data;                                                                                                          \\\n\t\tint count;                                                                                                               \\\n\t\tint capacity;                                                                                                            \\\n\t} PREFIX##Array;                                                                                                             \\\n\tPREFIX##Array PREFIX##Array_Create( int capacity );                                                                          \\\n\tvoid PREFIX##Array_Reserve( PREFIX##Array* a, int newCapacity );                                                             \\\n\tvoid PREFIX##Array_Destroy( PREFIX##Array* a )\n\n#define B2_DECLARE_ARRAY_NATIVE( T, PREFIX )                                                                                     \\\n\ttypedef struct                                                                                                               \\\n\t{                                                                                                                            \\\n\t\tT* data;                                                                                                                 \\\n\t\tint count;                                                                                                               \\\n\t\tint capacity;                                                                                                            \\\n\t} PREFIX##Array;                                                                                                             \\\n\t/* Create array with initial capacity. Zero initialization is also supported */                                              \\\n\tPREFIX##Array PREFIX##Array_Create( int capacity );                                                                          \\\n\tvoid PREFIX##Array_Reserve( PREFIX##Array* a, int newCapacity );                                                             \\\n\tvoid PREFIX##Array_Destroy( PREFIX##Array* a )\n\n// Inline array functions that need the type T to be defined\n#define B2_ARRAY_INLINE( T, PREFIX )                                                                                             \\\n\t/* Resize */                                                                                                                 \\\n\tstatic inline void PREFIX##Array_Resize( PREFIX##Array* a, int count )                                                       \\\n\t{                                                                                                                            \\\n\t\tPREFIX##Array_Reserve( a, count );                                                                                       \\\n\t\ta->count = count;                                                                                                        \\\n\t}                                                                                                                            \\\n\t/* Get */                                                                                                                    \\\n\tstatic inline T* PREFIX##Array_Get( PREFIX##Array* a, int index )                                                            \\\n\t{                                                                                                                            \\\n\t\tB2_ASSERT( 0 <= index && index < a->count );                                                                             \\\n\t\treturn a->data + index;                                                                                                  \\\n\t}                                                                                                                            \\\n\t/* Add */                                                                                                                    \\\n\tstatic inline T* PREFIX##Array_Add( PREFIX##Array* a )                                                                       \\\n\t{                                                                                                                            \\\n\t\tif ( a->count == a->capacity )                                                                                           \\\n\t\t{                                                                                                                        \\\n\t\t\tint newCapacity = a->capacity < 2 ? 2 : a->capacity + ( a->capacity >> 1 );                                          \\\n\t\t\tPREFIX##Array_Reserve( a, newCapacity );                                                                             \\\n\t\t}                                                                                                                        \\\n\t\ta->count += 1;                                                                                                           \\\n\t\treturn a->data + ( a->count - 1 );                                                                                       \\\n\t}                                                                                                                            \\\n\t/* Push */                                                                                                                   \\\n\tstatic inline void PREFIX##Array_Push( PREFIX##Array* a, T value )                                                           \\\n\t{                                                                                                                            \\\n\t\tif ( a->count == a->capacity )                                                                                           \\\n\t\t{                                                                                                                        \\\n\t\t\tint newCapacity = a->capacity < 2 ? 2 : a->capacity + ( a->capacity >> 1 );                                          \\\n\t\t\tPREFIX##Array_Reserve( a, newCapacity );                                                                             \\\n\t\t}                                                                                                                        \\\n\t\ta->data[a->count] = value;                                                                                               \\\n\t\ta->count += 1;                                                                                                           \\\n\t}                                                                                                                            \\\n\t/* Set */                                                                                                                    \\\n\tstatic inline void PREFIX##Array_Set( PREFIX##Array* a, int index, T value )                                                 \\\n\t{                                                                                                                            \\\n\t\tB2_ASSERT( 0 <= index && index < a->count );                                                                             \\\n\t\ta->data[index] = value;                                                                                                  \\\n\t}                                                                                                                            \\\n\t/* RemoveSwap */                                                                                                             \\\n\tstatic inline int PREFIX##Array_RemoveSwap( PREFIX##Array* a, int index )                                                    \\\n\t{                                                                                                                            \\\n\t\tB2_ASSERT( 0 <= index && index < a->count );                                                                             \\\n\t\tint movedIndex = B2_NULL_INDEX;                                                                                          \\\n\t\tif ( index != a->count - 1 )                                                                                             \\\n\t\t{                                                                                                                        \\\n\t\t\tmovedIndex = a->count - 1;                                                                                           \\\n\t\t\ta->data[index] = a->data[movedIndex];                                                                                \\\n\t\t}                                                                                                                        \\\n\t\ta->count -= 1;                                                                                                           \\\n\t\treturn movedIndex;                                                                                                       \\\n\t}                                                                                                                            \\\n\t/* Pop */                                                                                                                    \\\n\tstatic inline T PREFIX##Array_Pop( PREFIX##Array* a )                                                                        \\\n\t{                                                                                                                            \\\n\t\tB2_ASSERT( a->count > 0 );                                                                                               \\\n\t\tT value = a->data[a->count - 1];                                                                                         \\\n\t\ta->count -= 1;                                                                                                           \\\n\t\treturn value;                                                                                                            \\\n\t}                                                                                                                            \\\n\t/* Clear */                                                                                                                  \\\n\tstatic inline void PREFIX##Array_Clear( PREFIX##Array* a )                                                                   \\\n\t{                                                                                                                            \\\n\t\ta->count = 0;                                                                                                            \\\n\t}                                                                                                                            \\\n\t/* ByteCount */                                                                                                              \\\n\tstatic inline int PREFIX##Array_ByteCount( PREFIX##Array* a )                                                                \\\n\t{                                                                                                                            \\\n\t\treturn (int)( a->capacity * sizeof( T ) );                                                                               \\\n\t}\n\n// Array implementations to be instantiated in a source file where the type T is known\n#define B2_ARRAY_SOURCE( T, PREFIX )                                                                                             \\\n\t/* Create */                                                                                                                 \\\n\tPREFIX##Array PREFIX##Array_Create( int capacity )                                                                           \\\n\t{                                                                                                                            \\\n\t\tPREFIX##Array a = { 0 };                                                                                                 \\\n\t\tif ( capacity > 0 )                                                                                                      \\\n\t\t{                                                                                                                        \\\n\t\t\ta.data = b2Alloc( capacity * sizeof( T ) );                                                                          \\\n\t\t\ta.capacity = capacity;                                                                                               \\\n\t\t}                                                                                                                        \\\n\t\treturn a;                                                                                                                \\\n\t}                                                                                                                            \\\n\t/* Reserve */                                                                                                                \\\n\tvoid PREFIX##Array_Reserve( PREFIX##Array* a, int newCapacity )                                                              \\\n\t{                                                                                                                            \\\n\t\tif ( newCapacity <= a->capacity )                                                                                        \\\n\t\t{                                                                                                                        \\\n\t\t\treturn;                                                                                                              \\\n\t\t}                                                                                                                        \\\n\t\ta->data = b2GrowAlloc( a->data, a->capacity * sizeof( T ), newCapacity * sizeof( T ) );                                  \\\n\t\ta->capacity = newCapacity;                                                                                               \\\n\t}                                                                                                                            \\\n\t/* Destroy */                                                                                                                \\\n\tvoid PREFIX##Array_Destroy( PREFIX##Array* a )                                                                               \\\n\t{                                                                                                                            \\\n\t\tb2Free( a->data, a->capacity * sizeof( T ) );                                                                            \\\n\t\ta->data = NULL;                                                                                                          \\\n\t\ta->count = 0;                                                                                                            \\\n\t\ta->capacity = 0;                                                                                                         \\\n\t}\n\nB2_DECLARE_ARRAY_NATIVE( int, b2Int );\nB2_ARRAY_INLINE( int, b2Int )\n\n// Declare all the arrays\nB2_ARRAY_DECLARE( b2Body, b2Body );\nB2_ARRAY_DECLARE( b2BodyMoveEvent, b2BodyMoveEvent );\nB2_ARRAY_DECLARE( b2BodySim, b2BodySim );\nB2_ARRAY_DECLARE( b2BodyState, b2BodyState );\nB2_ARRAY_DECLARE( b2ChainShape, b2ChainShape );\nB2_ARRAY_DECLARE( b2Contact, b2Contact );\nB2_ARRAY_DECLARE( b2ContactBeginTouchEvent, b2ContactBeginTouchEvent );\nB2_ARRAY_DECLARE( b2ContactEndTouchEvent, b2ContactEndTouchEvent );\nB2_ARRAY_DECLARE( b2ContactHitEvent, b2ContactHitEvent );\nB2_ARRAY_DECLARE( b2ContactSim, b2ContactSim );\nB2_ARRAY_DECLARE( b2Island, b2Island );\nB2_ARRAY_DECLARE( b2IslandSim, b2IslandSim );\nB2_ARRAY_DECLARE( b2Joint, b2Joint );\nB2_ARRAY_DECLARE( b2JointEvent, b2JointEvent );\nB2_ARRAY_DECLARE( b2JointSim, b2JointSim );\nB2_ARRAY_DECLARE( b2Sensor, b2Sensor );\nB2_ARRAY_DECLARE( b2SensorBeginTouchEvent, b2SensorBeginTouchEvent );\nB2_ARRAY_DECLARE( b2SensorEndTouchEvent, b2SensorEndTouchEvent );\nB2_ARRAY_DECLARE( b2SensorTaskContext, b2SensorTaskContext );\nB2_ARRAY_DECLARE( b2Shape, b2Shape );\nB2_ARRAY_DECLARE( b2Visitor, b2Visitor );\nB2_ARRAY_DECLARE( b2SolverSet, b2SolverSet );\nB2_ARRAY_DECLARE( b2TaskContext, b2TaskContext );\nB2_ARRAY_DECLARE( b2SensorHit, b2SensorHit );\n"
  },
  {
    "path": "src/atomic.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"core.h\"\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#if defined( _MSC_VER )\n#include <intrin.h>\n#endif\n\nstatic inline void b2AtomicStoreInt( b2AtomicInt* a, int value )\n{\n#if defined( _MSC_VER )\n\t(void)_InterlockedExchange( (long*)&a->value, value );\n#elif defined( __GNUC__ ) || defined( __clang__ )\n\t__atomic_store_n( &a->value, value, __ATOMIC_SEQ_CST );\n#else\n#error \"Unsupported platform\"\n#endif\n}\n\nstatic inline int b2AtomicLoadInt( b2AtomicInt* a )\n{\n#if defined( _MSC_VER )\n\treturn _InterlockedOr( (long*)&a->value, 0 );\n#elif defined( __GNUC__ ) || defined( __clang__ )\n\treturn __atomic_load_n( &a->value, __ATOMIC_SEQ_CST );\n#else\n#error \"Unsupported platform\"\n#endif\n}\n\nstatic inline int b2AtomicFetchAddInt( b2AtomicInt* a, int increment )\n{\n#if defined( _MSC_VER )\n\treturn _InterlockedExchangeAdd( (long*)&a->value, (long)increment );\n#elif defined( __GNUC__ ) || defined( __clang__ )\n\treturn __atomic_fetch_add( &a->value, increment, __ATOMIC_SEQ_CST );\n#else\n#error \"Unsupported platform\"\n#endif\n}\n\nstatic inline bool b2AtomicCompareExchangeInt( b2AtomicInt* a, int expected, int desired )\n{\n#if defined( _MSC_VER )\n\treturn _InterlockedCompareExchange( (long*)&a->value, (long)desired, (long)expected ) == expected;\n#elif defined( __GNUC__ ) || defined( __clang__ )\n\t// The value written to expected is ignored\n\treturn __atomic_compare_exchange_n( &a->value, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST );\n#else\n#error \"Unsupported platform\"\n#endif\n}\n\nstatic inline void b2AtomicStoreU32( b2AtomicU32* a, uint32_t value )\n{\n#if defined( _MSC_VER )\n\t(void)_InterlockedExchange( (long*)&a->value, value );\n#elif defined( __GNUC__ ) || defined( __clang__ )\n\t__atomic_store_n( &a->value, value, __ATOMIC_SEQ_CST );\n#else\n#error \"Unsupported platform\"\n#endif\n}\n\nstatic inline uint32_t b2AtomicLoadU32( b2AtomicU32* a )\n{\n#if defined( _MSC_VER )\n\treturn (uint32_t)_InterlockedOr( (long*)&a->value, 0 );\n#elif defined( __GNUC__ ) || defined( __clang__ )\n\treturn __atomic_load_n( &a->value, __ATOMIC_SEQ_CST );\n#else\n#error \"Unsupported platform\"\n#endif\n}\n"
  },
  {
    "path": "src/bitset.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"bitset.h\"\n\n#include <string.h>\n\nb2BitSet b2CreateBitSet( uint32_t bitCapacity )\n{\n\tb2BitSet bitSet = { 0 };\n\n\tbitSet.blockCapacity = ( bitCapacity + sizeof( uint64_t ) * 8 - 1 ) / ( sizeof( uint64_t ) * 8 );\n\tbitSet.blockCount = 0;\n\tbitSet.bits = b2Alloc( bitSet.blockCapacity * sizeof( uint64_t ) );\n\tmemset( bitSet.bits, 0, bitSet.blockCapacity * sizeof( uint64_t ) );\n\treturn bitSet;\n}\n\nvoid b2DestroyBitSet( b2BitSet* bitSet )\n{\n\tb2Free( bitSet->bits, bitSet->blockCapacity * sizeof( uint64_t ) );\n\tbitSet->blockCapacity = 0;\n\tbitSet->blockCount = 0;\n\tbitSet->bits = NULL;\n}\n\nvoid b2SetBitCountAndClear( b2BitSet* bitSet, uint32_t bitCount )\n{\n\tuint32_t blockCount = ( bitCount + sizeof( uint64_t ) * 8 - 1 ) / ( sizeof( uint64_t ) * 8 );\n\tif ( bitSet->blockCapacity < blockCount )\n\t{\n\t\tb2DestroyBitSet( bitSet );\n\t\tuint32_t newBitCapacity = bitCount + ( bitCount >> 1 );\n\t\t*bitSet = b2CreateBitSet( newBitCapacity );\n\t}\n\n\tbitSet->blockCount = blockCount;\n\tmemset( bitSet->bits, 0, bitSet->blockCount * sizeof( uint64_t ) );\n}\n\nvoid b2GrowBitSet( b2BitSet* bitSet, uint32_t blockCount )\n{\n\tB2_ASSERT( blockCount > bitSet->blockCount );\n\tif ( blockCount > bitSet->blockCapacity )\n\t{\n\t\tuint32_t oldCapacity = bitSet->blockCapacity;\n\t\tbitSet->blockCapacity = blockCount + blockCount / 2;\n\t\tuint64_t* newBits = b2Alloc( bitSet->blockCapacity * sizeof( uint64_t ) );\n\t\tmemset( newBits, 0, bitSet->blockCapacity * sizeof( uint64_t ) );\n\t\tB2_ASSERT( bitSet->bits != NULL );\n\t\tmemcpy( newBits, bitSet->bits, oldCapacity * sizeof( uint64_t ) );\n\t\tb2Free( bitSet->bits, oldCapacity * sizeof( uint64_t ) );\n\t\tbitSet->bits = newBits;\n\t}\n\n\tbitSet->blockCount = blockCount;\n}\n\nvoid b2InPlaceUnion( b2BitSet* B2_RESTRICT setA, const b2BitSet* B2_RESTRICT setB )\n{\n\tB2_ASSERT( setA->blockCount == setB->blockCount );\n\tuint32_t blockCount = setA->blockCount;\n\tfor ( uint32_t i = 0; i < blockCount; ++i )\n\t{\n\t\tsetA->bits[i] |= setB->bits[i];\n\t}\n}\n"
  },
  {
    "path": "src/bitset.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"core.h\"\n\n#include <stdbool.h>\n#include <stdint.h>\n\n// Bit set provides fast operations on large arrays of bits.\ntypedef struct b2BitSet\n{\n\tuint64_t* bits;\n\tuint32_t blockCapacity;\n\tuint32_t blockCount;\n} b2BitSet;\n\nb2BitSet b2CreateBitSet( uint32_t bitCapacity );\nvoid b2DestroyBitSet( b2BitSet* bitSet );\nvoid b2SetBitCountAndClear( b2BitSet* bitSet, uint32_t bitCount );\nvoid b2InPlaceUnion( b2BitSet* setA, const b2BitSet* setB );\nvoid b2GrowBitSet( b2BitSet* bitSet, uint32_t blockCount );\nint b2CountSetBits( b2BitSet* bitSet );\n\nstatic inline void b2SetBit( b2BitSet* bitSet, uint32_t bitIndex )\n{\n\tuint32_t blockIndex = bitIndex / 64;\n\tB2_ASSERT( blockIndex < bitSet->blockCount );\n\tbitSet->bits[blockIndex] |= ( (uint64_t)1 << bitIndex % 64 );\n}\n\nstatic inline void b2SetBitGrow( b2BitSet* bitSet, uint32_t bitIndex )\n{\n\tuint32_t blockIndex = bitIndex / 64;\n\tif ( blockIndex >= bitSet->blockCount )\n\t{\n\t\tb2GrowBitSet( bitSet, blockIndex + 1 );\n\t}\n\tbitSet->bits[blockIndex] |= ( (uint64_t)1 << bitIndex % 64 );\n}\n\nstatic inline void b2ClearBit( b2BitSet* bitSet, uint32_t bitIndex )\n{\n\tuint32_t blockIndex = bitIndex / 64;\n\tif ( blockIndex >= bitSet->blockCount )\n\t{\n\t\treturn;\n\t}\n\tbitSet->bits[blockIndex] &= ~( (uint64_t)1 << bitIndex % 64 );\n}\n\nstatic inline bool b2GetBit( const b2BitSet* bitSet, uint32_t bitIndex )\n{\n\tuint32_t blockIndex = bitIndex / 64;\n\tif ( blockIndex >= bitSet->blockCount )\n\t{\n\t\treturn false;\n\t}\n\treturn ( bitSet->bits[blockIndex] & ( (uint64_t)1 << bitIndex % 64 ) ) != 0;\n}\n\nstatic inline int b2GetBitSetBytes( b2BitSet* bitSet )\n{\n\treturn bitSet->blockCapacity * sizeof( uint64_t );\n}\n"
  },
  {
    "path": "src/body.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"body.h\"\n\n#include \"aabb.h\"\n#include \"array.h\"\n#include \"contact.h\"\n#include \"core.h\"\n#include \"id_pool.h\"\n#include \"island.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"sensor.h\"\n#include \"shape.h\"\n#include \"solver_set.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/id.h\"\n\n#include <string.h>\n\n// Implement functions for b2BodyArray\nB2_ARRAY_SOURCE( b2Body, b2Body )\nB2_ARRAY_SOURCE( b2BodySim, b2BodySim )\nB2_ARRAY_SOURCE( b2BodyState, b2BodyState )\n\nstatic void b2LimitVelocity( b2BodyState* state, float maxLinearSpeed )\n{\n\tfloat v2 = b2LengthSquared( state->linearVelocity );\n\tif ( v2 > maxLinearSpeed * maxLinearSpeed )\n\t{\n\t\tstate->linearVelocity = b2MulSV( maxLinearSpeed / sqrtf( v2 ), state->linearVelocity );\n\t}\n}\n\n// Get a validated body from a world using an id.\nb2Body* b2GetBodyFullId( b2World* world, b2BodyId bodyId )\n{\n\tB2_ASSERT( b2Body_IsValid( bodyId ) );\n\n\t// id index starts at one so that zero can represent null\n\treturn b2BodyArray_Get( &world->bodies, bodyId.index1 - 1 );\n}\n\nb2Transform b2GetBodyTransformQuick( b2World* world, b2Body* body )\n{\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );\n\tb2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex );\n\treturn bodySim->transform;\n}\n\nb2Transform b2GetBodyTransform( b2World* world, int bodyId )\n{\n\tb2Body* body = b2BodyArray_Get( &world->bodies, bodyId );\n\treturn b2GetBodyTransformQuick( world, body );\n}\n\n// Create a b2BodyId from a raw id.\nb2BodyId b2MakeBodyId( b2World* world, int bodyId )\n{\n\tb2Body* body = b2BodyArray_Get( &world->bodies, bodyId );\n\treturn (b2BodyId){ bodyId + 1, world->worldId, body->generation };\n}\n\nb2BodySim* b2GetBodySim( b2World* world, b2Body* body )\n{\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );\n\tb2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex );\n\treturn bodySim;\n}\n\nb2BodyState* b2GetBodyState( b2World* world, b2Body* body )\n{\n\tif ( body->setIndex == b2_awakeSet )\n\t{\n\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\t\treturn b2BodyStateArray_Get( &set->bodyStates, body->localIndex );\n\t}\n\n\treturn NULL;\n}\n\nstatic void b2CreateIslandForBody( b2World* world, int setIndex, b2Body* body )\n{\n\tB2_ASSERT( body->islandId == B2_NULL_INDEX );\n\tB2_ASSERT( body->islandPrev == B2_NULL_INDEX );\n\tB2_ASSERT( body->islandNext == B2_NULL_INDEX );\n\tB2_ASSERT( setIndex != b2_disabledSet );\n\n\tb2Island* island = b2CreateIsland( world, setIndex );\n\n\tbody->islandId = island->islandId;\n\tisland->headBody = body->id;\n\tisland->tailBody = body->id;\n\tisland->bodyCount = 1;\n}\n\nstatic void b2RemoveBodyFromIsland( b2World* world, b2Body* body )\n{\n\tif ( body->islandId == B2_NULL_INDEX )\n\t{\n\t\tB2_ASSERT( body->islandPrev == B2_NULL_INDEX );\n\t\tB2_ASSERT( body->islandNext == B2_NULL_INDEX );\n\t\treturn;\n\t}\n\n\tint islandId = body->islandId;\n\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\n\t// Fix the island's linked list of sims\n\tif ( body->islandPrev != B2_NULL_INDEX )\n\t{\n\t\tb2Body* prevBody = b2BodyArray_Get( &world->bodies, body->islandPrev );\n\t\tprevBody->islandNext = body->islandNext;\n\t}\n\n\tif ( body->islandNext != B2_NULL_INDEX )\n\t{\n\t\tb2Body* nextBody = b2BodyArray_Get( &world->bodies, body->islandNext );\n\t\tnextBody->islandPrev = body->islandPrev;\n\t}\n\n\tB2_ASSERT( island->bodyCount > 0 );\n\tisland->bodyCount -= 1;\n\tbool islandDestroyed = false;\n\n\tif ( island->headBody == body->id )\n\t{\n\t\tisland->headBody = body->islandNext;\n\n\t\tif ( island->headBody == B2_NULL_INDEX )\n\t\t{\n\t\t\t// Destroy empty island\n\t\t\tB2_ASSERT( island->tailBody == body->id );\n\t\t\tB2_ASSERT( island->bodyCount == 0 );\n\t\t\tB2_ASSERT( island->contactCount == 0 );\n\t\t\tB2_ASSERT( island->jointCount == 0 );\n\n\t\t\t// Free the island\n\t\t\tb2DestroyIsland( world, island->islandId );\n\t\t\tislandDestroyed = true;\n\t\t}\n\t}\n\telse if ( island->tailBody == body->id )\n\t{\n\t\tisland->tailBody = body->islandPrev;\n\t}\n\n\tif ( islandDestroyed == false )\n\t{\n\t\tb2ValidateIsland( world, islandId );\n\t}\n\n\tbody->islandId = B2_NULL_INDEX;\n\tbody->islandPrev = B2_NULL_INDEX;\n\tbody->islandNext = B2_NULL_INDEX;\n}\n\nstatic void b2DestroyBodyContacts( b2World* world, b2Body* body, bool wakeBodies )\n{\n\t// Destroy the attached contacts\n\tint edgeKey = body->headContactKey;\n\twhile ( edgeKey != B2_NULL_INDEX )\n\t{\n\t\tint contactId = edgeKey >> 1;\n\t\tint edgeIndex = edgeKey & 1;\n\n\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\tedgeKey = contact->edges[edgeIndex].nextKey;\n\t\tb2DestroyContact( world, contact, wakeBodies );\n\t}\n\n\tb2ValidateSolverSets( world );\n}\n\nb2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def )\n{\n\tB2_CHECK_DEF( def );\n\tB2_ASSERT( b2IsValidVec2( def->position ) );\n\tB2_ASSERT( b2IsValidRotation( def->rotation ) );\n\tB2_ASSERT( b2IsValidVec2( def->linearVelocity ) );\n\tB2_ASSERT( b2IsValidFloat( def->angularVelocity ) );\n\tB2_ASSERT( b2IsValidFloat( def->linearDamping ) && def->linearDamping >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( def->angularDamping ) && def->angularDamping >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( def->sleepThreshold ) && def->sleepThreshold >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( def->gravityScale ) );\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\n\tif ( world->locked )\n\t{\n\t\treturn b2_nullBodyId;\n\t}\n\n\tbool isAwake = ( def->isAwake || def->enableSleep == false ) && def->isEnabled;\n\n\t// determine the solver set\n\tint setId;\n\tif ( def->isEnabled == false )\n\t{\n\t\t// any body type can be disabled\n\t\tsetId = b2_disabledSet;\n\t}\n\telse if ( def->type == b2_staticBody )\n\t{\n\t\tsetId = b2_staticSet;\n\t}\n\telse if ( isAwake == true )\n\t{\n\t\tsetId = b2_awakeSet;\n\t}\n\telse\n\t{\n\t\t// new set for a sleeping body in its own island\n\t\tsetId = b2AllocId( &world->solverSetIdPool );\n\t\tif ( setId == world->solverSets.count )\n\t\t{\n\t\t\t// Create a zero initialized solver set. All sub-arrays are also zero initialized.\n\t\t\tb2SolverSetArray_Push( &world->solverSets, (b2SolverSet){ 0 } );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tB2_ASSERT( world->solverSets.data[setId].setIndex == B2_NULL_INDEX );\n\t\t}\n\n\t\tworld->solverSets.data[setId].setIndex = setId;\n\t}\n\n\tB2_ASSERT( 0 <= setId && setId < world->solverSets.count );\n\n\tint bodyId = b2AllocId( &world->bodyIdPool );\n\n\tuint32_t lockFlags = 0;\n\tlockFlags |= def->motionLocks.linearX ? b2_lockLinearX : 0;\n\tlockFlags |= def->motionLocks.linearY ? b2_lockLinearY : 0;\n\tlockFlags |= def->motionLocks.angularZ ? b2_lockAngularZ : 0;\n\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setId );\n\tb2BodySim* bodySim = b2BodySimArray_Add( &set->bodySims );\n\t*bodySim = (b2BodySim){ 0 };\n\tbodySim->transform.p = def->position;\n\tbodySim->transform.q = def->rotation;\n\tbodySim->center = def->position;\n\tbodySim->rotation0 = bodySim->transform.q;\n\tbodySim->center0 = bodySim->center;\n\tbodySim->minExtent = B2_HUGE;\n\tbodySim->maxExtent = 0.0f;\n\tbodySim->linearDamping = def->linearDamping;\n\tbodySim->angularDamping = def->angularDamping;\n\tbodySim->gravityScale = def->gravityScale;\n\tbodySim->bodyId = bodyId;\n\tbodySim->flags = lockFlags;\n\tbodySim->flags |= def->isBullet ? b2_isBullet : 0;\n\tbodySim->flags |= def->allowFastRotation ? b2_allowFastRotation : 0;\n\tbodySim->flags |= def->type == b2_dynamicBody ? b2_dynamicFlag : 0;\n\n\tif ( setId == b2_awakeSet )\n\t{\n\t\tb2BodyState* bodyState = b2BodyStateArray_Add( &set->bodyStates );\n\t\tB2_ASSERT( ( (uintptr_t)bodyState & 0x1F ) == 0 );\n\n\t\t*bodyState = (b2BodyState){ 0 };\n\t\tbodyState->linearVelocity = def->linearVelocity;\n\t\tbodyState->angularVelocity = def->angularVelocity;\n\t\tbodyState->deltaRotation = b2Rot_identity;\n\t\tbodyState->flags = bodySim->flags;\n\t}\n\n\tif ( bodyId == world->bodies.count )\n\t{\n\t\tb2BodyArray_Push( &world->bodies, (b2Body){ 0 } );\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( world->bodies.data[bodyId].id == B2_NULL_INDEX );\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, bodyId );\n\n\tif ( def->name )\n\t{\n\t\tint i = 0;\n\t\twhile ( i < B2_NAME_LENGTH - 1 && def->name[i] != 0 )\n\t\t{\n\t\t\tbody->name[i] = def->name[i];\n\t\t\ti += 1;\n\t\t}\n\n\t\twhile ( i < B2_NAME_LENGTH )\n\t\t{\n\t\t\tbody->name[i] = 0;\n\t\t\ti += 1;\n\t\t}\n\t}\n\telse\n\t{\n\t\tmemset( body->name, 0, B2_NAME_LENGTH * sizeof( char ) );\n\t}\n\n\tbody->userData = def->userData;\n\tbody->setIndex = setId;\n\tbody->localIndex = set->bodySims.count - 1;\n\tbody->generation += 1;\n\tbody->headShapeId = B2_NULL_INDEX;\n\tbody->shapeCount = 0;\n\tbody->headChainId = B2_NULL_INDEX;\n\tbody->headContactKey = B2_NULL_INDEX;\n\tbody->contactCount = 0;\n\tbody->headJointKey = B2_NULL_INDEX;\n\tbody->jointCount = 0;\n\tbody->islandId = B2_NULL_INDEX;\n\tbody->islandPrev = B2_NULL_INDEX;\n\tbody->islandNext = B2_NULL_INDEX;\n\tbody->bodyMoveIndex = B2_NULL_INDEX;\n\tbody->id = bodyId;\n\tbody->mass = 0.0f;\n\tbody->inertia = 0.0f;\n\tbody->sleepThreshold = def->sleepThreshold;\n\tbody->sleepTime = 0.0f;\n\tbody->type = def->type;\n\tbody->flags = bodySim->flags;\n\tbody->enableSleep = def->enableSleep;\n\n\t// dynamic and kinematic bodies that are enabled need a island\n\tif ( setId >= b2_awakeSet )\n\t{\n\t\tb2CreateIslandForBody( world, setId, body );\n\t}\n\n\tb2ValidateSolverSets( world );\n\n\tb2BodyId id = { bodyId + 1, world->worldId, body->generation };\n\treturn id;\n}\n\nbool b2WakeBody( b2World* world, b2Body* body )\n{\n\tif ( body->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeSolverSet( world, body->setIndex );\n\t\tb2ValidateSolverSets( world );\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid b2DestroyBody( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\t// Wake bodies attached to this body, even if this body is static.\n\tbool wakeBodies = true;\n\n\t// Destroy the attached joints\n\tint edgeKey = body->headJointKey;\n\twhile ( edgeKey != B2_NULL_INDEX )\n\t{\n\t\tint jointId = edgeKey >> 1;\n\t\tint edgeIndex = edgeKey & 1;\n\n\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\tedgeKey = joint->edges[edgeIndex].nextKey;\n\n\t\t// Careful because this modifies the list being traversed\n\t\tb2DestroyJointInternal( world, joint, wakeBodies );\n\t}\n\n\t// Destroy all contacts attached to this body.\n\tb2DestroyBodyContacts( world, body, wakeBodies );\n\n\t// Destroy the attached shapes and their broad-phase proxies.\n\tint shapeId = body->headShapeId;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\t\tif ( shape->sensorIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2DestroySensor( world, shape );\n\t\t}\n\n\t\tb2DestroyShapeProxy( shape, &world->broadPhase );\n\n\t\t// Return shape to free list.\n\t\tb2FreeId( &world->shapeIdPool, shapeId );\n\t\tshape->id = B2_NULL_INDEX;\n\n\t\tshapeId = shape->nextShapeId;\n\t}\n\n\t// Destroy the attached chains. The associated shapes have already been destroyed above.\n\tint chainId = body->headChainId;\n\twhile ( chainId != B2_NULL_INDEX )\n\t{\n\t\tb2ChainShape* chain = b2ChainShapeArray_Get( &world->chainShapes, chainId );\n\n\t\tb2FreeChainData( chain );\n\n\t\t// Return chain to free list.\n\t\tb2FreeId( &world->chainIdPool, chainId );\n\t\tchain->id = B2_NULL_INDEX;\n\n\t\tchainId = chain->nextChainId;\n\t}\n\n\tb2RemoveBodyFromIsland( world, body );\n\n\t// Remove body sim from solver set that owns it\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );\n\tint movedIndex = b2BodySimArray_RemoveSwap( &set->bodySims, body->localIndex );\n\tif ( movedIndex != B2_NULL_INDEX )\n\t{\n\t\t// Fix moved body index\n\t\tb2BodySim* movedSim = set->bodySims.data + body->localIndex;\n\t\tint movedId = movedSim->bodyId;\n\t\tb2Body* movedBody = b2BodyArray_Get( &world->bodies, movedId );\n\t\tB2_ASSERT( movedBody->localIndex == movedIndex );\n\t\tmovedBody->localIndex = body->localIndex;\n\t}\n\n\t// Remove body state from awake set\n\tif ( body->setIndex == b2_awakeSet )\n\t{\n\t\tint result = b2BodyStateArray_RemoveSwap( &set->bodyStates, body->localIndex );\n\t\tB2_ASSERT( result == movedIndex );\n\t\tB2_UNUSED( result );\n\t}\n\telse if ( set->setIndex >= b2_firstSleepingSet && set->bodySims.count == 0 )\n\t{\n\t\t// Remove solver set if it's now an orphan.\n\t\tb2DestroySolverSet( world, set->setIndex );\n\t}\n\n\t// Free body and id (preserve body generation)\n\tb2FreeId( &world->bodyIdPool, body->id );\n\n\tbody->setIndex = B2_NULL_INDEX;\n\tbody->localIndex = B2_NULL_INDEX;\n\tbody->id = B2_NULL_INDEX;\n\n\tb2ValidateSolverSets( world );\n}\n\nint b2Body_GetContactCapacity( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\t// Conservative and fast\n\treturn body->contactCount;\n}\n\nint b2Body_GetContactData( b2BodyId bodyId, b2ContactData* contactData, int capacity )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tint contactKey = body->headContactKey;\n\tint index = 0;\n\twhile ( contactKey != B2_NULL_INDEX && index < capacity )\n\t{\n\t\tint contactId = contactKey >> 1;\n\t\tint edgeIndex = contactKey & 1;\n\n\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\n\t\t// Is contact touching?\n\t\tif ( contact->flags & b2_contactTouchingFlag )\n\t\t{\n\t\t\tb2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contact->shapeIdA );\n\t\t\tb2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contact->shapeIdB );\n\n\t\t\tcontactData[index].contactId = (b2ContactId){ contact->contactId + 1, bodyId.world0, 0, contact->generation };\n\t\t\tcontactData[index].shapeIdA = (b2ShapeId){ shapeA->id + 1, bodyId.world0, shapeA->generation };\n\t\t\tcontactData[index].shapeIdB = (b2ShapeId){ shapeB->id + 1, bodyId.world0, shapeB->generation };\n\n\t\t\tb2ContactSim* contactSim = b2GetContactSim( world, contact );\n\t\t\tcontactData[index].manifold = contactSim->manifold;\n\n\t\t\tindex += 1;\n\t\t}\n\n\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\t}\n\n\tB2_ASSERT( index <= capacity );\n\n\treturn index;\n}\n\nb2AABB b2Body_ComputeAABB( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn (b2AABB){ 0 };\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tif ( body->headShapeId == B2_NULL_INDEX )\n\t{\n\t\tb2Transform transform = b2GetBodyTransform( world, body->id );\n\t\treturn (b2AABB){ transform.p, transform.p };\n\t}\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, body->headShapeId );\n\tb2AABB aabb = shape->aabb;\n\twhile ( shape->nextShapeId != B2_NULL_INDEX )\n\t{\n\t\tshape = b2ShapeArray_Get( &world->shapes, shape->nextShapeId );\n\t\taabb = b2AABB_Union( aabb, shape->aabb );\n\t}\n\n\treturn aabb;\n}\n\nvoid b2UpdateBodyMassData( b2World* world, b2Body* body )\n{\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\n\t// Mass is no longer dirty\n\tbody->flags &= ~b2_dirtyMass;\n\n\t// Compute mass data from shapes. Each shape has its own density.\n\tbody->mass = 0.0f;\n\tbody->inertia = 0.0f;\n\n\tbodySim->invMass = 0.0f;\n\tbodySim->invInertia = 0.0f;\n\tbodySim->localCenter = b2Vec2_zero;\n\tbodySim->minExtent = B2_HUGE;\n\tbodySim->maxExtent = 0.0f;\n\n\t// Static and kinematic sims have zero mass.\n\tif ( body->type != b2_dynamicBody )\n\t{\n\t\tbodySim->center = bodySim->transform.p;\n\t\tbodySim->center0 = bodySim->center;\n\n\t\t// Need extents for kinematic bodies for sleeping to work correctly.\n\t\tif ( body->type == b2_kinematicBody )\n\t\t{\n\t\t\tint shapeId = body->headShapeId;\n\t\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tconst b2Shape* s = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\t\t\t\tb2ShapeExtent extent = b2ComputeShapeExtent( s, b2Vec2_zero );\n\t\t\t\tbodySim->minExtent = b2MinFloat( bodySim->minExtent, extent.minExtent );\n\t\t\t\tbodySim->maxExtent = b2MaxFloat( bodySim->maxExtent, extent.maxExtent );\n\n\t\t\t\tshapeId = s->nextShapeId;\n\t\t\t}\n\t\t}\n\n\t\treturn;\n\t}\n\n\tint shapeCount = body->shapeCount;\n\tb2MassData* masses = b2AllocateArenaItem( &world->arena, shapeCount * sizeof( b2MassData ), \"mass data\" );\n\n\t// Accumulate mass over all shapes.\n\tb2Vec2 localCenter = b2Vec2_zero;\n\tint shapeId = body->headShapeId;\n\tint shapeIndex = 0;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tconst b2Shape* s = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tshapeId = s->nextShapeId;\n\n\t\tif ( s->density == 0.0f )\n\t\t{\n\t\t\tmasses[shapeIndex] = (b2MassData){ 0 };\n\t\t\tshapeIndex += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2MassData massData = b2ComputeShapeMass( s );\n\t\tbody->mass += massData.mass;\n\t\tlocalCenter = b2MulAdd( localCenter, massData.mass, massData.center );\n\n\t\tmasses[shapeIndex] = massData;\n\t\tshapeIndex += 1;\n\t}\n\n\t// Compute center of mass.\n\tif ( body->mass > 0.0f )\n\t{\n\t\tbodySim->invMass = 1.0f / body->mass;\n\t\tlocalCenter = b2MulSV( bodySim->invMass, localCenter );\n\t}\n\n\t// Second loop to accumulate the rotational inertia about the center of mass\n\tfor ( shapeIndex = 0; shapeIndex < shapeCount; ++shapeIndex )\n\t{\n\t\tb2MassData massData = masses[shapeIndex];\n\t\tif ( massData.mass == 0.0f )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Shift to center of mass. This is safe because it can only increase.\n\t\tb2Vec2 offset = b2Sub( localCenter, massData.center );\n\t\tfloat inertia = massData.rotationalInertia + massData.mass * b2Dot( offset, offset );\n\t\tbody->inertia += inertia;\n\t}\n\n\tb2FreeArenaItem( &world->arena, masses );\n\tmasses = NULL;\n\n\tB2_ASSERT( body->inertia >= 0.0f );\n\n\tif ( body->inertia > 0.0f )\n\t{\n\t\tbodySim->invInertia = 1.0f / body->inertia;\n\t}\n\telse\n\t{\n\t\tbody->inertia = 0.0f;\n\t\tbodySim->invInertia = 0.0f;\n\t}\n\n\t// Move center of mass.\n\tb2Vec2 oldCenter = bodySim->center;\n\tbodySim->localCenter = localCenter;\n\tbodySim->center = b2TransformPoint( bodySim->transform, bodySim->localCenter );\n\tbodySim->center0 = bodySim->center;\n\n\t// Update center of mass velocity\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tif ( state != NULL )\n\t{\n\t\tb2Vec2 deltaLinear = b2CrossSV( state->angularVelocity, b2Sub( bodySim->center, oldCenter ) );\n\t\tstate->linearVelocity = b2Add( state->linearVelocity, deltaLinear );\n\t}\n\n\t// Compute body extents relative to center of mass\n\tshapeId = body->headShapeId;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tconst b2Shape* s = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\t\tb2ShapeExtent extent = b2ComputeShapeExtent( s, localCenter );\n\t\tbodySim->minExtent = b2MinFloat( bodySim->minExtent, extent.minExtent );\n\t\tbodySim->maxExtent = b2MaxFloat( bodySim->maxExtent, extent.maxExtent );\n\n\t\tshapeId = s->nextShapeId;\n\t}\n}\n\nb2Vec2 b2Body_GetPosition( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\treturn transform.p;\n}\n\nb2Rot b2Body_GetRotation( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\treturn transform.q;\n}\n\nb2Transform b2Body_GetTransform( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn b2GetBodyTransformQuick( world, body );\n}\n\nb2Vec2 b2Body_GetLocalPoint( b2BodyId bodyId, b2Vec2 worldPoint )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\treturn b2InvTransformPoint( transform, worldPoint );\n}\n\nb2Vec2 b2Body_GetWorldPoint( b2BodyId bodyId, b2Vec2 localPoint )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\treturn b2TransformPoint( transform, localPoint );\n}\n\nb2Vec2 b2Body_GetLocalVector( b2BodyId bodyId, b2Vec2 worldVector )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\treturn b2InvRotateVector( transform.q, worldVector );\n}\n\nb2Vec2 b2Body_GetWorldVector( b2BodyId bodyId, b2Vec2 localVector )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\treturn b2RotateVector( transform.q, localVector );\n}\n\nvoid b2Body_SetTransform( b2BodyId bodyId, b2Vec2 position, b2Rot rotation )\n{\n\tB2_ASSERT( b2IsValidVec2( position ) );\n\tB2_ASSERT( b2IsValidRotation( rotation ) );\n\tB2_ASSERT( b2Body_IsValid( bodyId ) );\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tB2_ASSERT( world->locked == false );\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\n\tbodySim->transform.p = position;\n\tbodySim->transform.q = rotation;\n\tbodySim->center = b2TransformPoint( bodySim->transform, bodySim->localCenter );\n\n\tbodySim->rotation0 = bodySim->transform.q;\n\tbodySim->center0 = bodySim->center;\n\n\tb2BroadPhase* broadPhase = &world->broadPhase;\n\n\tb2Transform transform = bodySim->transform;\n\tconst float margin = B2_AABB_MARGIN;\n\tconst float speculativeDistance = B2_SPECULATIVE_DISTANCE;\n\n\tint shapeId = body->headShapeId;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tb2AABB aabb = b2ComputeShapeAABB( shape, transform );\n\t\taabb.lowerBound.x -= speculativeDistance;\n\t\taabb.lowerBound.y -= speculativeDistance;\n\t\taabb.upperBound.x += speculativeDistance;\n\t\taabb.upperBound.y += speculativeDistance;\n\t\tshape->aabb = aabb;\n\n\t\tif ( b2AABB_Contains( shape->fatAABB, aabb ) == false )\n\t\t{\n\t\t\tb2AABB fatAABB;\n\t\t\tfatAABB.lowerBound.x = aabb.lowerBound.x - margin;\n\t\t\tfatAABB.lowerBound.y = aabb.lowerBound.y - margin;\n\t\t\tfatAABB.upperBound.x = aabb.upperBound.x + margin;\n\t\t\tfatAABB.upperBound.y = aabb.upperBound.y + margin;\n\t\t\tshape->fatAABB = fatAABB;\n\n\t\t\t// They body could be disabled\n\t\t\tif ( shape->proxyKey != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tb2BroadPhase_MoveProxy( broadPhase, shape->proxyKey, fatAABB );\n\t\t\t}\n\t\t}\n\n\t\tshapeId = shape->nextShapeId;\n\t}\n}\n\nb2Vec2 b2Body_GetLinearVelocity( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tif ( state != NULL )\n\t{\n\t\treturn state->linearVelocity;\n\t}\n\treturn b2Vec2_zero;\n}\n\nfloat b2Body_GetAngularVelocity( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tif ( state != NULL )\n\t{\n\t\treturn state->angularVelocity;\n\t}\n\treturn 0.0;\n}\n\nvoid b2Body_SetLinearVelocity( b2BodyId bodyId, b2Vec2 linearVelocity )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( body->type == b2_staticBody )\n\t{\n\t\treturn;\n\t}\n\n\tif ( b2LengthSquared( linearVelocity ) > 0.0f )\n\t{\n\t\tb2WakeBody( world, body );\n\t}\n\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tif ( state == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tstate->linearVelocity = linearVelocity;\n}\n\nvoid b2Body_SetAngularVelocity( b2BodyId bodyId, float angularVelocity )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( body->type == b2_staticBody || ( body->flags & b2_lockAngularZ ) )\n\t{\n\t\treturn;\n\t}\n\n\tif ( angularVelocity != 0.0f )\n\t{\n\t\tb2WakeBody( world, body );\n\t}\n\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tif ( state == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tstate->angularVelocity = angularVelocity;\n}\n\nvoid b2Body_SetTargetTransform( b2BodyId bodyId, b2Transform target, float timeStep, bool wake )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( body->setIndex == b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\tif ( body->type == b2_staticBody || timeStep <= 0.0f )\n\t{\n\t\treturn;\n\t}\n\n\tif ( body->setIndex != b2_awakeSet && wake == false )\n\t{\n\t\treturn;\n\t}\n\n\tb2BodySim* sim = b2GetBodySim( world, body );\n\n\t// Compute linear velocity\n\tb2Vec2 center1 = sim->center;\n\tb2Vec2 center2 = b2TransformPoint( target, sim->localCenter );\n\tfloat invTimeStep = 1.0f / timeStep;\n\tb2Vec2 linearVelocity = b2MulSV( invTimeStep, b2Sub( center2, center1 ) );\n\n\t// Compute angular velocity\n\tb2Rot q1 = sim->transform.q;\n\tb2Rot q2 = target.q;\n\tfloat deltaAngle = b2RelativeAngle( q1, q2 );\n\tfloat angularVelocity = invTimeStep * deltaAngle;\n\n\t// Early out if the body is asleep already and the desired movement is small\n\tif ( body->setIndex != b2_awakeSet )\n\t{\n\t\tfloat maxVelocity = b2Length( linearVelocity ) + b2AbsFloat( angularVelocity ) * sim->maxExtent;\n\n\t\t// Return if velocity would be sleepy\n\t\tif ( maxVelocity < body->sleepThreshold )\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Must wake for state to exist\n\t\tb2WakeBody( world, body );\n\t}\n\n\tB2_ASSERT( body->setIndex == b2_awakeSet );\n\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tstate->linearVelocity = linearVelocity;\n\tstate->angularVelocity = angularVelocity;\n}\n\nb2Vec2 b2Body_GetLocalPointVelocity( b2BodyId bodyId, b2Vec2 localPoint )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tif ( state == NULL )\n\t{\n\t\treturn b2Vec2_zero;\n\t}\n\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );\n\tb2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex );\n\n\tb2Vec2 r = b2RotateVector( bodySim->transform.q, b2Sub( localPoint, bodySim->localCenter ) );\n\tb2Vec2 v = b2Add( state->linearVelocity, b2CrossSV( state->angularVelocity, r ) );\n\treturn v;\n}\n\nb2Vec2 b2Body_GetWorldPointVelocity( b2BodyId bodyId, b2Vec2 worldPoint )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tif ( state == NULL )\n\t{\n\t\treturn b2Vec2_zero;\n\t}\n\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );\n\tb2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex );\n\n\tb2Vec2 r = b2Sub( worldPoint, bodySim->center );\n\tb2Vec2 v = b2Add( state->linearVelocity, b2CrossSV( state->angularVelocity, r ) );\n\treturn v;\n}\n\nvoid b2Body_ApplyForce( b2BodyId bodyId, b2Vec2 force, b2Vec2 point, bool wake )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( body->type != b2_dynamicBody || body->setIndex == b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\tif ( wake && body->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeBody( world, body );\n\t}\n\n\tif ( body->setIndex == b2_awakeSet )\n\t{\n\t\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\t\tbodySim->force = b2Add( bodySim->force, force );\n\t\tbodySim->torque += b2Cross( b2Sub( point, bodySim->center ), force );\n\t}\n}\n\nvoid b2Body_ApplyForceToCenter( b2BodyId bodyId, b2Vec2 force, bool wake )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( body->type != b2_dynamicBody || body->setIndex == b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\tif ( wake && body->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeBody( world, body );\n\t}\n\n\tif ( body->setIndex == b2_awakeSet )\n\t{\n\t\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\t\tbodySim->force = b2Add( bodySim->force, force );\n\t}\n}\n\nvoid b2Body_ApplyTorque( b2BodyId bodyId, float torque, bool wake )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( body->type != b2_dynamicBody || body->setIndex == b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\tif ( wake && body->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeBody( world, body );\n\t}\n\n\tif ( body->setIndex == b2_awakeSet )\n\t{\n\t\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\t\tbodySim->torque += torque;\n\t}\n}\n\nvoid b2Body_ClearForces( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\tbodySim->force = b2Vec2_zero;\n\tbodySim->torque = 0.0f;\n}\n\nvoid b2Body_ApplyLinearImpulse( b2BodyId bodyId, b2Vec2 impulse, b2Vec2 point, bool wake )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( body->type != b2_dynamicBody || body->setIndex == b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\tif ( wake && body->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeBody( world, body );\n\t}\n\n\tif ( body->setIndex == b2_awakeSet )\n\t{\n\t\tint localIndex = body->localIndex;\n\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\t\tb2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex );\n\t\tb2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex );\n\t\tstate->linearVelocity = b2MulAdd( state->linearVelocity, bodySim->invMass, impulse );\n\t\tstate->angularVelocity += bodySim->invInertia * b2Cross( b2Sub( point, bodySim->center ), impulse );\n\n\t\tb2LimitVelocity( state, world->maxLinearSpeed );\n\t}\n}\n\nvoid b2Body_ApplyLinearImpulseToCenter( b2BodyId bodyId, b2Vec2 impulse, bool wake )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( body->type != b2_dynamicBody || body->setIndex == b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\tif ( wake && body->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeBody( world, body );\n\t}\n\n\tif ( body->setIndex == b2_awakeSet )\n\t{\n\t\tint localIndex = body->localIndex;\n\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\t\tb2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex );\n\t\tb2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex );\n\t\tstate->linearVelocity = b2MulAdd( state->linearVelocity, bodySim->invMass, impulse );\n\n\t\tb2LimitVelocity( state, world->maxLinearSpeed );\n\t}\n}\n\nvoid b2Body_ApplyAngularImpulse( b2BodyId bodyId, float impulse, bool wake )\n{\n\tB2_ASSERT( b2Body_IsValid( bodyId ) );\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( body->type != b2_dynamicBody || body->setIndex == b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\tif ( wake && body->setIndex >= b2_firstSleepingSet )\n\t{\n\t\t// this will not invalidate body pointer\n\t\tb2WakeBody( world, body );\n\t}\n\n\tif ( body->setIndex == b2_awakeSet )\n\t{\n\t\tint localIndex = body->localIndex;\n\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\t\tb2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex );\n\t\tb2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex );\n\t\tstate->angularVelocity += bodySim->invInertia * impulse;\n\t}\n}\n\nb2BodyType b2Body_GetType( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->type;\n}\n\n// This should follow similar steps as you would get destroying and recreating the body, shapes, and joints.\n// Contacts are difficult to preserve because the broad-phase pairs change, so I just destroy them.\n// todo with a bit more effort I could support an option to let the body sleep\n//\n// Revised steps:\n// 1 Skip disabled bodies\n// 2 Destroy all contacts on the body\n// 3 Wake the body\n// 4 For all joints attached to the body\n//  - wake attached bodies\n//  - remove from island\n//  - move to static set temporarily\n// 5 Change the body type and transfer the body\n// 6 If the body was static\n//   - create an island for the body\n//   Else if the body is becoming static\n//   - remove it from the island\n// 7 For all joints\n//  - if either body is non-static\n//    - link into island\n//    - transfer to constraint graph\n// 8 For all shapes\n//  - Destroy proxy in old tree\n//  - Create proxy in new tree\n// Notes:\n// - the implementation below tries to minimize the number of predicates, so some\n//   operations may have no effect, such as transferring a joint to the same set\nvoid b2Body_SetType( b2BodyId bodyId, b2BodyType type )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tb2BodyType originalType = body->type;\n\tif ( originalType == type )\n\t{\n\t\treturn;\n\t}\n\n\t// Stage 1: skip disabled bodies\n\tif ( body->setIndex == b2_disabledSet )\n\t{\n\t\t// Disabled bodies don't change solver sets or islands when they change type.\n\t\tbody->type = type;\n\n\t\tif ( type == b2_dynamicBody )\n\t\t{\n\t\t\tbody->flags |= b2_dynamicFlag;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbody->flags &= ~b2_dynamicFlag;\n\t\t}\n\n\t\t// Body type affects the mass properties\n\t\tb2UpdateBodyMassData( world, body );\n\t\treturn;\n\t}\n\n\t// Stage 2: destroy all contacts but don't wake bodies (because we don't need to)\n\tbool wakeBodies = false;\n\tb2DestroyBodyContacts( world, body, wakeBodies );\n\n\t// Stage 3: wake this body (does nothing if body is static), otherwise it will also wake\n\t// all bodies in the same sleeping solver set.\n\tb2WakeBody( world, body );\n\n\t// Stage 4: move joints to temporary storage\n\tb2SolverSet* staticSet = b2SolverSetArray_Get( &world->solverSets, b2_staticSet );\n\n\tint jointKey = body->headJointKey;\n\twhile ( jointKey != B2_NULL_INDEX )\n\t{\n\t\tint jointId = jointKey >> 1;\n\t\tint edgeIndex = jointKey & 1;\n\n\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\n\t\t// Joint may be disabled by other body\n\t\tif ( joint->setIndex == b2_disabledSet )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Wake attached bodies. The b2WakeBody call above does not wake bodies\n\t\t// attached to a static body. But it is necessary because the body may have\n\t\t// no joints.\n\t\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );\n\t\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );\n\t\tb2WakeBody( world, bodyA );\n\t\tb2WakeBody( world, bodyB );\n\n\t\t// Remove joint from island\n\t\tb2UnlinkJoint( world, joint );\n\n\t\t// It is necessary to transfer all joints to the static set\n\t\t// so they can be added to the constraint graph below and acquire consistent colors.\n\t\tb2SolverSet* jointSourceSet = b2SolverSetArray_Get( &world->solverSets, joint->setIndex );\n\t\tb2TransferJoint( world, staticSet, jointSourceSet, joint );\n\t}\n\n\t// Stage 5: change the body type and transfer body\n\tbody->type = type;\n\n\tif ( type == b2_dynamicBody )\n\t{\n\t\tbody->flags |= b2_dynamicFlag;\n\t}\n\telse\n\t{\n\t\tbody->flags &= ~b2_dynamicFlag;\n\t}\n\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tb2SolverSet* sourceSet = b2SolverSetArray_Get( &world->solverSets, body->setIndex );\n\tb2SolverSet* targetSet = type == b2_staticBody ? staticSet : awakeSet;\n\n\t// Transfer body\n\tb2TransferBody( world, targetSet, sourceSet, body );\n\n\t// Stage 6: update island participation for the body\n\tif ( originalType == b2_staticBody )\n\t{\n\t\t// Create island for body\n\t\tb2CreateIslandForBody( world, b2_awakeSet, body );\n\t}\n\telse if ( type == b2_staticBody )\n\t{\n\t\t// Remove body from island.\n\t\tb2RemoveBodyFromIsland( world, body );\n\t}\n\n\t// Stage 7: Transfer joints to the target set\n\tjointKey = body->headJointKey;\n\twhile ( jointKey != B2_NULL_INDEX )\n\t{\n\t\tint jointId = jointKey >> 1;\n\t\tint edgeIndex = jointKey & 1;\n\n\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\n\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\n\t\t// Joint may be disabled by other body\n\t\tif ( joint->setIndex == b2_disabledSet )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t// All joints were transferred to the static set in an earlier stage\n\t\tB2_ASSERT( joint->setIndex == b2_staticSet );\n\n\t\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );\n\t\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );\n\t\tB2_ASSERT( bodyA->setIndex == b2_staticSet || bodyA->setIndex == b2_awakeSet );\n\t\tB2_ASSERT( bodyB->setIndex == b2_staticSet || bodyB->setIndex == b2_awakeSet );\n\n\t\tif ( bodyA->type == b2_dynamicBody || bodyB->type == b2_dynamicBody )\n\t\t{\n\t\t\tb2TransferJoint( world, awakeSet, staticSet, joint );\n\t\t}\n\t}\n\n\t// Recreate shape proxies in broadphase\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\tint shapeId = body->headShapeId;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tshapeId = shape->nextShapeId;\n\t\tb2DestroyShapeProxy( shape, &world->broadPhase );\n\t\tbool forcePairCreation = true;\n\t\tb2CreateShapeProxy( shape, &world->broadPhase, type, transform, forcePairCreation );\n\t}\n\n\t// Relink all joints\n\tjointKey = body->headJointKey;\n\twhile ( jointKey != B2_NULL_INDEX )\n\t{\n\t\tint jointId = jointKey >> 1;\n\t\tint edgeIndex = jointKey & 1;\n\n\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\n\t\tint otherEdgeIndex = edgeIndex ^ 1;\n\t\tint otherBodyId = joint->edges[otherEdgeIndex].bodyId;\n\t\tb2Body* otherBody = b2BodyArray_Get( &world->bodies, otherBodyId );\n\n\t\tif ( otherBody->setIndex == b2_disabledSet )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ( body->type != b2_dynamicBody && otherBody->type != b2_dynamicBody )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2LinkJoint( world, joint );\n\t}\n\n\t// Body type affects the mass\n\tb2UpdateBodyMassData( world, body );\n\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tif ( state != NULL )\n\t{\n\t\t// Ensure flags are in sync (b2_skipSolverWrite)\n\t\tstate->flags = body->flags;\n\t}\n\n\tb2ValidateSolverSets( world );\n\tb2ValidateIsland( world, body->islandId );\n}\n\nvoid b2Body_SetName( b2BodyId bodyId, const char* name )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( name )\n\t{\n\t\tint i = 0;\n\t\twhile ( i < B2_NAME_LENGTH - 1 && name[i] != 0 )\n\t\t{\n\t\t\tbody->name[i] = name[i];\n\t\t\ti += 1;\n\t\t}\n\n\t\twhile ( i < B2_NAME_LENGTH )\n\t\t{\n\t\t\tbody->name[i] = 0;\n\t\t\ti += 1;\n\t\t}\n\t}\n\telse\n\t{\n\t\tmemset( body->name, 0, B2_NAME_LENGTH * sizeof( char ) );\n\t}\n}\n\nconst char* b2Body_GetName( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->name;\n}\n\nvoid b2Body_SetUserData( b2BodyId bodyId, void* userData )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tbody->userData = userData;\n}\n\nvoid* b2Body_GetUserData( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->userData;\n}\n\nfloat b2Body_GetMass( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->mass;\n}\n\nfloat b2Body_GetRotationalInertia( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->inertia;\n}\n\nb2Vec2 b2Body_GetLocalCenterOfMass( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\treturn bodySim->localCenter;\n}\n\nb2Vec2 b2Body_GetWorldCenterOfMass( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\treturn bodySim->center;\n}\n\nvoid b2Body_SetMassData( b2BodyId bodyId, b2MassData massData )\n{\n\tB2_ASSERT( b2IsValidFloat( massData.mass ) && massData.mass >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( massData.rotationalInertia ) && massData.rotationalInertia >= 0.0f );\n\tB2_ASSERT( b2IsValidVec2( massData.center ) );\n\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\n\tbody->mass = massData.mass;\n\tbody->inertia = massData.rotationalInertia;\n\tbodySim->localCenter = massData.center;\n\n\tb2Vec2 center = b2TransformPoint( bodySim->transform, massData.center );\n\tbodySim->center = center;\n\tbodySim->center0 = center;\n\n\tbodySim->invMass = body->mass > 0.0f ? 1.0f / body->mass : 0.0f;\n\tbodySim->invInertia = body->inertia > 0.0f ? 1.0f / body->inertia : 0.0f;\n}\n\nb2MassData b2Body_GetMassData( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\tb2MassData massData = { body->mass, bodySim->localCenter, body->inertia };\n\treturn massData;\n}\n\nvoid b2Body_ApplyMassFromShapes( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2UpdateBodyMassData( world, body );\n}\n\nvoid b2Body_SetLinearDamping( b2BodyId bodyId, float linearDamping )\n{\n\tB2_ASSERT( b2IsValidFloat( linearDamping ) && linearDamping >= 0.0f );\n\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\tbodySim->linearDamping = linearDamping;\n}\n\nfloat b2Body_GetLinearDamping( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\treturn bodySim->linearDamping;\n}\n\nvoid b2Body_SetAngularDamping( b2BodyId bodyId, float angularDamping )\n{\n\tB2_ASSERT( b2IsValidFloat( angularDamping ) && angularDamping >= 0.0f );\n\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\tbodySim->angularDamping = angularDamping;\n}\n\nfloat b2Body_GetAngularDamping( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\treturn bodySim->angularDamping;\n}\n\nvoid b2Body_SetGravityScale( b2BodyId bodyId, float gravityScale )\n{\n\tB2_ASSERT( b2Body_IsValid( bodyId ) );\n\tB2_ASSERT( b2IsValidFloat( gravityScale ) );\n\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\tbodySim->gravityScale = gravityScale;\n}\n\nfloat b2Body_GetGravityScale( b2BodyId bodyId )\n{\n\tB2_ASSERT( b2Body_IsValid( bodyId ) );\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\treturn bodySim->gravityScale;\n}\n\nbool b2Body_IsAwake( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->setIndex == b2_awakeSet;\n}\n\nvoid b2Body_SetAwake( b2BodyId bodyId, bool awake )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tif ( awake && body->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeBody( world, body );\n\t}\n\telse if ( awake == false && body->setIndex == b2_awakeSet )\n\t{\n\t\tb2Island* island = b2IslandArray_Get( &world->islands, body->islandId );\n\t\tif ( island->constraintRemoveCount > 0 )\n\t\t{\n\t\t\t// Must split the island before sleeping. This is expensive.\n\t\t\tb2SplitIsland( world, body->islandId );\n\t\t}\n\n\t\tb2TrySleepIsland( world, body->islandId );\n\t}\n}\n\nvoid b2Body_WakeTouching(b2BodyId bodyId)\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tint contactKey = body->headContactKey;\n\twhile ( contactKey != B2_NULL_INDEX )\n\t{\n\t\tint contactId = contactKey >> 1;\n\t\tint edgeIndex = contactKey & 1;\n\n\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\tb2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contact->shapeIdA );\n\t\tb2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contact->shapeIdB );\n\n\t\tif (shapeA->bodyId == bodyId.index1 - 1)\n\t\t{\n\t\t\tb2Body* otherBody = b2BodyArray_Get( &world->bodies, shapeB->bodyId );\n\t\t\tb2WakeBody( world, otherBody );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb2Body* otherBody = b2BodyArray_Get( &world->bodies, shapeA->bodyId );\n\t\t\tb2WakeBody( world, otherBody );\n\t\t}\n\n\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\t}\n}\n\nbool b2Body_IsEnabled( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->setIndex != b2_disabledSet;\n}\n\nbool b2Body_IsSleepEnabled( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->enableSleep;\n}\n\nvoid b2Body_SetSleepThreshold( b2BodyId bodyId, float sleepThreshold )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tbody->sleepThreshold = sleepThreshold;\n}\n\nfloat b2Body_GetSleepThreshold( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->sleepThreshold;\n}\n\nvoid b2Body_EnableSleep( b2BodyId bodyId, bool enableSleep )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tbody->enableSleep = enableSleep;\n\n\tif ( enableSleep == false )\n\t{\n\t\tb2WakeBody( world, body );\n\t}\n}\n\n// Disabling a body requires a lot of detailed bookkeeping, but it is a valuable feature.\n// The most challenging aspect is that joints may connect to bodies that are not disabled.\nvoid b2Body_Disable( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tif ( body->setIndex == b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\t// Destroy contacts and wake bodies touching this body. This avoid floating bodies.\n\t// This is necessary even for static bodies.\n\tbool wakeBodies = true;\n\tb2DestroyBodyContacts( world, body, wakeBodies );\n\n\t// The current solver set of the body\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );\n\n\t// Disabled bodies and connected joints are moved to the disabled set\n\tb2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet );\n\n\t// Unlink joints and transfer them to the disabled set\n\tint jointKey = body->headJointKey;\n\twhile ( jointKey != B2_NULL_INDEX )\n\t{\n\t\tint jointId = jointKey >> 1;\n\t\tint edgeIndex = jointKey & 1;\n\n\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\n\t\t// joint may already be disabled by other body\n\t\tif ( joint->setIndex == b2_disabledSet )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tB2_ASSERT( joint->setIndex == set->setIndex || set->setIndex == b2_staticSet );\n\n\t\t// Remove joint from island\n\t\tb2UnlinkJoint( world, joint );\n\n\t\t// Transfer joint to disabled set\n\t\tb2SolverSet* jointSet = b2SolverSetArray_Get( &world->solverSets, joint->setIndex );\n\t\tb2TransferJoint( world, disabledSet, jointSet, joint );\n\t}\n\n\t// Remove shapes from broad-phase\n\tint shapeId = body->headShapeId;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tshapeId = shape->nextShapeId;\n\t\tb2DestroyShapeProxy( shape, &world->broadPhase );\n\t}\n\n\t// Disabled bodies are not in an island. If the island becomes empty it will be destroyed.\n\tb2RemoveBodyFromIsland( world, body );\n\n\t// Transfer body sim\n\tb2TransferBody( world, disabledSet, set, body );\n\n\tb2ValidateConnectivity( world );\n\tb2ValidateSolverSets( world );\n}\n\nvoid b2Body_Enable( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tif ( body->setIndex != b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\tb2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet );\n\tint setId = body->type == b2_staticBody ? b2_staticSet : b2_awakeSet;\n\tb2SolverSet* targetSet = b2SolverSetArray_Get( &world->solverSets, setId );\n\n\tb2TransferBody( world, targetSet, disabledSet, body );\n\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\t// Add shapes to broad-phase\n\tb2BodyType proxyType = body->type;\n\tbool forcePairCreation = true;\n\tint shapeId = body->headShapeId;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tshapeId = shape->nextShapeId;\n\n\t\tb2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, forcePairCreation );\n\t}\n\n\tif ( setId != b2_staticSet )\n\t{\n\t\tb2CreateIslandForBody( world, setId, body );\n\t}\n\n\t// Transfer joints. If the other body is disabled, don't transfer.\n\t// If the other body is sleeping, wake it.\n\tint jointKey = body->headJointKey;\n\twhile ( jointKey != B2_NULL_INDEX )\n\t{\n\t\tint jointId = jointKey >> 1;\n\t\tint edgeIndex = jointKey & 1;\n\n\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\tB2_ASSERT( joint->setIndex == b2_disabledSet );\n\t\tB2_ASSERT( joint->islandId == B2_NULL_INDEX );\n\n\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\n\t\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );\n\t\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );\n\n\t\tif ( bodyA->setIndex == b2_disabledSet || bodyB->setIndex == b2_disabledSet )\n\t\t{\n\t\t\t// one body is still disabled\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Transfer joint first\n\t\tint jointSetId;\n\t\tif ( bodyA->setIndex == b2_staticSet && bodyB->setIndex == b2_staticSet )\n\t\t{\n\t\t\tjointSetId = b2_staticSet;\n\t\t}\n\t\telse if ( bodyA->setIndex == b2_staticSet )\n\t\t{\n\t\t\tjointSetId = bodyB->setIndex;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tjointSetId = bodyA->setIndex;\n\t\t}\n\n\t\tb2SolverSet* jointSet = b2SolverSetArray_Get( &world->solverSets, jointSetId );\n\t\tb2TransferJoint( world, jointSet, disabledSet, joint );\n\n\t\t// Now that the joint is in the correct set, I can link the joint in the island.\n\t\tif ( jointSetId != b2_staticSet )\n\t\t{\n\t\t\tb2LinkJoint( world, joint );\n\t\t}\n\t}\n\n\tb2ValidateSolverSets( world );\n}\n\nvoid b2Body_SetMotionLocks( b2BodyId bodyId, b2MotionLocks locks )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tuint32_t newFlags = 0;\n\tnewFlags |= locks.linearX ? b2_lockLinearX : 0;\n\tnewFlags |= locks.linearY ? b2_lockLinearY : 0;\n\tnewFlags |= locks.angularZ ? b2_lockAngularZ : 0;\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tif ( ( body->flags & b2_allLocks ) != newFlags )\n\t{\n\t\tbody->flags &= ~b2_allLocks;\n\t\tbody->flags |= newFlags;\n\n\t\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\t\tbodySim->flags &= ~b2_allLocks;\n\t\tbodySim->flags |= newFlags;\n\n\t\tb2BodyState* state = b2GetBodyState( world, body );\n\n\t\tif ( state != NULL )\n\t\t{\n\t\t\tstate->flags = bodySim->flags;\n\n\t\t\tif ( locks.linearX )\n\t\t\t{\n\t\t\t\tstate->linearVelocity.x = 0.0f;\n\t\t\t}\n\n\t\t\tif ( locks.linearY )\n\t\t\t{\n\t\t\t\tstate->linearVelocity.y = 0.0f;\n\t\t\t}\n\n\t\t\tif ( locks.angularZ )\n\t\t\t{\n\t\t\t\tstate->angularVelocity = 0.0f;\n\t\t\t}\n\t\t}\n\t}\n}\n\nb2MotionLocks b2Body_GetMotionLocks( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\n\tb2MotionLocks locks;\n\tlocks.linearX = ( body->flags & b2_lockLinearX );\n\tlocks.linearY = ( body->flags & b2_lockLinearY );\n\tlocks.angularZ = ( body->flags & b2_lockAngularZ );\n\treturn locks;\n}\n\nvoid b2Body_SetBullet( b2BodyId bodyId, bool flag )\n{\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\n\tif ( flag )\n\t{\n\t\tbodySim->flags |= b2_isBullet;\n\t}\n\telse\n\t{\n\t\tbodySim->flags &= ~b2_isBullet;\n\t}\n}\n\nbool b2Body_IsBullet( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\treturn ( bodySim->flags & b2_isBullet ) != 0;\n}\n\nvoid b2Body_EnableContactEvents( b2BodyId bodyId, bool flag )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tint shapeId = body->headShapeId;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tshape->enableContactEvents = flag;\n\t\tshapeId = shape->nextShapeId;\n\t}\n}\n\nvoid b2Body_EnableHitEvents( b2BodyId bodyId, bool flag )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tint shapeId = body->headShapeId;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tshape->enableHitEvents = flag;\n\t\tshapeId = shape->nextShapeId;\n\t}\n}\n\nb2WorldId b2Body_GetWorld( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\treturn (b2WorldId){ bodyId.world0 + 1, world->generation };\n}\n\nint b2Body_GetShapeCount( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->shapeCount;\n}\n\nint b2Body_GetShapes( b2BodyId bodyId, b2ShapeId* shapeArray, int capacity )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tint shapeId = body->headShapeId;\n\tint shapeCount = 0;\n\twhile ( shapeId != B2_NULL_INDEX && shapeCount < capacity )\n\t{\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tb2ShapeId id = { shape->id + 1, bodyId.world0, shape->generation };\n\t\tshapeArray[shapeCount] = id;\n\t\tshapeCount += 1;\n\n\t\tshapeId = shape->nextShapeId;\n\t}\n\n\treturn shapeCount;\n}\n\nint b2Body_GetJointCount( b2BodyId bodyId )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\treturn body->jointCount;\n}\n\nint b2Body_GetJoints( b2BodyId bodyId, b2JointId* jointArray, int capacity )\n{\n\tb2World* world = b2GetWorld( bodyId.world0 );\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tint jointKey = body->headJointKey;\n\n\tint jointCount = 0;\n\twhile ( jointKey != B2_NULL_INDEX && jointCount < capacity )\n\t{\n\t\tint jointId = jointKey >> 1;\n\t\tint edgeIndex = jointKey & 1;\n\n\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\n\t\tb2JointId id = { jointId + 1, bodyId.world0, joint->generation };\n\t\tjointArray[jointCount] = id;\n\t\tjointCount += 1;\n\n\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\t}\n\n\treturn jointCount;\n}\n\nbool b2ShouldBodiesCollide( b2World* world, b2Body* bodyA, b2Body* bodyB )\n{\n\tif ( bodyA->type != b2_dynamicBody && bodyB->type != b2_dynamicBody )\n\t{\n\t\treturn false;\n\t}\n\n\tint jointKey;\n\tint otherBodyId;\n\tif ( bodyA->jointCount < bodyB->jointCount )\n\t{\n\t\tjointKey = bodyA->headJointKey;\n\t\totherBodyId = bodyB->id;\n\t}\n\telse\n\t{\n\t\tjointKey = bodyB->headJointKey;\n\t\totherBodyId = bodyA->id;\n\t}\n\n\twhile ( jointKey != B2_NULL_INDEX )\n\t{\n\t\tint jointId = jointKey >> 1;\n\t\tint edgeIndex = jointKey & 1;\n\t\tint otherEdgeIndex = edgeIndex ^ 1;\n\n\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\tif ( joint->collideConnected == false && joint->edges[otherEdgeIndex].bodyId == otherBodyId )\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/body.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n\n#include \"box2d/math_functions.h\"\n#include \"box2d/types.h\"\n\n// Length of body debug name\n#define B2_NAME_LENGTH 32\n\ntypedef struct b2World b2World;\n\nenum b2BodyFlags\n{\n\t// This body has fixed translation along the x-axis\n\tb2_lockLinearX = 0x00000001,\n\n\t// This body has fixed translation along the y-axis\n\tb2_lockLinearY = 0x00000002,\n\n\t// This body has fixed rotation\n\tb2_lockAngularZ = 0x00000004,\n\n\t// This flag is used for debug draw\n\tb2_isFast = 0x00000008,\n\n\t// This dynamic body does a final CCD pass against all body types, but not other bullets\n\tb2_isBullet = 0x00000010,\n\n\t// This body was speed capped in the current time step\n\tb2_isSpeedCapped = 0x00000020,\n\t\n\t// This body had a time of impact event in the current time step\n\tb2_hadTimeOfImpact = 0x00000040,\n\n\t// This body has no limit on angular velocity\n\tb2_allowFastRotation = 0x00000080,\n\n\t// This body need's to have its AABB increased\n\tb2_enlargeBounds = 0x00000100,\n\n\t// This body is dynamic so the solver should write to it.\n\t// This prevents writing to kinematic bodies that causes a multithreaded sharing\n\t// cache coherence problem even when the values are not changing.\n\t// Used for b2BodyState flags.\n\tb2_dynamicFlag = 0x00000200,\n\n\t// Flag to indicate the user has used the updateBodyMass option to defer mass\n\t// computation but b2Body_ApplyMassFromShapes was not called before the world step.\n\tb2_dirtyMass = 0x00000400,\n\n\t// All lock flags\n\tb2_allLocks = b2_lockAngularZ | b2_lockLinearX | b2_lockLinearY,\n};\n\n// Body organizational details that are not used in the solver.\ntypedef struct b2Body\n{\n\tchar name[B2_NAME_LENGTH];\n\n\tvoid* userData;\n\n\t// index of solver set stored in b2World\n\t// may be B2_NULL_INDEX\n\tint setIndex;\n\n\t// body sim and state index within set\n\t// may be B2_NULL_INDEX\n\tint localIndex;\n\n\t// [31 : contactId | 1 : edgeIndex]\n\tint headContactKey;\n\tint contactCount;\n\n\t// todo maybe move this to the body sim\n\tint headShapeId;\n\tint shapeCount;\n\n\tint headChainId;\n\n\t// [31 : jointId | 1 : edgeIndex]\n\tint headJointKey;\n\tint jointCount;\n\n\t// All enabled dynamic and kinematic bodies are in an island.\n\tint islandId;\n\n\t// doubly-linked island list\n\tint islandPrev;\n\tint islandNext;\n\n\tfloat mass;\n\n\t// Rotational inertia about the center of mass.\n\tfloat inertia;\n\n\tfloat sleepThreshold;\n\tfloat sleepTime;\n\n\t// this is used to adjust the fellAsleep flag in the body move array\n\tint bodyMoveIndex;\n\n\tint id;\n\n\t// b2BodyFlags\n\tuint32_t flags;\n\n\tb2BodyType type;\n\n\t// This is monotonically advanced when a body is allocated in this slot\n\t// Used to check for invalid b2BodyId\n\tuint16_t generation;\n\n\t// todo move into flags\n\tbool enableSleep;\n} b2Body;\n\n// Body State\n// The body state is designed for fast conversion to and from SIMD via scatter-gather.\n// Only awake dynamic and kinematic bodies have a body state.\n// This is used in the performance critical constraint solver\n//\n// The solver operates on the body state. The body state array does not hold static bodies. Static bodies are shared\n// across worker threads. It would be okay to read their states, but writing to them would cause cache thrashing across\n// workers, even if the values don't change.\n// This causes some trouble when computing anchors. I rotate joint anchors using the body rotation every sub-step. For static\n// bodies the anchor doesn't rotate. Body A or B could be static and this can lead to lots of branching. This branching\n// should be minimized.\n//\n// Solution 1:\n// Use delta rotations. This means anchors need to be prepared in world space. The delta rotation for static bodies will be\n// identity using a dummy state. Base separation and angles need to be computed. Manifolds will be behind a frame, but that\n// is probably best if bodies move fast.\n//\n// Solution 2:\n// Use full rotation. The anchors for static bodies will be in world space while the anchors for dynamic bodies will be in local\n// space. Potentially confusing and bug prone.\n//\n// Note:\n// I rotate joint anchors each sub-step but not contact anchors. Joint stability improves a lot by rotating joint anchors\n// according to substep progress. Contacts have reduced stability when anchors are rotated during substeps, especially for\n// round shapes.\n\n// 32 bytes\ntypedef struct b2BodyState\n{\n\tb2Vec2 linearVelocity; // 8\n\tfloat angularVelocity; // 4\n\n\t// b2BodyFlags\n\t// Important flags: locking, dynamic\n\tuint32_t flags; // 4\n\n\t// Using delta position reduces round-off error far from the origin\n\tb2Vec2 deltaPosition; // 8\n\n\t// Using delta rotation because I cannot access the full rotation on static bodies in\n\t// the solver and must use zero delta rotation for static bodies (c,s) = (1,0)\n\tb2Rot deltaRotation; // 8\n} b2BodyState;\n\n// Identity body state, notice the deltaRotation is {1, 0}\nstatic const b2BodyState b2_identityBodyState = { { 0.0f, 0.0f }, 0.0f, 0, { 0.0f, 0.0f }, { 1.0f, 0.0f } };\n\n// Body simulation data used for integration of position and velocity\n// Transform data used for collision and solver preparation.\ntypedef struct b2BodySim\n{\n\t// transform for body origin\n\tb2Transform transform;\n\n\t// center of mass position in world space\n\tb2Vec2 center;\n\n\t// previous rotation and COM for TOI\n\tb2Rot rotation0;\n\tb2Vec2 center0;\n\n\t// location of center of mass relative to the body origin\n\tb2Vec2 localCenter;\n\n\tb2Vec2 force;\n\tfloat torque;\n\n\t// inverse inertia\n\tfloat invMass;\n\tfloat invInertia;\n\n\tfloat minExtent;\n\tfloat maxExtent;\n\tfloat linearDamping;\n\tfloat angularDamping;\n\tfloat gravityScale;\n\n\t// Index of b2Body\n\tint bodyId;\n\n\t// b2BodyFlags\n\tuint32_t flags;\n} b2BodySim;\n\n// Get a validated body from a world using an id.\nb2Body* b2GetBodyFullId( b2World* world, b2BodyId bodyId );\n\nb2Transform b2GetBodyTransformQuick( b2World* world, b2Body* body );\nb2Transform b2GetBodyTransform( b2World* world, int bodyId );\n\n// Create a b2BodyId from a raw id.\nb2BodyId b2MakeBodyId( b2World* world, int bodyId );\n\nbool b2ShouldBodiesCollide( b2World* world, b2Body* bodyA, b2Body* bodyB );\n\nb2BodySim* b2GetBodySim( b2World* world, b2Body* body );\nb2BodyState* b2GetBodyState( b2World* world, b2Body* body );\n\n// careful calling this because it can invalidate body, state, joint, and contact pointers\nbool b2WakeBody( b2World* world, b2Body* body );\n\nvoid b2UpdateBodyMassData( b2World* world, b2Body* body );\n\nstatic inline b2Sweep b2MakeSweep( const b2BodySim* bodySim )\n{\n\tb2Sweep s;\n\ts.c1 = bodySim->center0;\n\ts.c2 = bodySim->center;\n\ts.q1 = bodySim->rotation0;\n\ts.q2 = bodySim->transform.q;\n\ts.localCenter = bodySim->localCenter;\n\treturn s;\n}\n\n// Define inline functions for arrays\nB2_ARRAY_INLINE( b2Body, b2Body )\nB2_ARRAY_INLINE( b2BodySim, b2BodySim )\nB2_ARRAY_INLINE( b2BodyState, b2BodyState )\n"
  },
  {
    "path": "src/box2d.natvis",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<AutoVisualizer xmlns=\"http://schemas.microsoft.com/vstudio/debugger/natvis/2010\">\n<Type Name=\"__m128\">\n\t<DisplayString>[{m128_f32[0]}, {m128_f32[1]}, {m128_f32[2]}, {m128_f32[3]}]</DisplayString>\n\t<Expand>\n\t\t<Item Name=\"x\">m128_f32[0]</Item>\n\t\t<Item Name=\"y\">m128_f32[1]</Item>\n\t\t<Item Name=\"z\">m128_f32[2]</Item>\n\t\t<Item Name=\"w\">m128_f32[3]</Item>\n\t\t<Item Name=\"Address\">(void*)this</Item>\n\t</Expand>\n</Type>\n<Type Name=\"__m256\">\n\t<DisplayString>[{m256_f32[0]}, {m256_f32[1]}, {m256_f32[2]}, {m256_f32[3]}, {m256_f32[4]}, {m256_f32[5]}, {m256_f32[6]}, {m256_f32[7]}]</DisplayString>\n\t<Expand>\n\t\t<Item Name=\"x1\">m256_f32[0]</Item>\n\t\t<Item Name=\"y1\">m256_f32[1]</Item>\n\t\t<Item Name=\"z1\">m256_f32[2]</Item>\n\t\t<Item Name=\"w1\">m256_f32[3]</Item>\n\t\t<Item Name=\"x2\">m256_f32[4]</Item>\n\t\t<Item Name=\"y2\">m256_f32[5]</Item>\n\t\t<Item Name=\"z2\">m256_f32[6]</Item>\n\t\t<Item Name=\"w2\">m256_f32[7]</Item>\n\t\t<Item Name=\"Address\">(void*)this</Item>\n\t</Expand>\n</Type>\n\n<Type Name=\"b2WorldId\">\n\t<DisplayString>index: {index1}</DisplayString>\n\t<Expand>\n\t\t<ExpandedItem>b2_worlds[ index1 - 1 ]</ExpandedItem>\n\t</Expand>\n</Type>\n\t\n<Type Name=\"b2BodyId\">\n\t<DisplayString>\n\t\t{b2_worlds[world0].bodies.data[index1-1].name,na},\n\t\t{b2_worlds[world0].bodies.data[index1-1].type}\n\t</DisplayString>\n</Type>\n</AutoVisualizer>\n"
  },
  {
    "path": "src/broad_phase.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"broad_phase.h\"\n\n#include \"aabb.h\"\n#include \"array.h\"\n#include \"atomic.h\"\n#include \"body.h\"\n#include \"contact.h\"\n#include \"core.h\"\n#include \"shape.h\"\n#include \"arena_allocator.h\"\n#include \"physics_world.h\"\n\n#include <stdbool.h>\n#include <string.h>\n\n// #include <stdio.h>\n\n// static FILE* s_file = NULL;\n\nvoid b2CreateBroadPhase( b2BroadPhase* bp )\n{\n\t_Static_assert( b2_bodyTypeCount == 3, \"must be three body types\" );\n\n\t// if (s_file == NULL)\n\t//{\n\t//\ts_file = fopen(\"pairs01.txt\", \"a\");\n\t//\tfprintf(s_file, \"============\\n\\n\");\n\t// }\n\n\tbp->moveSet = b2CreateSet( 16 );\n\tbp->moveArray = b2IntArray_Create( 16 );\n\tbp->moveResults = NULL;\n\tbp->movePairs = NULL;\n\tbp->movePairCapacity = 0;\n\tb2AtomicStoreInt( &bp->movePairIndex, 0 );\n\tbp->pairSet = b2CreateSet( 32 );\n\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tbp->trees[i] = b2DynamicTree_Create();\n\t}\n}\n\nvoid b2DestroyBroadPhase( b2BroadPhase* bp )\n{\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tb2DynamicTree_Destroy( bp->trees + i );\n\t}\n\n\tb2DestroySet( &bp->moveSet );\n\tb2IntArray_Destroy( &bp->moveArray );\n\tb2DestroySet( &bp->pairSet );\n\n\tmemset( bp, 0, sizeof( b2BroadPhase ) );\n\n\t// if (s_file != NULL)\n\t//{\n\t//\tfclose(s_file);\n\t//\ts_file = NULL;\n\t// }\n}\n\nstatic inline void b2UnBufferMove( b2BroadPhase* bp, int proxyKey )\n{\n\tbool found = b2RemoveKey( &bp->moveSet, proxyKey + 1 );\n\n\tif ( found )\n\t{\n\t\t// Purge from move buffer. Linear search.\n\t\t// todo if I can iterate the move set then I don't need the moveArray\n\t\tint count = bp->moveArray.count;\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tif ( bp->moveArray.data[i] == proxyKey )\n\t\t\t{\n\t\t\t\tb2IntArray_RemoveSwap( &bp->moveArray, i );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nint b2BroadPhase_CreateProxy( b2BroadPhase* bp, b2BodyType proxyType, b2AABB aabb, uint64_t categoryBits, int shapeIndex,\n\t\t\t\t\t\t\t  bool forcePairCreation )\n{\n\tB2_ASSERT( 0 <= proxyType && proxyType < b2_bodyTypeCount );\n\tint proxyId = b2DynamicTree_CreateProxy( bp->trees + proxyType, aabb, categoryBits, shapeIndex );\n\tint proxyKey = B2_PROXY_KEY( proxyId, proxyType );\n\tif ( proxyType != b2_staticBody || forcePairCreation )\n\t{\n\t\tb2BufferMove( bp, proxyKey );\n\t}\n\treturn proxyKey;\n}\n\nvoid b2BroadPhase_DestroyProxy( b2BroadPhase* bp, int proxyKey )\n{\n\tB2_ASSERT( bp->moveArray.count == (int)bp->moveSet.count );\n\tb2UnBufferMove( bp, proxyKey );\n\n\tb2BodyType proxyType = B2_PROXY_TYPE( proxyKey );\n\tint proxyId = B2_PROXY_ID( proxyKey );\n\n\tB2_ASSERT( 0 <= proxyType && proxyType <= b2_bodyTypeCount );\n\tb2DynamicTree_DestroyProxy( bp->trees + proxyType, proxyId );\n}\n\nvoid b2BroadPhase_MoveProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb )\n{\n\tb2BodyType proxyType = B2_PROXY_TYPE( proxyKey );\n\tint proxyId = B2_PROXY_ID( proxyKey );\n\n\tb2DynamicTree_MoveProxy( bp->trees + proxyType, proxyId, aabb );\n\tb2BufferMove( bp, proxyKey );\n}\n\nvoid b2BroadPhase_EnlargeProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb )\n{\n\tB2_ASSERT( proxyKey != B2_NULL_INDEX );\n\tint typeIndex = B2_PROXY_TYPE( proxyKey );\n\tint proxyId = B2_PROXY_ID( proxyKey );\n\n\tB2_ASSERT( typeIndex != b2_staticBody );\n\n\tb2DynamicTree_EnlargeProxy( bp->trees + typeIndex, proxyId, aabb );\n\tb2BufferMove( bp, proxyKey );\n}\n\ntypedef struct b2MovePair\n{\n\tint shapeIndexA;\n\tint shapeIndexB;\n\tb2MovePair* next;\n\tbool heap;\n} b2MovePair;\n\ntypedef struct b2MoveResult\n{\n\tb2MovePair* pairList;\n} b2MoveResult;\n\ntypedef struct b2QueryPairContext\n{\n\tb2World* world;\n\tb2MoveResult* moveResult;\n\tb2BodyType queryTreeType;\n\tint queryProxyKey;\n\tint queryShapeIndex;\n} b2QueryPairContext;\n\n// This is called from b2DynamicTree::Query when we are gathering pairs.\nstatic bool b2PairQueryCallback( int proxyId, uint64_t userData, void* context )\n{\n\tint shapeId = (int)userData;\n\n\tb2QueryPairContext* queryContext = context;\n\tb2BroadPhase* broadPhase = &queryContext->world->broadPhase;\n\n\tint proxyKey = B2_PROXY_KEY( proxyId, queryContext->queryTreeType );\n\tint queryProxyKey = queryContext->queryProxyKey;\n\n\t// A proxy cannot form a pair with itself.\n\tif ( proxyKey == queryContext->queryProxyKey )\n\t{\n\t\treturn true;\n\t}\n\n\tb2BodyType treeType = queryContext->queryTreeType;\n\tb2BodyType queryProxyType = B2_PROXY_TYPE( queryProxyKey );\n\n\t// De-duplication\n\t// It is important to prevent duplicate contacts from being created. Ideally I can prevent duplicates\n\t// early and in the worker. Most of the time the moveSet contains dynamic and kinematic proxies, but\n\t// sometimes it has static proxies.\n\n\t// I had an optimization here to skip checking the move set if this is a query into\n\t// the static tree. The assumption is that the static proxies are never in the move set\n\t// so there is no risk of duplication. However, this is not true with\n\t// b2ShapeDef::invokeContactCreation or when a static shape is modified.\n\t// There can easily be scenarios where the static proxy is in the moveSet but the dynamic proxy is not.\n\t// I could have some flag to indicate that there are any static bodies in the moveSet.\n\n\t// Is this proxy also moving?\n\tif ( queryProxyType == b2_dynamicBody )\n\t{\n\t\tif ( treeType == b2_dynamicBody && proxyKey < queryProxyKey )\n\t\t{\n\t\t\tbool moved = b2ContainsKey( &broadPhase->moveSet, proxyKey + 1 );\n\t\t\tif ( moved )\n\t\t\t{\n\t\t\t\t// Both proxies are moving. Avoid duplicate pairs.\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( treeType == b2_dynamicBody );\n\t\tbool moved = b2ContainsKey( &broadPhase->moveSet, proxyKey + 1 );\n\t\tif ( moved )\n\t\t{\n\t\t\t// Both proxies are moving. Avoid duplicate pairs.\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tuint64_t pairKey = B2_SHAPE_PAIR_KEY( shapeId, queryContext->queryShapeIndex );\n\tif ( b2ContainsKey( &broadPhase->pairSet, pairKey ) )\n\t{\n\t\t// contact exists\n\t\treturn true;\n\t}\n\n\tint shapeIdA, shapeIdB;\n\tif ( proxyKey < queryProxyKey )\n\t{\n\t\tshapeIdA = shapeId;\n\t\tshapeIdB = queryContext->queryShapeIndex;\n\t}\n\telse\n\t{\n\t\tshapeIdA = queryContext->queryShapeIndex;\n\t\tshapeIdB = shapeId;\n\t}\n\n\tb2World* world = queryContext->world;\n\n\tb2Shape* shapeA = b2ShapeArray_Get( &world->shapes, shapeIdA );\n\tb2Shape* shapeB = b2ShapeArray_Get( &world->shapes, shapeIdB );\n\n\tint bodyIdA = shapeA->bodyId;\n\tint bodyIdB = shapeB->bodyId;\n\n\t// Are the shapes on the same body?\n\tif ( bodyIdA == bodyIdB )\n\t{\n\t\treturn true;\n\t}\n\n\t// Sensors are handled elsewhere\n\tif ( shapeA->sensorIndex != B2_NULL_INDEX || shapeB->sensorIndex != B2_NULL_INDEX )\n\t{\n\t\treturn true;\n\t}\n\n\tif ( b2ShouldShapesCollide( shapeA->filter, shapeB->filter ) == false )\n\t{\n\t\treturn true;\n\t}\n\n\t// Does a joint override collision?\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );\n\tif ( b2ShouldBodiesCollide( world, bodyA, bodyB ) == false )\n\t{\n\t\treturn true;\n\t}\n\n\t// Custom user filter\n\tif ( shapeA->enableCustomFiltering || shapeB->enableCustomFiltering )\n\t{\n\t\tb2CustomFilterFcn* customFilterFcn = queryContext->world->customFilterFcn;\n\t\tif ( customFilterFcn != NULL )\n\t\t{\n\t\t\tb2ShapeId idA = { shapeIdA + 1, world->worldId, shapeA->generation };\n\t\t\tb2ShapeId idB = { shapeIdB + 1, world->worldId, shapeB->generation };\n\t\t\tbool shouldCollide = customFilterFcn( idA, idB, queryContext->world->customFilterContext );\n\t\t\tif ( shouldCollide == false )\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// todo per thread to eliminate atomic?\n\tint pairIndex = b2AtomicFetchAddInt( &broadPhase->movePairIndex, 1 );\n\n\tb2MovePair* pair;\n\tif ( pairIndex < broadPhase->movePairCapacity )\n\t{\n\t\tpair = broadPhase->movePairs + pairIndex;\n\t\tpair->heap = false;\n\t}\n\telse\n\t{\n\t\tpair = b2Alloc( sizeof( b2MovePair ) );\n\t\tpair->heap = true;\n\t}\n\n\tpair->shapeIndexA = shapeIdA;\n\tpair->shapeIndexB = shapeIdB;\n\tpair->next = queryContext->moveResult->pairList;\n\tqueryContext->moveResult->pairList = pair;\n\n\t// continue the query\n\treturn true;\n}\n\n// Warning: writing to these globals significantly slows multithreading performance\n#if B2_SNOOP_PAIR_COUNTERS\nb2TreeStats b2_dynamicStats;\nb2TreeStats b2_kinematicStats;\nb2TreeStats b2_staticStats;\n#endif\n\nstatic void b2FindPairsTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )\n{\n\tb2TracyCZoneNC( pair_task, \"Pair\", b2_colorMediumSlateBlue, true );\n\n\tB2_UNUSED( threadIndex );\n\n\tb2World* world = context;\n\tb2BroadPhase* bp = &world->broadPhase;\n\n\tb2QueryPairContext queryContext;\n\tqueryContext.world = world;\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\t// Initialize move result for this moved proxy\n\t\tqueryContext.moveResult = bp->moveResults + i;\n\t\tqueryContext.moveResult->pairList = NULL;\n\n\t\tint proxyKey = bp->moveArray.data[i];\n\t\tif ( proxyKey == B2_NULL_INDEX )\n\t\t{\n\t\t\t// proxy was destroyed after it moved\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2BodyType proxyType = B2_PROXY_TYPE( proxyKey );\n\n\t\tint proxyId = B2_PROXY_ID( proxyKey );\n\t\tqueryContext.queryProxyKey = proxyKey;\n\n\t\tconst b2DynamicTree* baseTree = bp->trees + proxyType;\n\n\t\t// We have to query the tree with the fat AABB so that\n\t\t// we don't fail to create a contact that may touch later.\n\t\tb2AABB fatAABB = b2DynamicTree_GetAABB( baseTree, proxyId );\n\t\tqueryContext.queryShapeIndex = (int)b2DynamicTree_GetUserData( baseTree, proxyId );\n\n\t\t// Query trees. Only dynamic proxies collide with kinematic and static proxies.\n\t\t// Using B2_DEFAULT_MASK_BITS so that b2Filter::groupIndex works.\n\t\tb2TreeStats stats = { 0 };\n\t\tif ( proxyType == b2_dynamicBody )\n\t\t{\n\t\t\t// consider using bits = groupIndex > 0 ? B2_DEFAULT_MASK_BITS : maskBits\n\t\t\tqueryContext.queryTreeType = b2_kinematicBody;\n\t\t\tb2TreeStats statsKinematic = b2DynamicTree_Query( bp->trees + b2_kinematicBody, fatAABB, B2_DEFAULT_MASK_BITS,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  b2PairQueryCallback, &queryContext );\n\t\t\tstats.nodeVisits += statsKinematic.nodeVisits;\n\t\t\tstats.leafVisits += statsKinematic.leafVisits;\n\n\t\t\tqueryContext.queryTreeType = b2_staticBody;\n\t\t\tb2TreeStats statsStatic = b2DynamicTree_Query( bp->trees + b2_staticBody, fatAABB, B2_DEFAULT_MASK_BITS,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   b2PairQueryCallback, &queryContext );\n\t\t\tstats.nodeVisits += statsStatic.nodeVisits;\n\t\t\tstats.leafVisits += statsStatic.leafVisits;\n\t\t}\n\n\t\t// All proxies collide with dynamic proxies\n\t\t// Using B2_DEFAULT_MASK_BITS so that b2Filter::groupIndex works.\n\t\tqueryContext.queryTreeType = b2_dynamicBody;\n\t\tb2TreeStats statsDynamic =\n\t\t\tb2DynamicTree_Query( bp->trees + b2_dynamicBody, fatAABB, B2_DEFAULT_MASK_BITS, b2PairQueryCallback, &queryContext );\n\t\tstats.nodeVisits += statsDynamic.nodeVisits;\n\t\tstats.leafVisits += statsDynamic.leafVisits;\n\t}\n\n\tb2TracyCZoneEnd( pair_task );\n}\n\nvoid b2UpdateBroadPhasePairs( b2World* world )\n{\n\tb2BroadPhase* bp = &world->broadPhase;\n\n\tint moveCount = bp->moveArray.count;\n\tB2_ASSERT( moveCount == (int)bp->moveSet.count );\n\n\tif ( moveCount == 0 )\n\t{\n\t\treturn;\n\t}\n\n\tb2TracyCZoneNC( update_pairs, \"Find Pairs\", b2_colorMediumSlateBlue, true );\n\n\tb2ArenaAllocator* alloc = &world->arena;\n\n\t// todo these could be in the step context\n\tbp->moveResults = b2AllocateArenaItem( alloc, moveCount * sizeof( b2MoveResult ), \"move results\" );\n\tbp->movePairCapacity = 16 * moveCount;\n\tbp->movePairs = b2AllocateArenaItem( alloc, bp->movePairCapacity * sizeof( b2MovePair ), \"move pairs\" );\n\tb2AtomicStoreInt( &bp->movePairIndex, 0 );\n\n#if B2_SNOOP_TABLE_COUNTERS\n\textern b2AtomicInt b2_probeCount;\n\tb2AtomicStoreInt( &b2_probeCount, 0 );\n#endif\n\n\tint minRange = 64;\n\tvoid* userPairTask = world->enqueueTaskFcn( &b2FindPairsTask, moveCount, minRange, world, world->userTaskContext );\n\tif ( userPairTask != NULL )\n\t{\n\t\tworld->finishTaskFcn( userPairTask, world->userTaskContext );\n\t\tworld->taskCount += 1;\n\t}\n\n\t// todo_erin could start tree rebuild here\n\n\tb2TracyCZoneNC( create_contacts, \"Create Contacts\", b2_colorCoral, true );\n\n\t// Single-threaded work\n\t// - Clear move flags\n\t// - Create contacts in deterministic order\n\tfor ( int i = 0; i < moveCount; ++i )\n\t{\n\t\tb2MoveResult* result = bp->moveResults + i;\n\t\tb2MovePair* pair = result->pairList;\n\t\twhile ( pair != NULL )\n\t\t{\n\t\t\tint shapeIdA = pair->shapeIndexA;\n\t\t\tint shapeIdB = pair->shapeIndexB;\n\n\t\t\t// if (s_file != NULL)\n\t\t\t//{\n\t\t\t//\tfprintf(s_file, \"%d %d\\n\", shapeIdA, shapeIdB);\n\t\t\t// }\n\n\t\t\tb2Shape* shapeA = b2ShapeArray_Get( &world->shapes, shapeIdA );\n\t\t\tb2Shape* shapeB = b2ShapeArray_Get( &world->shapes, shapeIdB );\n\n\t\t\tb2CreateContact( world, shapeA, shapeB );\n\n\t\t\tif ( pair->heap )\n\t\t\t{\n\t\t\t\tb2MovePair* temp = pair;\n\t\t\t\tpair = pair->next;\n\t\t\t\tb2Free( temp, sizeof( b2MovePair ) );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpair = pair->next;\n\t\t\t}\n\t\t}\n\n\t\t// if (s_file != NULL)\n\t\t//{\n\t\t//\tfprintf(s_file, \"\\n\");\n\t\t// }\n\t}\n\n\t// if (s_file != NULL)\n\t//{\n\t//\tfprintf(s_file, \"count = %d\\n\\n\", pairCount);\n\t// }\n\n\t// Reset move buffer\n\tb2IntArray_Clear( &bp->moveArray );\n\tb2ClearSet( &bp->moveSet );\n\n\tb2FreeArenaItem( alloc, bp->movePairs );\n\tbp->movePairs = NULL;\n\tb2FreeArenaItem( alloc, bp->moveResults );\n\tbp->moveResults = NULL;\n\n\tb2ValidateSolverSets( world );\n\n\tb2TracyCZoneEnd( create_contacts );\n\n\tb2TracyCZoneEnd( update_pairs );\n}\n\nbool b2BroadPhase_TestOverlap( const b2BroadPhase* bp, int proxyKeyA, int proxyKeyB )\n{\n\tint typeIndexA = B2_PROXY_TYPE( proxyKeyA );\n\tint proxyIdA = B2_PROXY_ID( proxyKeyA );\n\tint typeIndexB = B2_PROXY_TYPE( proxyKeyB );\n\tint proxyIdB = B2_PROXY_ID( proxyKeyB );\n\n\tb2AABB aabbA = b2DynamicTree_GetAABB( bp->trees + typeIndexA, proxyIdA );\n\tb2AABB aabbB = b2DynamicTree_GetAABB( bp->trees + typeIndexB, proxyIdB );\n\treturn b2AABB_Overlaps( aabbA, aabbB );\n}\n\nvoid b2BroadPhase_RebuildTrees( b2BroadPhase* bp )\n{\n\tb2DynamicTree_Rebuild( bp->trees + b2_dynamicBody, false );\n\tb2DynamicTree_Rebuild( bp->trees + b2_kinematicBody, false );\n}\n\nint b2BroadPhase_GetShapeIndex( b2BroadPhase* bp, int proxyKey )\n{\n\tint typeIndex = B2_PROXY_TYPE( proxyKey );\n\tint proxyId = B2_PROXY_ID( proxyKey );\n\n\treturn (int)b2DynamicTree_GetUserData( bp->trees + typeIndex, proxyId );\n}\n\nvoid b2ValidateBroadphase( const b2BroadPhase* bp )\n{\n\tb2DynamicTree_Validate( bp->trees + b2_dynamicBody );\n\tb2DynamicTree_Validate( bp->trees + b2_kinematicBody );\n\n\t// TODO_ERIN validate every shape AABB is contained in tree AABB\n}\n\nvoid b2ValidateNoEnlarged( const b2BroadPhase* bp )\n{\n#if B2_VALIDATE == 1\n\tfor ( int j = 0; j < b2_bodyTypeCount; ++j )\n\t{\n\t\tconst b2DynamicTree* tree = bp->trees + j;\n\t\tb2DynamicTree_ValidateNoEnlarged( tree );\n\t}\n#else\n\tB2_UNUSED( bp );\n#endif\n}\n"
  },
  {
    "path": "src/broad_phase.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n#include \"table.h\"\n\n#include \"box2d/collision.h\"\n#include \"box2d/types.h\"\n\ntypedef struct b2Shape b2Shape;\ntypedef struct b2MovePair b2MovePair;\ntypedef struct b2MoveResult b2MoveResult;\ntypedef struct b2ArenaAllocator b2ArenaAllocator;\ntypedef struct b2World b2World;\n\n// Store the proxy type in the lower 2 bits of the proxy key. This leaves 30 bits for the id.\n#define B2_PROXY_TYPE( KEY ) ( (b2BodyType)( ( KEY ) & 3 ) )\n#define B2_PROXY_ID( KEY ) ( ( KEY ) >> 2 )\n#define B2_PROXY_KEY( ID, TYPE ) ( ( ( ID ) << 2 ) | ( TYPE ) )\n\n/// The broad-phase is used for computing pairs and performing volume queries and ray casts.\n/// This broad-phase does not persist pairs. Instead, this reports potentially new pairs.\n/// It is up to the client to consume the new pairs and to track subsequent overlap.\ntypedef struct b2BroadPhase\n{\n\tb2DynamicTree trees[b2_bodyTypeCount];\n\n\t// The move set and array are used to track shapes that have moved significantly\n\t// and need a pair query for new contacts. The array has a deterministic order.\n\t// todo perhaps just a move set?\n\t// todo implement a 32bit hash set for faster lookup\n\t// todo moveSet can grow quite large on the first time step and remain large\n\tb2HashSet moveSet;\n\tb2IntArray moveArray;\n\n\t// These are the results from the pair query and are used to create new contacts\n\t// in deterministic order.\n\t// todo these could be in the step context\n\tb2MoveResult* moveResults;\n\tb2MovePair* movePairs;\n\tint movePairCapacity;\n\tb2AtomicInt movePairIndex;\n\n\t// Tracks shape pairs that have a b2Contact\n\t// todo pairSet can grow quite large on the first time step and remain large\n\tb2HashSet pairSet;\n\n} b2BroadPhase;\n\nvoid b2CreateBroadPhase( b2BroadPhase* bp );\nvoid b2DestroyBroadPhase( b2BroadPhase* bp );\n\nint b2BroadPhase_CreateProxy( b2BroadPhase* bp, b2BodyType proxyType, b2AABB aabb, uint64_t categoryBits, int shapeIndex,\n\t\t\t\t\t\t\t  bool forcePairCreation );\nvoid b2BroadPhase_DestroyProxy( b2BroadPhase* bp, int proxyKey );\n\nvoid b2BroadPhase_MoveProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb );\nvoid b2BroadPhase_EnlargeProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb );\n\nvoid b2BroadPhase_RebuildTrees( b2BroadPhase* bp );\n\nint b2BroadPhase_GetShapeIndex( b2BroadPhase* bp, int proxyKey );\n\nvoid b2UpdateBroadPhasePairs( b2World* world );\nbool b2BroadPhase_TestOverlap( const b2BroadPhase* bp, int proxyKeyA, int proxyKeyB );\n\nvoid b2ValidateBroadphase( const b2BroadPhase* bp );\nvoid b2ValidateNoEnlarged( const b2BroadPhase* bp );\n\n// This is what triggers new contact pairs to be created\n// Warning: this must be called in deterministic order\nstatic inline void b2BufferMove( b2BroadPhase* bp, int queryProxy )\n{\n\t// Adding 1 because 0 is the sentinel\n\tbool alreadyAdded = b2AddKey( &bp->moveSet, queryProxy + 1 );\n\tif ( alreadyAdded == false )\n\t{\n\t\tb2IntArray_Push( &bp->moveArray, queryProxy );\n\t}\n}\n"
  },
  {
    "path": "src/constants.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\nextern float b2_lengthUnitsPerMeter;\n\n// Used to detect bad values. Positions greater than about 16km will have precision\n// problems, so 100km as a limit should be fine in all cases.\n#define B2_HUGE ( 100000.0f * b2_lengthUnitsPerMeter )\n\n// Maximum parallel workers. Used to size some static arrays.\n#define B2_MAX_WORKERS 64\n\n// Maximum number of colors in the constraint graph. Constraints that cannot\n// find a color are added to the overflow set which are solved single-threaded.\n// The compound barrel benchmark has minor overflow with 24 colors \n#define B2_GRAPH_COLOR_COUNT 24\n\n// A small length used as a collision and constraint tolerance. Usually it is\n// chosen to be numerically significant, but visually insignificant. In meters.\n// Normally this is 0.5cm.\n// @warning modifying this can have a significant impact on stability\n#define B2_LINEAR_SLOP ( 0.005f * b2_lengthUnitsPerMeter )\n\n// Maximum number of simultaneous worlds that can be allocated\n#ifndef B2_MAX_WORLDS\n#define B2_MAX_WORLDS 128\n#endif\n\n// The maximum rotation of a body per time step. This limit is very large and is used\n// to prevent numerical problems. You shouldn't need to adjust this.\n// @warning increasing this to 0.5f * b2_pi or greater will break continuous collision.\n#define B2_MAX_ROTATION ( 0.25f * B2_PI )\n\n// Box2D uses limited speculative collision. This reduces jitter.\n// Normally this is 2cm.\n// @warning modifying this can have a significant impact on performance and stability\n#define B2_SPECULATIVE_DISTANCE ( 4.0f * B2_LINEAR_SLOP )\n\n// This is used to fatten AABBs in the dynamic tree. This allows proxies\n// to move by a small amount without triggering a tree adjustment. This is in meters.\n// Normally this is 5cm.\n// @warning modifying this can have a significant impact on performance\n#define B2_AABB_MARGIN ( 0.05f * b2_lengthUnitsPerMeter )\n\n// The time that a body must be still before it will go to sleep. In seconds.\n#define B2_TIME_TO_SLEEP 0.5f\n\nenum b2TreeNodeFlags\n{\n\tb2_allocatedNode = 0x0001,\n\tb2_enlargedNode = 0x0002,\n\tb2_leafNode = 0x0004,\n};\n"
  },
  {
    "path": "src/constraint_graph.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"constraint_graph.h\"\n\n#include \"array.h\"\n#include \"bitset.h\"\n#include \"body.h\"\n#include \"contact.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"solver_set.h\"\n\n#include <string.h>\n\n// Solver using graph coloring. Islands are only used for sleep.\n// High-Performance Physical Simulations on Next-Generation Architecture with Many Cores\n// http://web.eecs.umich.edu/~msmelyan/papers/physsim_onmanycore_itj.pdf\n\n// Kinematic bodies have to be treated like dynamic bodies in graph coloring. Unlike static bodies, we cannot use a dummy solver\n// body for kinematic bodies. We cannot access a kinematic body from multiple threads efficiently because the SIMD solver body\n// scatter would write to the same kinematic body from multiple threads. Even if these writes don't modify the body, they will\n// cause horrible cache stalls. To make this feasible I would need a way to block these writes.\n// todo should be possible to branch on the scatters to avoid writing to kinematic bodies\n\n// This is used for debugging by making all constraints be assigned to overflow.\n#define B2_FORCE_OVERFLOW 0\n\nvoid b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity )\n{\n\t_Static_assert( B2_GRAPH_COLOR_COUNT >= 2, \"must have at least two constraint graph colors\" );\n\t_Static_assert( B2_OVERFLOW_INDEX == B2_GRAPH_COLOR_COUNT - 1, \"bad over flow index\" );\n\t_Static_assert( B2_DYNAMIC_COLOR_COUNT >= 2, \"need more dynamic colors\" );\n\n\t*graph = (b2ConstraintGraph){ 0 };\n\n\tbodyCapacity = b2MaxInt( bodyCapacity, 8 );\n\n\t// Initialize graph color bit set.\n\t// No bitset for overflow color.\n\tfor ( int i = 0; i < B2_OVERFLOW_INDEX; ++i )\n\t{\n\t\tb2GraphColor* color = graph->colors + i;\n\t\tcolor->bodySet = b2CreateBitSet( bodyCapacity );\n\t\tb2SetBitCountAndClear( &color->bodySet, bodyCapacity );\n\t}\n}\n\nvoid b2DestroyGraph( b2ConstraintGraph* graph )\n{\n\tfor ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )\n\t{\n\t\tb2GraphColor* color = graph->colors + i;\n\n\t\t// The bit set should never be used on the overflow color\n\t\tB2_ASSERT( i != B2_OVERFLOW_INDEX || color->bodySet.bits == NULL );\n\n\t\tb2DestroyBitSet( &color->bodySet );\n\n\t\tb2ContactSimArray_Destroy( &color->contactSims );\n\t\tb2JointSimArray_Destroy( &color->jointSims );\n\t}\n}\n\n// Contacts are always created as non-touching. They get cloned into the constraint\n// graph once they are found to be touching.\nvoid b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* contact )\n{\n\tB2_ASSERT( contactSim->manifold.pointCount > 0 );\n\tB2_ASSERT( contactSim->simFlags & b2_simTouchingFlag );\n\tB2_ASSERT( contact->flags & b2_contactTouchingFlag );\n\n\tb2ConstraintGraph* graph = &world->constraintGraph;\n\tint colorIndex = B2_OVERFLOW_INDEX;\n\n\tint bodyIdA = contact->edges[0].bodyId;\n\tint bodyIdB = contact->edges[1].bodyId;\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );\n\n\tb2BodyType typeA = bodyA->type;\n\tb2BodyType typeB = bodyB->type;\n\tB2_ASSERT( typeA == b2_dynamicBody || typeB == b2_dynamicBody );\n\n#if B2_FORCE_OVERFLOW == 0\n\tif (typeA == b2_dynamicBody && typeB == b2_dynamicBody)\n\t{\n\t\t// Dynamic constraint colors cannot encroach on colors reserved for static constraints\n\t\tfor ( int i = 0; i < B2_DYNAMIC_COLOR_COUNT; ++i )\n\t\t{\n\t\t\tb2GraphColor* color = graph->colors + i;\n\t\t\tif ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2SetBitGrow( &color->bodySet, bodyIdA );\n\t\t\tb2SetBitGrow( &color->bodySet, bodyIdB );\n\t\t\tcolorIndex = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\telse if ( typeA == b2_dynamicBody )\n\t{\n\t\t// Static constraint colors build from the end to get higher priority than dyn-dyn constraints\n\t\tfor ( int i = B2_OVERFLOW_INDEX - 1; i >= 1; --i )\n\t\t{\n\t\t\tb2GraphColor* color = graph->colors + i;\n\t\t\tif ( b2GetBit( &color->bodySet, bodyIdA ) )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2SetBitGrow( &color->bodySet, bodyIdA );\n\t\t\tcolorIndex = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\telse if ( typeB == b2_dynamicBody )\n\t{\n\t\t// Static constraint colors build from the end to get higher priority than dyn-dyn constraints\n\t\tfor ( int i = B2_OVERFLOW_INDEX - 1; i >= 1; --i )\n\t\t{\n\t\t\tb2GraphColor* color = graph->colors + i;\n\t\t\tif ( b2GetBit( &color->bodySet, bodyIdB ) )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2SetBitGrow( &color->bodySet, bodyIdB );\n\t\t\tcolorIndex = i;\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\n\tb2GraphColor* color = graph->colors + colorIndex;\n\tcontact->colorIndex = colorIndex;\n\tcontact->localIndex = color->contactSims.count;\n\n\tb2ContactSim* newContact = b2ContactSimArray_Add( &color->contactSims );\n\tmemcpy( newContact, contactSim, sizeof( b2ContactSim ) );\n\n\t// todo perhaps skip this if the contact is already awake\n\n\tif ( typeA == b2_staticBody )\n\t{\n\t\tnewContact->bodySimIndexA = B2_NULL_INDEX;\n\t\tnewContact->invMassA = 0.0f;\n\t\tnewContact->invIA = 0.0f;\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( bodyA->setIndex == b2_awakeSet );\n\t\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\n\t\tint localIndex = bodyA->localIndex;\n\t\tnewContact->bodySimIndexA = localIndex;\n\n\t\tb2BodySim* bodySimA = b2BodySimArray_Get( &awakeSet->bodySims, localIndex );\n\t\tnewContact->invMassA = bodySimA->invMass;\n\t\tnewContact->invIA = bodySimA->invInertia;\n\t}\n\n\tif ( typeB == b2_staticBody )\n\t{\n\t\tnewContact->bodySimIndexB = B2_NULL_INDEX;\n\t\tnewContact->invMassB = 0.0f;\n\t\tnewContact->invIB = 0.0f;\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( bodyB->setIndex == b2_awakeSet );\n\t\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\n\t\tint localIndex = bodyB->localIndex;\n\t\tnewContact->bodySimIndexB = localIndex;\n\n\t\tb2BodySim* bodySimB = b2BodySimArray_Get( &awakeSet->bodySims, localIndex );\n\t\tnewContact->invMassB = bodySimB->invMass;\n\t\tnewContact->invIB = bodySimB->invInertia;\n\t}\n}\n\nvoid b2RemoveContactFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex )\n{\n\tb2ConstraintGraph* graph = &world->constraintGraph;\n\n\tB2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );\n\tb2GraphColor* color = graph->colors + colorIndex;\n\n\tif ( colorIndex != B2_OVERFLOW_INDEX )\n\t{\n\t\t// This might clear a bit for a kinematic or static body, but this has no effect\n\t\tb2ClearBit( &color->bodySet, bodyIdA );\n\t\tb2ClearBit( &color->bodySet, bodyIdB );\n\t}\n\n\tint movedIndex = b2ContactSimArray_RemoveSwap( &color->contactSims, localIndex );\n\tif ( movedIndex != B2_NULL_INDEX )\n\t{\n\t\t// Fix index on swapped contact\n\t\tb2ContactSim* movedContactSim = color->contactSims.data + localIndex;\n\n\t\t// Fix moved contact\n\t\tint movedId = movedContactSim->contactId;\n\t\tb2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedId );\n\t\tB2_ASSERT( movedContact->setIndex == b2_awakeSet );\n\t\tB2_ASSERT( movedContact->colorIndex == colorIndex );\n\t\tB2_ASSERT( movedContact->localIndex == movedIndex );\n\t\tmovedContact->localIndex = localIndex;\n\t}\n}\n\nstatic int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyIdB, b2BodyType typeA, b2BodyType typeB )\n{\n\tB2_ASSERT( typeA == b2_dynamicBody || typeB == b2_dynamicBody );\n\n#if B2_FORCE_OVERFLOW == 0\n\tif ( typeA == b2_dynamicBody && typeB == b2_dynamicBody )\n\t{\n\t\t// Dynamic constraint colors cannot encroach on colors reserved for static constraints\n\t\tfor ( int i = 0; i < B2_DYNAMIC_COLOR_COUNT; ++i )\n\t\t{\n\t\t\tb2GraphColor* color = graph->colors + i;\n\t\t\tif ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2SetBitGrow( &color->bodySet, bodyIdA );\n\t\t\tb2SetBitGrow( &color->bodySet, bodyIdB );\n\t\t\treturn i;\n\t\t}\n\t}\n\telse if ( typeA == b2_dynamicBody )\n\t{\n\t\t// Static constraint colors build from the end to get higher priority than dyn-dyn constraints\n\t\tfor ( int i = B2_OVERFLOW_INDEX - 1; i >= 1; --i )\n\t\t{\n\t\t\tb2GraphColor* color = graph->colors + i;\n\t\t\tif ( b2GetBit( &color->bodySet, bodyIdA ) )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2SetBitGrow( &color->bodySet, bodyIdA );\n\t\t\treturn i;\n\t\t}\n\t}\n\telse if ( typeB == b2_dynamicBody )\n\t{\n\t\t// Static constraint colors build from the end to get higher priority than dyn-dyn constraints\n\t\tfor ( int i = B2_OVERFLOW_INDEX - 1; i >= 1; --i )\n\t\t{\n\t\t\tb2GraphColor* color = graph->colors + i;\n\t\t\tif ( b2GetBit( &color->bodySet, bodyIdB ) )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2SetBitGrow( &color->bodySet, bodyIdB );\n\t\t\treturn i;\n\t\t}\n\t}\n#else\n\tB2_UNUSED( graph, bodyIdA, bodyIdB );\n#endif\n\n\treturn B2_OVERFLOW_INDEX;\n}\n\nb2JointSim* b2CreateJointInGraph( b2World* world, b2Joint* joint )\n{\n\tb2ConstraintGraph* graph = &world->constraintGraph;\n\n\tint bodyIdA = joint->edges[0].bodyId;\n\tint bodyIdB = joint->edges[1].bodyId;\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );\n\n\tint colorIndex = b2AssignJointColor( graph, bodyIdA, bodyIdB, bodyA->type, bodyB->type );\n\n\tb2JointSim* jointSim = b2JointSimArray_Add( &graph->colors[colorIndex].jointSims );\n\tmemset( jointSim, 0, sizeof( b2JointSim ) );\n\n\tjoint->colorIndex = colorIndex;\n\tjoint->localIndex = graph->colors[colorIndex].jointSims.count - 1;\n\treturn jointSim;\n}\n\nvoid b2AddJointToGraph( b2World* world, b2JointSim* jointSim, b2Joint* joint )\n{\n\tb2JointSim* jointDst = b2CreateJointInGraph( world, joint );\n\tmemcpy( jointDst, jointSim, sizeof( b2JointSim ) );\n}\n\nvoid b2RemoveJointFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex )\n{\n\tb2ConstraintGraph* graph = &world->constraintGraph;\n\n\tB2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );\n\tb2GraphColor* color = graph->colors + colorIndex;\n\n\tif ( colorIndex != B2_OVERFLOW_INDEX )\n\t{\n\t\t// May clear static bodies, no effect\n\t\tb2ClearBit( &color->bodySet, bodyIdA );\n\t\tb2ClearBit( &color->bodySet, bodyIdB );\n\t}\n\n\tint movedIndex = b2JointSimArray_RemoveSwap( &color->jointSims, localIndex );\n\tif ( movedIndex != B2_NULL_INDEX )\n\t{\n\t\t// Fix moved joint\n\t\tb2JointSim* movedJointSim = color->jointSims.data + localIndex;\n\t\tint movedId = movedJointSim->jointId;\n\t\tb2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId );\n\t\tB2_ASSERT( movedJoint->setIndex == b2_awakeSet );\n\t\tB2_ASSERT( movedJoint->colorIndex == colorIndex );\n\t\tB2_ASSERT( movedJoint->localIndex == movedIndex );\n\t\tmovedJoint->localIndex = localIndex;\n\t}\n}\n\nb2HexColor b2_graphColors[B2_GRAPH_COLOR_COUNT] = {\n\tb2_colorRed,\tb2_colorOrange, b2_colorYellow,\t   b2_colorGreen,\t  b2_colorCyan,\t\tb2_colorBlue,\n\tb2_colorViolet, b2_colorPink,\tb2_colorChocolate, b2_colorGoldenRod, b2_colorCoral,\tb2_colorRosyBrown,\n\tb2_colorAqua,\tb2_colorPeru,\tb2_colorLime,\t   b2_colorGold,\t  b2_colorPlum,\t\tb2_colorSnow,\n\tb2_colorTeal,\tb2_colorKhaki,\tb2_colorSalmon,\t   b2_colorPeachPuff, b2_colorHoneyDew, b2_colorBlack,\n};\n"
  },
  {
    "path": "src/constraint_graph.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n#include \"bitset.h\"\n#include \"constants.h\"\n\n#include \"box2d/types.h\"\n\ntypedef struct b2Body b2Body;\ntypedef struct b2ContactSim b2ContactSim;\ntypedef struct b2Contact b2Contact;\ntypedef struct b2ContactConstraint b2ContactConstraint;\ntypedef struct b2ContactConstraintSIMD b2ContactConstraintSIMD;\ntypedef struct b2JointSim b2JointSim;\ntypedef struct b2Joint b2Joint;\ntypedef struct b2StepContext b2StepContext;\ntypedef struct b2World b2World;\n\n// This holds constraints that cannot fit the graph color limit. This happens when a single dynamic body\n// is touching many other bodies.\n#define B2_OVERFLOW_INDEX (B2_GRAPH_COLOR_COUNT - 1)\n\n// This keeps constraints involving two dynamic bodies at a lower solver priority than constraints\n// involving a dynamic and static bodies. This reduces tunneling due to push through.\n#define B2_DYNAMIC_COLOR_COUNT (B2_GRAPH_COLOR_COUNT - 4)\n\ntypedef struct b2GraphColor\n{\n\t// This bitset is indexed by bodyId so this is over-sized to encompass static bodies\n\t// however I never traverse these bits or use the bit count for anything\n\t// This bitset is unused on the overflow color.\n\t//\n\t// Dirk suggested having a uint64_t per body that tracks the graph color membership\n\t// but I think this would make debugging harder and be less flexible. With the bitset\n\t// I can trivially increase the number of graph colors beyond 64. See usage of b2CountSetBits\n\t// for validation.\n\tb2BitSet bodySet;\n\n\t// cache friendly arrays\n\tb2ContactSimArray contactSims;\n\tb2JointSimArray jointSims;\n\n\t// transient\n\tunion\n\t{\n\t\tb2ContactConstraintSIMD* simdConstraints;\n\t\tb2ContactConstraint* overflowConstraints;\n\t};\n} b2GraphColor;\n\ntypedef struct b2ConstraintGraph\n{\n\t// including overflow at the end\n\tb2GraphColor colors[B2_GRAPH_COLOR_COUNT];\n} b2ConstraintGraph;\n\nvoid b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity );\nvoid b2DestroyGraph( b2ConstraintGraph* graph );\n\nvoid b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* contact );\nvoid b2RemoveContactFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex );\n\nb2JointSim* b2CreateJointInGraph( b2World* world, b2Joint* joint );\nvoid b2AddJointToGraph( b2World* world, b2JointSim* jointSim, b2Joint* joint );\nvoid b2RemoveJointFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex );\n\nextern b2HexColor b2_graphColors[B2_GRAPH_COLOR_COUNT];\n"
  },
  {
    "path": "src/contact.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"contact.h\"\n\n#include \"array.h\"\n#include \"body.h\"\n#include \"core.h\"\n#include \"island.h\"\n#include \"physics_world.h\"\n#include \"shape.h\"\n#include \"solver_set.h\"\n#include \"table.h\"\n\n// needed for dll export\n#include \"box2d/box2d.h\"\n\n#include <stddef.h>\n\nB2_ARRAY_SOURCE( b2Contact, b2Contact )\nB2_ARRAY_SOURCE( b2ContactSim, b2ContactSim )\n\n// Contacts and determinism\n// A deterministic simulation requires contacts to exist in the same order in b2Island no matter the thread count.\n// The order must reproduce from run to run. This is necessary because the Gauss-Seidel constraint solver is order dependent.\n//\n// Creation:\n// - Contacts are created using results from b2UpdateBroadPhasePairs\n// - These results are ordered according to the order of the broad-phase move array\n// - The move array is ordered according to the shape creation order using a bitset.\n// - The island/shape/body order is determined by creation order\n// - Logically contacts are only created for awake bodies, so they are immediately added to the awake contact array (serially)\n//\n// Island linking:\n// - The awake contact array is built from the body-contact graph for all awake bodies in awake islands.\n// - Awake contacts are solved in parallel and they generate contact state changes.\n// - These state changes may link islands together using union find.\n// - The state changes are ordered using a bit array that encompasses all contacts\n// - As long as contacts are created in deterministic order, island link order is deterministic.\n// - This keeps the order of contacts in islands deterministic\n\n// Manifold functions should compute important results in local space to improve precision. However, this\n// interface function takes two world transforms instead of a relative transform for these reasons:\n//\n// First:\n// The anchors need to be computed relative to the shape origin in world space. This is necessary so the\n// solver does not need to access static body transforms. Not even in constraint preparation. This approach\n// has world space vectors yet retains precision.\n//\n// Second:\n// b2ManifoldPoint::point is very useful for debugging and it is in world space.\n//\n// Third:\n// The user may call the manifold functions directly and they should be easy to use and have easy to use\n// results.\n\nstatic b2Contact* b2GetContactFullId( b2World* world, b2ContactId contactId )\n{\n\tint id = contactId.index1 - 1;\n\tb2Contact* contact = b2ContactArray_Get( &world->contacts, id );\n\tB2_ASSERT( contact->contactId == id && contact->generation == contactId.generation );\n\treturn contact;\n}\n\nb2ContactData b2Contact_GetData( b2ContactId contactId )\n{\n\tb2World* world = b2GetWorld( contactId.world0 );\n\tb2Contact* contact = b2GetContactFullId( world, contactId );\n\tb2ContactSim* contactSim = b2GetContactSim( world, contact );\n\tconst b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contact->shapeIdA );\n\tconst b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contact->shapeIdB );\n\n\tb2ContactData data = {\n\t\t.contactId = contactId,\n\t\t.shapeIdA =\n\t\t\t{\n\t\t\t\t.index1 = shapeA->id + 1,\n\t\t\t\t.world0 = (uint16_t)contactId.world0,\n\t\t\t\t.generation = shapeA->generation,\n\t\t\t},\n\t\t.shapeIdB =\n\t\t\t{\n\t\t\t\t.index1 = shapeB->id + 1,\n\t\t\t\t.world0 = (uint16_t)contactId.world0,\n\t\t\t\t.generation = shapeB->generation,\n\t\t\t},\n\t\t.manifold = contactSim->manifold,\n\t};\n\n\treturn data;\n}\n\ntypedef b2Manifold b2ManifoldFcn( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t  b2SimplexCache* cache );\n\nstruct b2ContactRegister\n{\n\tb2ManifoldFcn* fcn;\n\tbool primary;\n};\n\nstatic struct b2ContactRegister s_registers[b2_shapeTypeCount][b2_shapeTypeCount];\nstatic bool s_initialized = false;\n\nstatic b2Manifold b2CircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\tb2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollideCircles( &shapeA->circle, xfA, &shapeB->circle, xfB );\n}\n\nstatic b2Manifold b2CapsuleAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\t\t\t  b2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollideCapsuleAndCircle( &shapeA->capsule, xfA, &shapeB->circle, xfB );\n}\n\nstatic b2Manifold b2CapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\t b2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollideCapsules( &shapeA->capsule, xfA, &shapeB->capsule, xfB );\n}\n\nstatic b2Manifold b2PolygonAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\t\t\t  b2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollidePolygonAndCircle( &shapeA->polygon, xfA, &shapeB->circle, xfB );\n}\n\nstatic b2Manifold b2PolygonAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\t\t\t   b2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollidePolygonAndCapsule( &shapeA->polygon, xfA, &shapeB->capsule, xfB );\n}\n\nstatic b2Manifold b2PolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\t b2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollidePolygons( &shapeA->polygon, xfA, &shapeB->polygon, xfB );\n}\n\nstatic b2Manifold b2SegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\t\t\t  b2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollideSegmentAndCircle( &shapeA->segment, xfA, &shapeB->circle, xfB );\n}\n\nstatic b2Manifold b2SegmentAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\t\t\t   b2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollideSegmentAndCapsule( &shapeA->segment, xfA, &shapeB->capsule, xfB );\n}\n\nstatic b2Manifold b2SegmentAndPolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\t\t\t   b2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollideSegmentAndPolygon( &shapeA->segment, xfA, &shapeB->polygon, xfB );\n}\n\nstatic b2Manifold b2ChainSegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,\n\t\t\t\t\t\t\t\t\t\t\t\t   b2SimplexCache* cache )\n{\n\tB2_UNUSED( cache );\n\treturn b2CollideChainSegmentAndCircle( &shapeA->chainSegment, xfA, &shapeB->circle, xfB );\n}\n\nstatic b2Manifold b2ChainSegmentAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB,\n\t\t\t\t\t\t\t\t\t\t\t\t\tb2Transform xfB, b2SimplexCache* cache )\n{\n\treturn b2CollideChainSegmentAndCapsule( &shapeA->chainSegment, xfA, &shapeB->capsule, xfB, cache );\n}\n\nstatic b2Manifold b2ChainSegmentAndPolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB,\n\t\t\t\t\t\t\t\t\t\t\t\t\tb2Transform xfB, b2SimplexCache* cache )\n{\n\treturn b2CollideChainSegmentAndPolygon( &shapeA->chainSegment, xfA, &shapeB->polygon, xfB, cache );\n}\n\nstatic void b2AddType( b2ManifoldFcn* fcn, b2ShapeType type1, b2ShapeType type2 )\n{\n\tB2_ASSERT( 0 <= type1 && type1 < b2_shapeTypeCount );\n\tB2_ASSERT( 0 <= type2 && type2 < b2_shapeTypeCount );\n\n\ts_registers[type1][type2].fcn = fcn;\n\ts_registers[type1][type2].primary = true;\n\n\tif ( type1 != type2 )\n\t{\n\t\ts_registers[type2][type1].fcn = fcn;\n\t\ts_registers[type2][type1].primary = false;\n\t}\n}\n\nvoid b2InitializeContactRegisters( void )\n{\n\tif ( s_initialized == false )\n\t{\n\t\tb2AddType( b2CircleManifold, b2_circleShape, b2_circleShape );\n\t\tb2AddType( b2CapsuleAndCircleManifold, b2_capsuleShape, b2_circleShape );\n\t\tb2AddType( b2CapsuleManifold, b2_capsuleShape, b2_capsuleShape );\n\t\tb2AddType( b2PolygonAndCircleManifold, b2_polygonShape, b2_circleShape );\n\t\tb2AddType( b2PolygonAndCapsuleManifold, b2_polygonShape, b2_capsuleShape );\n\t\tb2AddType( b2PolygonManifold, b2_polygonShape, b2_polygonShape );\n\t\tb2AddType( b2SegmentAndCircleManifold, b2_segmentShape, b2_circleShape );\n\t\tb2AddType( b2SegmentAndCapsuleManifold, b2_segmentShape, b2_capsuleShape );\n\t\tb2AddType( b2SegmentAndPolygonManifold, b2_segmentShape, b2_polygonShape );\n\t\tb2AddType( b2ChainSegmentAndCircleManifold, b2_chainSegmentShape, b2_circleShape );\n\t\tb2AddType( b2ChainSegmentAndCapsuleManifold, b2_chainSegmentShape, b2_capsuleShape );\n\t\tb2AddType( b2ChainSegmentAndPolygonManifold, b2_chainSegmentShape, b2_polygonShape );\n\t\ts_initialized = true;\n\t}\n}\n\nvoid b2CreateContact( b2World* world, b2Shape* shapeA, b2Shape* shapeB )\n{\n\tb2ShapeType type1 = shapeA->type;\n\tb2ShapeType type2 = shapeB->type;\n\n\tB2_ASSERT( 0 <= type1 && type1 < b2_shapeTypeCount );\n\tB2_ASSERT( 0 <= type2 && type2 < b2_shapeTypeCount );\n\n\tif ( s_registers[type1][type2].fcn == NULL )\n\t{\n\t\t// For example, no segment vs segment collision\n\t\treturn;\n\t}\n\n\tif ( s_registers[type1][type2].primary == false )\n\t{\n\t\t// flip order\n\t\tb2CreateContact( world, shapeB, shapeA );\n\t\treturn;\n\t}\n\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, shapeA->bodyId );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, shapeB->bodyId );\n\n\tB2_ASSERT( bodyA->setIndex != b2_disabledSet && bodyB->setIndex != b2_disabledSet );\n\tB2_ASSERT( bodyA->setIndex != b2_staticSet || bodyB->setIndex != b2_staticSet );\n\n\tint setIndex;\n\tif ( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet )\n\t{\n\t\tsetIndex = b2_awakeSet;\n\t}\n\telse\n\t{\n\t\t// sleeping and non-touching contacts live in the disabled set\n\t\t// later if this set is found to be touching then the sleeping\n\t\t// islands will be linked and the contact moved to the merged island\n\t\tsetIndex = b2_disabledSet;\n\t}\n\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );\n\n\t// Create contact key and contact\n\tint contactId = b2AllocId( &world->contactIdPool );\n\tif ( contactId == world->contacts.count )\n\t{\n\t\tb2ContactArray_Push( &world->contacts, (b2Contact){ 0 } );\n\t}\n\n\tint shapeIdA = shapeA->id;\n\tint shapeIdB = shapeB->id;\n\n\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\tcontact->contactId = contactId;\n\tcontact->generation += 1;\n\tcontact->setIndex = setIndex;\n\tcontact->colorIndex = B2_NULL_INDEX;\n\tcontact->localIndex = set->contactSims.count;\n\tcontact->islandId = B2_NULL_INDEX;\n\tcontact->islandPrev = B2_NULL_INDEX;\n\tcontact->islandNext = B2_NULL_INDEX;\n\tcontact->shapeIdA = shapeIdA;\n\tcontact->shapeIdB = shapeIdB;\n\t//contact->isMarked = false;\n\tcontact->flags = 0;\n\n\tB2_ASSERT( shapeA->sensorIndex == B2_NULL_INDEX && shapeB->sensorIndex == B2_NULL_INDEX );\n\n\tif ( shapeA->enableContactEvents || shapeB->enableContactEvents )\n\t{\n\t\tcontact->flags |= b2_contactEnableContactEvents;\n\t}\n\n\t// Connect to body A\n\t{\n\t\tcontact->edges[0].bodyId = shapeA->bodyId;\n\t\tcontact->edges[0].prevKey = B2_NULL_INDEX;\n\t\tcontact->edges[0].nextKey = bodyA->headContactKey;\n\n\t\tint keyA = ( contactId << 1 ) | 0;\n\t\tint headContactKey = bodyA->headContactKey;\n\t\tif ( headContactKey != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Contact* headContact = b2ContactArray_Get( &world->contacts, headContactKey >> 1 );\n\t\t\theadContact->edges[headContactKey & 1].prevKey = keyA;\n\t\t}\n\t\tbodyA->headContactKey = keyA;\n\t\tbodyA->contactCount += 1;\n\t}\n\n\t// Connect to body B\n\t{\n\t\tcontact->edges[1].bodyId = shapeB->bodyId;\n\t\tcontact->edges[1].prevKey = B2_NULL_INDEX;\n\t\tcontact->edges[1].nextKey = bodyB->headContactKey;\n\n\t\tint keyB = ( contactId << 1 ) | 1;\n\t\tint headContactKey = bodyB->headContactKey;\n\t\tif ( bodyB->headContactKey != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Contact* headContact = b2ContactArray_Get( &world->contacts, headContactKey >> 1 );\n\t\t\theadContact->edges[headContactKey & 1].prevKey = keyB;\n\t\t}\n\t\tbodyB->headContactKey = keyB;\n\t\tbodyB->contactCount += 1;\n\t}\n\n\t// Add to pair set for fast lookup\n\tuint64_t pairKey = B2_SHAPE_PAIR_KEY( shapeIdA, shapeIdB );\n\tb2AddKey( &world->broadPhase.pairSet, pairKey );\n\n\t// Contacts are created as non-touching. Later if they are found to be touching\n\t// they will link islands and be moved into the constraint graph.\n\tb2ContactSim* contactSim = b2ContactSimArray_Add( &set->contactSims );\n\tcontactSim->contactId = contactId;\n\n#if B2_VALIDATE\n\tcontactSim->bodyIdA = shapeA->bodyId;\n\tcontactSim->bodyIdB = shapeB->bodyId;\n#endif\n\n\tcontactSim->bodySimIndexA = B2_NULL_INDEX;\n\tcontactSim->bodySimIndexB = B2_NULL_INDEX;\n\tcontactSim->invMassA = 0.0f;\n\tcontactSim->invIA = 0.0f;\n\tcontactSim->invMassB = 0.0f;\n\tcontactSim->invIB = 0.0f;\n\tcontactSim->shapeIdA = shapeIdA;\n\tcontactSim->shapeIdB = shapeIdB;\n\tcontactSim->cache = b2_emptySimplexCache;\n\tcontactSim->manifold = (b2Manifold){ 0 };\n\n\t// These also get updated in the narrow phase\n\tcontactSim->friction = world->frictionCallback( shapeA->material.friction, shapeA->material.userMaterialId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tshapeB->material.friction, shapeB->material.userMaterialId );\n\tcontactSim->restitution = world->restitutionCallback( shapeA->material.restitution, shapeA->material.userMaterialId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shapeB->material.restitution, shapeB->material.userMaterialId );\n\n\tcontactSim->tangentSpeed = 0.0f;\n\tcontactSim->simFlags = 0;\n\n\tif ( shapeA->enablePreSolveEvents || shapeB->enablePreSolveEvents )\n\t{\n\t\tcontactSim->simFlags |= b2_simEnablePreSolveEvents;\n\t}\n}\n\n// A contact is destroyed when:\n// - broad-phase proxies stop overlapping\n// - a body is destroyed\n// - a body is disabled\n// - a body changes type from dynamic to kinematic or static\n// - a shape is destroyed\n// - contact filtering is modified\nvoid b2DestroyContact( b2World* world, b2Contact* contact, bool wakeBodies )\n{\n\t// Remove pair from set\n\tuint64_t pairKey = B2_SHAPE_PAIR_KEY( contact->shapeIdA, contact->shapeIdB );\n\tb2RemoveKey( &world->broadPhase.pairSet, pairKey );\n\n\tb2ContactEdge* edgeA = contact->edges + 0;\n\tb2ContactEdge* edgeB = contact->edges + 1;\n\n\tint bodyIdA = edgeA->bodyId;\n\tint bodyIdB = edgeB->bodyId;\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );\n\n\tuint32_t flags = contact->flags;\n\tbool touching = ( flags & b2_contactTouchingFlag ) != 0;\n\n\t// End touch event\n\tif ( touching && ( flags & b2_contactEnableContactEvents ) != 0 )\n\t{\n\t\tuint16_t worldId = world->worldId;\n\t\tconst b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contact->shapeIdA );\n\t\tconst b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contact->shapeIdB );\n\t\tb2ShapeId shapeIdA = { shapeA->id + 1, worldId, shapeA->generation };\n\t\tb2ShapeId shapeIdB = { shapeB->id + 1, worldId, shapeB->generation };\n\n\t\tb2ContactId contactId = {\n\t\t\t.index1 = contact->contactId + 1,\n\t\t\t.world0 = world->worldId,\n\t\t\t.padding = 0,\n\t\t\t.generation = contact->generation,\n\t\t};\n\n\t\tb2ContactEndTouchEvent event = {\n\t\t\t.shapeIdA = shapeIdA,\n\t\t\t.shapeIdB = shapeIdB,\n\t\t\t.contactId = contactId,\n\t\t};\n\n\t\tb2ContactEndTouchEventArray_Push( world->contactEndEvents + world->endEventArrayIndex, event );\n\t}\n\n\t// Remove from body A\n\tif ( edgeA->prevKey != B2_NULL_INDEX )\n\t{\n\t\tb2Contact* prevContact = b2ContactArray_Get( &world->contacts, edgeA->prevKey >> 1 );\n\t\tb2ContactEdge* prevEdge = prevContact->edges + ( edgeA->prevKey & 1 );\n\t\tprevEdge->nextKey = edgeA->nextKey;\n\t}\n\n\tif ( edgeA->nextKey != B2_NULL_INDEX )\n\t{\n\t\tb2Contact* nextContact = b2ContactArray_Get( &world->contacts, edgeA->nextKey >> 1 );\n\t\tb2ContactEdge* nextEdge = nextContact->edges + ( edgeA->nextKey & 1 );\n\t\tnextEdge->prevKey = edgeA->prevKey;\n\t}\n\n\tint contactId = contact->contactId;\n\n\tint edgeKeyA = ( contactId << 1 ) | 0;\n\tif ( bodyA->headContactKey == edgeKeyA )\n\t{\n\t\tbodyA->headContactKey = edgeA->nextKey;\n\t}\n\n\tbodyA->contactCount -= 1;\n\n\t// Remove from body B\n\tif ( edgeB->prevKey != B2_NULL_INDEX )\n\t{\n\t\tb2Contact* prevContact = b2ContactArray_Get( &world->contacts, edgeB->prevKey >> 1 );\n\t\tb2ContactEdge* prevEdge = prevContact->edges + ( edgeB->prevKey & 1 );\n\t\tprevEdge->nextKey = edgeB->nextKey;\n\t}\n\n\tif ( edgeB->nextKey != B2_NULL_INDEX )\n\t{\n\t\tb2Contact* nextContact = b2ContactArray_Get( &world->contacts, edgeB->nextKey >> 1 );\n\t\tb2ContactEdge* nextEdge = nextContact->edges + ( edgeB->nextKey & 1 );\n\t\tnextEdge->prevKey = edgeB->prevKey;\n\t}\n\n\tint edgeKeyB = ( contactId << 1 ) | 1;\n\tif ( bodyB->headContactKey == edgeKeyB )\n\t{\n\t\tbodyB->headContactKey = edgeB->nextKey;\n\t}\n\n\tbodyB->contactCount -= 1;\n\n\t// Remove contact from the array that owns it\n\tif ( contact->islandId != B2_NULL_INDEX )\n\t{\n\t\tb2UnlinkContact( world, contact );\n\t}\n\n\tif ( contact->colorIndex != B2_NULL_INDEX )\n\t{\n\t\t// contact is an active constraint\n\t\tB2_ASSERT( contact->setIndex == b2_awakeSet );\n\t\tb2RemoveContactFromGraph( world, bodyIdA, bodyIdB, contact->colorIndex, contact->localIndex );\n\t}\n\telse\n\t{\n\t\t// contact is non-touching or is sleeping\n\t\tB2_ASSERT( contact->setIndex != b2_awakeSet || ( contact->flags & b2_contactTouchingFlag ) == 0 );\n\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, contact->setIndex );\n\n\t\tint movedIndex = b2ContactSimArray_RemoveSwap( &set->contactSims, contact->localIndex );\n\t\tif ( movedIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2ContactSim* movedContactSim = set->contactSims.data + contact->localIndex;\n\t\t\tb2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );\n\t\t\tmovedContact->localIndex = contact->localIndex;\n\t\t}\n\t}\n\n\t// Free contact and id (preserve generation)\n\tcontact->contactId = B2_NULL_INDEX;\n\tcontact->setIndex = B2_NULL_INDEX;\n\tcontact->colorIndex = B2_NULL_INDEX;\n\tcontact->localIndex = B2_NULL_INDEX;\n\tb2FreeId( &world->contactIdPool, contactId );\n\n\tif ( wakeBodies && touching )\n\t{\n\t\tb2WakeBody( world, bodyA );\n\t\tb2WakeBody( world, bodyB );\n\t}\n}\n\nb2ContactSim* b2GetContactSim( b2World* world, b2Contact* contact )\n{\n\tif ( contact->setIndex == b2_awakeSet && contact->colorIndex != B2_NULL_INDEX )\n\t{\n\t\t// contact lives in constraint graph\n\t\tB2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < B2_GRAPH_COLOR_COUNT );\n\t\tb2GraphColor* color = world->constraintGraph.colors + contact->colorIndex;\n\t\treturn b2ContactSimArray_Get( &color->contactSims, contact->localIndex );\n\t}\n\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, contact->setIndex );\n\treturn b2ContactSimArray_Get( &set->contactSims, contact->localIndex );\n}\n\n// Update the contact manifold and touching status.\n// Note: do not assume the shape AABBs are overlapping or are valid.\nbool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, b2Transform transformA, b2Vec2 centerOffsetA,\n\t\t\t\t\t  b2Shape* shapeB, b2Transform transformB, b2Vec2 centerOffsetB )\n{\n\t// Save old manifold\n\tb2Manifold oldManifold = contactSim->manifold;\n\n\t// Compute new manifold\n\tb2ManifoldFcn* fcn = s_registers[shapeA->type][shapeB->type].fcn;\n\tcontactSim->manifold = fcn( shapeA, transformA, shapeB, transformB, &contactSim->cache );\n\n\t// Keep these updated in case the values on the shapes are modified\n\tcontactSim->friction = world->frictionCallback( shapeA->material.friction, shapeA->material.userMaterialId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tshapeB->material.friction, shapeB->material.userMaterialId );\n\tcontactSim->restitution = world->restitutionCallback( shapeA->material.restitution, shapeA->material.userMaterialId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shapeB->material.restitution, shapeB->material.userMaterialId );\n\n\tif ( shapeA->material.rollingResistance > 0.0f || shapeB->material.rollingResistance > 0.0f )\n\t{\n\t\tfloat radiusA = b2GetShapeRadius( shapeA );\n\t\tfloat radiusB = b2GetShapeRadius( shapeB );\n\t\tfloat maxRadius = b2MaxFloat( radiusA, radiusB );\n\t\tcontactSim->rollingResistance =\n\t\t\tb2MaxFloat( shapeA->material.rollingResistance, shapeB->material.rollingResistance ) * maxRadius;\n\t}\n\telse\n\t{\n\t\tcontactSim->rollingResistance = 0.0f;\n\t}\n\n\tcontactSim->tangentSpeed = shapeA->material.tangentSpeed + shapeB->material.tangentSpeed;\n\n\tint pointCount = contactSim->manifold.pointCount;\n\tbool touching = pointCount > 0;\n\n\tif ( touching && world->preSolveFcn != NULL && ( contactSim->simFlags & b2_simEnablePreSolveEvents ) != 0 )\n\t{\n\t\tb2ShapeId shapeIdA = { shapeA->id + 1, world->worldId, shapeA->generation };\n\t\tb2ShapeId shapeIdB = { shapeB->id + 1, world->worldId, shapeB->generation };\n\n\t\tb2Manifold* manifold = &contactSim->manifold;\n\t\tfloat bestSeparation = manifold->points[0].separation;\n\t\tb2Vec2 bestPoint = manifold->points[0].point;\n\n\t\t// Get deepest point\n\t\tfor ( int i = 1; i < manifold->pointCount; ++i )\n\t\t{\n\t\t\tfloat separation = manifold->points[i].separation;\n\t\t\tif ( separation < bestSeparation )\n\t\t\t{\n\t\t\t\tbestSeparation = separation;\n\t\t\t\tbestPoint = manifold->points[i].point;\n\t\t\t}\n\t\t}\n\n\t\t// this call assumes thread safety\n\t\ttouching = world->preSolveFcn( shapeIdA, shapeIdB, bestPoint, manifold->normal, world->preSolveContext );\n\t\tif ( touching == false )\n\t\t{\n\t\t\t// disable contact\n\t\t\tpointCount = 0;\n\t\t\tmanifold->pointCount = 0;\n\t\t}\n\t}\n\n\t// This flag is for testing\n\tif ( world->enableSpeculative == false && pointCount == 2 )\n\t{\n\t\tif ( contactSim->manifold.points[0].separation > 1.5f * B2_LINEAR_SLOP )\n\t\t{\n\t\t\tcontactSim->manifold.points[0] = contactSim->manifold.points[1];\n\t\t\tcontactSim->manifold.pointCount = 1;\n\t\t}\n\t\telse if ( contactSim->manifold.points[0].separation > 1.5f * B2_LINEAR_SLOP )\n\t\t{\n\t\t\tcontactSim->manifold.pointCount = 1;\n\t\t}\n\n\t\tpointCount = contactSim->manifold.pointCount;\n\t}\n\n\tif ( touching && ( shapeA->enableHitEvents || shapeB->enableHitEvents ) )\n\t{\n\t\tcontactSim->simFlags |= b2_simEnableHitEvent;\n\t}\n\telse\n\t{\n\t\tcontactSim->simFlags &= ~b2_simEnableHitEvent;\n\t}\n\n\tif ( pointCount > 0 )\n\t{\n\t\tcontactSim->manifold.rollingImpulse = oldManifold.rollingImpulse;\n\t}\n\n\t// Match old contact ids to new contact ids and copy the\n\t// stored impulses to warm start the solver.\n\tint unmatchedCount = 0;\n\tfor ( int i = 0; i < pointCount; ++i )\n\t{\n\t\tb2ManifoldPoint* mp2 = contactSim->manifold.points + i;\n\n\t\t// shift anchors to be center of mass relative\n\t\tmp2->anchorA = b2Sub( mp2->anchorA, centerOffsetA );\n\t\tmp2->anchorB = b2Sub( mp2->anchorB, centerOffsetB );\n\n\t\tmp2->normalImpulse = 0.0f;\n\t\tmp2->tangentImpulse = 0.0f;\n\t\tmp2->totalNormalImpulse = 0.0f;\n\t\tmp2->normalVelocity = 0.0f;\n\t\tmp2->persisted = false;\n\n\t\tuint16_t id2 = mp2->id;\n\n\t\tfor ( int j = 0; j < oldManifold.pointCount; ++j )\n\t\t{\n\t\t\tb2ManifoldPoint* mp1 = oldManifold.points + j;\n\n\t\t\tif ( mp1->id == id2 )\n\t\t\t{\n\t\t\t\tmp2->normalImpulse = mp1->normalImpulse;\n\t\t\t\tmp2->tangentImpulse = mp1->tangentImpulse;\n\t\t\t\tmp2->persisted = true;\n\n\t\t\t\t// clear old impulse\n\t\t\t\tmp1->normalImpulse = 0.0f;\n\t\t\t\tmp1->tangentImpulse = 0.0f;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tunmatchedCount += mp2->persisted ? 0 : 1;\n\t}\n\n\tB2_UNUSED( unmatchedCount );\n\n#if 0\n\t\t// todo I haven't found an improvement from this yet\n\t\t// If there are unmatched new contact points, apply any left over old impulse.\n\t\tif (unmatchedCount > 0)\n\t\t{\n\t\t\tfloat unmatchedNormalImpulse = 0.0f;\n\t\t\tfloat unmatchedTangentImpulse = 0.0f;\n\t\t\tfor (int i = 0; i < oldManifold.pointCount; ++i)\n\t\t\t{\n\t\t\t\tb2ManifoldPoint* mp = oldManifold.points + i;\n\t\t\t\tunmatchedNormalImpulse += mp->normalImpulse;\n\t\t\t\tunmatchedTangentImpulse += mp->tangentImpulse;\n\t\t\t}\n\n\t\t\tfloat inverse = 1.0f / unmatchedCount;\n\t\t\tunmatchedNormalImpulse *= inverse;\n\t\t\tunmatchedTangentImpulse *= inverse;\n\n\t\t\tfor ( int i = 0; i < pointCount; ++i )\n\t\t\t{\n\t\t\t\tb2ManifoldPoint* mp2 = contactSim->manifold.points + i;\n\n\t\t\t\tif (mp2->persisted)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tmp2->normalImpulse = unmatchedNormalImpulse;\n\t\t\t\tmp2->tangentImpulse = unmatchedTangentImpulse;\n\t\t\t}\n\t\t}\n#endif\n\n\tif ( touching )\n\t{\n\t\tcontactSim->simFlags |= b2_simTouchingFlag;\n\t}\n\telse\n\t{\n\t\tcontactSim->simFlags &= ~b2_simTouchingFlag;\n\t}\n\n\treturn touching;\n}\n"
  },
  {
    "path": "src/contact.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n#include \"core.h\"\n\n#include \"box2d/collision.h\"\n#include \"box2d/types.h\"\n\ntypedef struct b2Shape b2Shape;\ntypedef struct b2World b2World;\n\nenum b2ContactFlags\n{\n\t// Set when the solid shapes are touching.\n\tb2_contactTouchingFlag = 0x00000001,\n\n\t// Contact has a hit event\n\tb2_contactHitEventFlag = 0x00000002,\n\n\t// This contact wants contact events\n\tb2_contactEnableContactEvents = 0x00000004,\n};\n\n// A contact edge is used to connect bodies and contacts together\n// in a contact graph where each body is a node and each contact\n// is an edge. A contact edge belongs to a doubly linked list\n// maintained in each attached body. Each contact has two contact\n// edges, one for each attached body.\ntypedef struct b2ContactEdge\n{\n\tint bodyId;\n\tint prevKey;\n\tint nextKey;\n} b2ContactEdge;\n\n// Cold contact data. Used as a persistent handle and for persistent island\n// connectivity.\ntypedef struct b2Contact\n{\n\t// index of simulation set stored in b2World\n\t// B2_NULL_INDEX when slot is free\n\tint setIndex;\n\n\t// index into the constraint graph color array\n\t// B2_NULL_INDEX for non-touching or sleeping contacts\n\t// B2_NULL_INDEX when slot is free\n\tint colorIndex;\n\n\t// contact index within set or graph color\n\t// B2_NULL_INDEX when slot is free\n\tint localIndex;\n\n\tint shapeIdA;\n\tint shapeIdB;\n\tint contactId;\n\n\t// A contact only belongs to an island if touching, otherwise B2_NULL_INDEX.\n\tb2ContactEdge edges[2];\n\tint islandPrev;\n\tint islandNext;\n\tint islandId;\n\n\t// b2ContactFlags\n\tuint32_t flags;\n\n\t// This is monotonically advanced when a contact is allocated in this slot\n\t// Used to check for invalid b2ContactId\n\tuint32_t generation;\n} b2Contact;\n\n// Shifted to be distinct from b2ContactFlags\nenum b2ContactSimFlags\n{\n\t// Set when the shapes are touching\n\tb2_simTouchingFlag = 0x00010000,\n\n\t// This contact no longer has overlapping AABBs\n\tb2_simDisjoint = 0x00020000,\n\n\t// This contact started touching\n\tb2_simStartedTouching = 0x00040000,\n\n\t// This contact stopped touching\n\tb2_simStoppedTouching = 0x00080000,\n\n\t// This contact has a hit event\n\tb2_simEnableHitEvent = 0x00100000,\n\n\t// This contact wants pre-solve events\n\tb2_simEnablePreSolveEvents = 0x00200000,\n};\n\n/// The class manages contact between two shapes. A contact exists for each overlapping\n/// AABB in the broad-phase (except if filtered). Therefore a contact object may exist\n/// that has no contact points.\ntypedef struct b2ContactSim\n{\n\tint contactId;\n\n#if B2_VALIDATE\n\tint bodyIdA;\n\tint bodyIdB;\n#endif\n\n\t// Transient body indices\n\tint bodySimIndexA;\n\tint bodySimIndexB;\n\n\tint shapeIdA;\n\tint shapeIdB;\n\n\tfloat invMassA;\n\tfloat invIA;\n\n\tfloat invMassB;\n\tfloat invIB;\n\n\tb2Manifold manifold;\n\n\t// Mixed friction and restitution\n\tfloat friction;\n\tfloat restitution;\n\tfloat rollingResistance;\n\tfloat tangentSpeed;\n\n\t// b2ContactSimFlags\n\tuint32_t simFlags;\n\n\tb2SimplexCache cache;\n} b2ContactSim;\n\nvoid b2InitializeContactRegisters( void );\n\nvoid b2CreateContact( b2World* world, b2Shape* shapeA, b2Shape* shapeB );\nvoid b2DestroyContact( b2World* world, b2Contact* contact, bool wakeBodies );\n\nb2ContactSim* b2GetContactSim( b2World* world, b2Contact* contact );\n\nbool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, b2Transform transformA, b2Vec2 centerOffsetA,\n\t\t\t\t\t  b2Shape* shapeB, b2Transform transformB, b2Vec2 centerOffsetB );\n\nB2_ARRAY_INLINE( b2Contact, b2Contact )\nB2_ARRAY_INLINE( b2ContactSim, b2ContactSim )\n"
  },
  {
    "path": "src/contact_solver.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"contact_solver.h\"\n\n#include \"body.h\"\n#include \"constraint_graph.h\"\n#include \"contact.h\"\n#include \"core.h\"\n#include \"physics_world.h\"\n#include \"solver_set.h\"\n\n#include <stddef.h>\n\n// contact separation for sub-stepping\n// s = s0 + dot(cB + rB - cA - rA, normal)\n// normal is held constant\n// body positions c can translation and anchors r can rotate\n// s(t) = s0 + dot(cB(t) + rB(t) - cA(t) - rA(t), normal)\n// s(t) = s0 + dot(cB0 + dpB + rot(dqB, rB0) - cA0 - dpA - rot(dqA, rA0), normal)\n// s(t) = s0 + dot(cB0 - cA0, normal) + dot(dpB - dpA + rot(dqB, rB0) - rot(dqA, rA0), normal)\n// s_base = s0 + dot(cB0 - cA0, normal)\n\nvoid b2PrepareOverflowContacts( b2StepContext* context )\n{\n\tb2TracyCZoneNC( prepare_overflow_contact, \"Prepare Overflow Contact\", b2_colorYellow, true );\n\n\tb2World* world = context->world;\n\tb2ConstraintGraph* graph = context->graph;\n\tb2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;\n\tb2ContactConstraint* constraints = color->overflowConstraints;\n\tint contactCount = color->contactSims.count;\n\tb2ContactSim* contacts = color->contactSims.data;\n\tb2BodyState* awakeStates = context->states;\n\n#if B2_VALIDATE\n\tb2Body* bodies = world->bodies.data;\n#endif\n\n\t// Stiffer for static contacts to avoid bodies getting pushed through the ground\n\tb2Softness contactSoftness = context->contactSoftness;\n\tb2Softness staticSoftness = context->staticSoftness;\n\n\tfloat warmStartScale = world->enableWarmStarting ? 1.0f : 0.0f;\n\n\tfor ( int i = 0; i < contactCount; ++i )\n\t{\n\t\tb2ContactSim* contactSim = contacts + i;\n\n\t\tconst b2Manifold* manifold = &contactSim->manifold;\n\t\tint pointCount = manifold->pointCount;\n\n\t\tB2_ASSERT( 0 < pointCount && pointCount <= 2 );\n\n\t\tint indexA = contactSim->bodySimIndexA;\n\t\tint indexB = contactSim->bodySimIndexB;\n\n#if B2_VALIDATE\n\t\tb2Body* bodyA = bodies + contactSim->bodyIdA;\n\t\tint validIndexA = bodyA->setIndex == b2_awakeSet ? bodyA->localIndex : B2_NULL_INDEX;\n\t\tB2_ASSERT( indexA == validIndexA );\n\n\t\tb2Body* bodyB = bodies + contactSim->bodyIdB;\n\t\tint validIndexB = bodyB->setIndex == b2_awakeSet ? bodyB->localIndex : B2_NULL_INDEX;\n\t\tB2_ASSERT( indexB == validIndexB );\n#endif\n\n\t\tb2ContactConstraint* constraint = constraints + i;\n\t\tconstraint->indexA = indexA;\n\t\tconstraint->indexB = indexB;\n\t\tconstraint->normal = manifold->normal;\n\t\tconstraint->friction = contactSim->friction;\n\t\tconstraint->restitution = contactSim->restitution;\n\t\tconstraint->rollingResistance = contactSim->rollingResistance;\n\t\tconstraint->rollingImpulse = warmStartScale * manifold->rollingImpulse;\n\t\tconstraint->tangentSpeed = contactSim->tangentSpeed;\n\t\tconstraint->pointCount = pointCount;\n\n\t\tb2Vec2 vA = b2Vec2_zero;\n\t\tfloat wA = 0.0f;\n\t\tfloat mA = contactSim->invMassA;\n\t\tfloat iA = contactSim->invIA;\n\t\tif ( indexA != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2BodyState* stateA = awakeStates + indexA;\n\t\t\tvA = stateA->linearVelocity;\n\t\t\twA = stateA->angularVelocity;\n\t\t}\n\n\t\tb2Vec2 vB = b2Vec2_zero;\n\t\tfloat wB = 0.0f;\n\t\tfloat mB = contactSim->invMassB;\n\t\tfloat iB = contactSim->invIB;\n\t\tif ( indexB != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2BodyState* stateB = awakeStates + indexB;\n\t\t\tvB = stateB->linearVelocity;\n\t\t\twB = stateB->angularVelocity;\n\t\t}\n\n\t\tif ( indexA == B2_NULL_INDEX || indexB == B2_NULL_INDEX )\n\t\t{\n\t\t\tconstraint->softness = staticSoftness;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconstraint->softness = contactSoftness;\n\t\t}\n\n\t\t// copy mass into constraint to avoid cache misses during sub-stepping\n\t\tconstraint->invMassA = mA;\n\t\tconstraint->invIA = iA;\n\t\tconstraint->invMassB = mB;\n\t\tconstraint->invIB = iB;\n\n\t\t{\n\t\t\tfloat k = iA + iB;\n\t\t\tconstraint->rollingMass = k > 0.0f ? 1.0f / k : 0.0f;\n\t\t}\n\n\t\tb2Vec2 normal = constraint->normal;\n\t\tb2Vec2 tangent = b2RightPerp( constraint->normal );\n\n\t\tfor ( int j = 0; j < pointCount; ++j )\n\t\t{\n\t\t\tconst b2ManifoldPoint* mp = manifold->points + j;\n\t\t\tb2ContactConstraintPoint* cp = constraint->points + j;\n\n\t\t\tcp->normalImpulse = warmStartScale * mp->normalImpulse;\n\t\t\tcp->tangentImpulse = warmStartScale * mp->tangentImpulse;\n\t\t\tcp->totalNormalImpulse = 0.0f;\n\n\t\t\tb2Vec2 rA = mp->anchorA;\n\t\t\tb2Vec2 rB = mp->anchorB;\n\n\t\t\tcp->anchorA = rA;\n\t\t\tcp->anchorB = rB;\n\t\t\tcp->baseSeparation = mp->separation - b2Dot( b2Sub( rB, rA ), normal );\n\n\t\t\tfloat rnA = b2Cross( rA, normal );\n\t\t\tfloat rnB = b2Cross( rB, normal );\n\t\t\tfloat kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB;\n\t\t\tcp->normalMass = kNormal > 0.0f ? 1.0f / kNormal : 0.0f;\n\n\t\t\tfloat rtA = b2Cross( rA, tangent );\n\t\t\tfloat rtB = b2Cross( rB, tangent );\n\t\t\tfloat kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB;\n\t\t\tcp->tangentMass = kTangent > 0.0f ? 1.0f / kTangent : 0.0f;\n\n\t\t\t// Save relative velocity for restitution\n\t\t\tb2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );\n\t\t\tb2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );\n\t\t\tcp->relativeVelocity = b2Dot( normal, b2Sub( vrB, vrA ) );\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( prepare_overflow_contact );\n}\n\nvoid b2WarmStartOverflowContacts( b2StepContext* context )\n{\n\tb2TracyCZoneNC( warmstart_overflow_contact, \"WarmStart Overflow Contact\", b2_colorDarkOrange, true );\n\n\tb2ConstraintGraph* graph = context->graph;\n\tb2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;\n\tb2ContactConstraint* constraints = color->overflowConstraints;\n\tint contactCount = color->contactSims.count;\n\tb2World* world = context->world;\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tb2BodyState* states = awakeSet->bodyStates.data;\n\n\t// This is a dummy state to represent a static body because static bodies don't have a solver body.\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tfor ( int i = 0; i < contactCount; ++i )\n\t{\n\t\tb2ContactConstraint* constraint = constraints + i;\n\n\t\tint indexA = constraint->indexA;\n\t\tint indexB = constraint->indexB;\n\n\t\tb2BodyState* stateA = indexA == B2_NULL_INDEX ? &dummyState : states + indexA;\n\t\tb2BodyState* stateB = indexB == B2_NULL_INDEX ? &dummyState : states + indexB;\n\n\t\tb2Vec2 vA = stateA->linearVelocity;\n\t\tfloat wA = stateA->angularVelocity;\n\t\tb2Vec2 vB = stateB->linearVelocity;\n\t\tfloat wB = stateB->angularVelocity;\n\n\t\tfloat mA = constraint->invMassA;\n\t\tfloat iA = constraint->invIA;\n\t\tfloat mB = constraint->invMassB;\n\t\tfloat iB = constraint->invIB;\n\n\t\t// Stiffer for static contacts to avoid bodies getting pushed through the ground\n\t\tb2Vec2 normal = constraint->normal;\n\t\tb2Vec2 tangent = b2RightPerp( constraint->normal );\n\t\tint pointCount = constraint->pointCount;\n\n\t\tfor ( int j = 0; j < pointCount; ++j )\n\t\t{\n\t\t\tb2ContactConstraintPoint* cp = constraint->points + j;\n\n\t\t\t// fixed anchors\n\t\t\tb2Vec2 rA = cp->anchorA;\n\t\t\tb2Vec2 rB = cp->anchorB;\n\n\t\t\tb2Vec2 P = b2Add( b2MulSV( cp->normalImpulse, normal ), b2MulSV( cp->tangentImpulse, tangent ) );\n\n\t\t\tcp->totalNormalImpulse += cp->normalImpulse;\n\n\t\t\twA -= iA * b2Cross( rA, P );\n\t\t\tvA = b2MulAdd( vA, -mA, P );\n\t\t\twB += iB * b2Cross( rB, P );\n\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t}\n\n\t\twA -= iA * constraint->rollingImpulse;\n\t\twB += iB * constraint->rollingImpulse;\n\n\t\tif ( stateA->flags & b2_dynamicFlag )\n\t\t{\n\t\t\tstateA->linearVelocity = vA;\n\t\t\tstateA->angularVelocity = wA;\n\t\t}\n\n\t\tif ( stateB->flags & b2_dynamicFlag )\n\t\t{\n\t\t\tstateB->linearVelocity = vB;\n\t\t\tstateB->angularVelocity = wB;\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( warmstart_overflow_contact );\n}\n\nvoid b2SolveOverflowContacts( b2StepContext* context, bool useBias )\n{\n\tb2TracyCZoneNC( solve_contact, \"Solve Contact\", b2_colorAliceBlue, true );\n\n\tb2ConstraintGraph* graph = context->graph;\n\tb2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;\n\tb2ContactConstraint* constraints = color->overflowConstraints;\n\tint contactCount = color->contactSims.count;\n\tb2World* world = context->world;\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tb2BodyState* states = awakeSet->bodyStates.data;\n\n\tfloat inv_h = context->inv_h;\n\tconst float contactSpeed = context->world->contactSpeed;\n\n\t// This is a dummy body to represent a static body since static bodies don't have a solver body.\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tfor ( int i = 0; i < contactCount; ++i )\n\t{\n\t\tb2ContactConstraint* constraint = constraints + i;\n\t\tfloat mA = constraint->invMassA;\n\t\tfloat iA = constraint->invIA;\n\t\tfloat mB = constraint->invMassB;\n\t\tfloat iB = constraint->invIB;\n\n\t\tb2BodyState* stateA = constraint->indexA == B2_NULL_INDEX ? &dummyState : states + constraint->indexA;\n\t\tb2Vec2 vA = stateA->linearVelocity;\n\t\tfloat wA = stateA->angularVelocity;\n\t\tb2Rot dqA = stateA->deltaRotation;\n\n\t\tb2BodyState* stateB = constraint->indexB == B2_NULL_INDEX ? &dummyState : states + constraint->indexB;\n\t\tb2Vec2 vB = stateB->linearVelocity;\n\t\tfloat wB = stateB->angularVelocity;\n\t\tb2Rot dqB = stateB->deltaRotation;\n\n\t\tb2Vec2 dp = b2Sub( stateB->deltaPosition, stateA->deltaPosition );\n\n\t\tb2Vec2 normal = constraint->normal;\n\t\tb2Vec2 tangent = b2RightPerp( normal );\n\t\tfloat friction = constraint->friction;\n\t\tb2Softness softness = constraint->softness;\n\n\t\tint pointCount = constraint->pointCount;\n\t\tfloat totalNormalImpulse = 0.0f;\n\n\t\t// Non-penetration\n\t\tfor ( int j = 0; j < pointCount; ++j )\n\t\t{\n\t\t\tb2ContactConstraintPoint* cp = constraint->points + j;\n\n\t\t\t// fixed anchor points\n\t\t\tb2Vec2 rA = cp->anchorA;\n\t\t\tb2Vec2 rB = cp->anchorB;\n\n\t\t\t// compute current separation\n\t\t\t// this is subject to round-off error if the anchor is far from the body center of mass\n\t\t\tb2Vec2 ds = b2Add( dp, b2Sub( b2RotateVector( dqB, rB ), b2RotateVector( dqA, rA ) ) );\n\t\t\tfloat s = cp->baseSeparation + b2Dot( ds, normal );\n\n\t\t\tfloat velocityBias = 0.0f;\n\t\t\tfloat massScale = 1.0f;\n\t\t\tfloat impulseScale = 0.0f;\n\t\t\tif ( s > 0.0f )\n\t\t\t{\n\t\t\t\t// speculative bias\n\t\t\t\tvelocityBias = s * inv_h;\n\t\t\t}\n\t\t\telse if ( useBias )\n\t\t\t{\n\t\t\t\tvelocityBias = b2MaxFloat( softness.massScale * softness.biasRate * s, -contactSpeed );\n\t\t\t\tmassScale = softness.massScale;\n\t\t\t\timpulseScale = softness.impulseScale;\n\t\t\t}\n\n\t\t\t// relative normal velocity at contact\n\t\t\tb2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );\n\t\t\tb2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );\n\t\t\tfloat vn = b2Dot( b2Sub( vrB, vrA ), normal );\n\n\t\t\t// incremental normal impulse\n\t\t\tfloat impulse = -cp->normalMass * ( massScale * vn + velocityBias ) - impulseScale * cp->normalImpulse;\n\n\t\t\t// clamp the accumulated impulse\n\t\t\tfloat newImpulse = b2MaxFloat( cp->normalImpulse + impulse, 0.0f );\n\t\t\timpulse = newImpulse - cp->normalImpulse;\n\t\t\tcp->normalImpulse = newImpulse;\n\t\t\tcp->totalNormalImpulse += impulse;\n\n\t\t\t// b2Log( \"vn %g impulse %g bias %g\", vn, newImpulse, velocityBias );\n\n\t\t\ttotalNormalImpulse += newImpulse;\n\n\t\t\t// apply normal impulse\n\t\t\tb2Vec2 P = b2MulSV( impulse, normal );\n\t\t\tvA = b2MulSub( vA, mA, P );\n\t\t\twA -= iA * b2Cross( rA, P );\n\n\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t\twB += iB * b2Cross( rB, P );\n\t\t}\n\n\t\t// Friction\n\t\tfor ( int j = 0; j < pointCount; ++j )\n\t\t{\n\t\t\tb2ContactConstraintPoint* cp = constraint->points + j;\n\n\t\t\t// fixed anchor points\n\t\t\tb2Vec2 rA = cp->anchorA;\n\t\t\tb2Vec2 rB = cp->anchorB;\n\n\t\t\t// relative tangent velocity at contact\n\t\t\tb2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );\n\t\t\tb2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );\n\n\t\t\t// vt = dot(vrB - sB * tangent - (vrA + sA * tangent), tangent)\n\t\t\t//    = dot(vrB - vrA, tangent) - (sA + sB)\n\n\t\t\tfloat vt = b2Dot( b2Sub( vrB, vrA ), tangent ) - constraint->tangentSpeed;\n\n\t\t\t// incremental tangent impulse\n\t\t\tfloat impulse = cp->tangentMass * ( -vt );\n\n\t\t\t// clamp the accumulated force\n\t\t\tfloat maxFriction = friction * cp->normalImpulse;\n\t\t\tfloat newImpulse = b2ClampFloat( cp->tangentImpulse + impulse, -maxFriction, maxFriction );\n\t\t\timpulse = newImpulse - cp->tangentImpulse;\n\t\t\tcp->tangentImpulse = newImpulse;\n\n\t\t\t// apply tangent impulse\n\t\t\tb2Vec2 P = b2MulSV( impulse, tangent );\n\t\t\tvA = b2MulSub( vA, mA, P );\n\t\t\twA -= iA * b2Cross( rA, P );\n\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t\twB += iB * b2Cross( rB, P );\n\t\t}\n\n\t\t// Rolling resistance\n\t\t{\n\t\t\tfloat deltaLambda = -constraint->rollingMass * ( wB - wA );\n\t\t\tfloat lambda = constraint->rollingImpulse;\n\t\t\tfloat maxLambda = constraint->rollingResistance * totalNormalImpulse;\n\t\t\tconstraint->rollingImpulse = b2ClampFloat( lambda + deltaLambda, -maxLambda, maxLambda );\n\t\t\tdeltaLambda = constraint->rollingImpulse - lambda;\n\n\t\t\twA -= iA * deltaLambda;\n\t\t\twB += iB * deltaLambda;\n\t\t}\n\n\t\tif ( stateA->flags & b2_dynamicFlag )\n\t\t{\n\t\t\tstateA->linearVelocity = vA;\n\t\t\tstateA->angularVelocity = wA;\n\t\t}\n\n\t\tif ( stateB->flags & b2_dynamicFlag )\n\t\t{\n\t\t\tstateB->linearVelocity = vB;\n\t\t\tstateB->angularVelocity = wB;\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( solve_contact );\n}\n\nvoid b2ApplyOverflowRestitution( b2StepContext* context )\n{\n\tb2TracyCZoneNC( overflow_resitution, \"Overflow Restitution\", b2_colorViolet, true );\n\n\tb2ConstraintGraph* graph = context->graph;\n\tb2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;\n\tb2ContactConstraint* constraints = color->overflowConstraints;\n\tint contactCount = color->contactSims.count;\n\tb2World* world = context->world;\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tb2BodyState* states = awakeSet->bodyStates.data;\n\n\tfloat threshold = context->world->restitutionThreshold;\n\n\t// dummy state to represent a static body\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tfor ( int i = 0; i < contactCount; ++i )\n\t{\n\t\tb2ContactConstraint* constraint = constraints + i;\n\n\t\tfloat restitution = constraint->restitution;\n\t\tif ( restitution == 0.0f )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tfloat mA = constraint->invMassA;\n\t\tfloat iA = constraint->invIA;\n\t\tfloat mB = constraint->invMassB;\n\t\tfloat iB = constraint->invIB;\n\n\t\tb2BodyState* stateA = constraint->indexA == B2_NULL_INDEX ? &dummyState : states + constraint->indexA;\n\t\tb2Vec2 vA = stateA->linearVelocity;\n\t\tfloat wA = stateA->angularVelocity;\n\n\t\tb2BodyState* stateB = constraint->indexB == B2_NULL_INDEX ? &dummyState : states + constraint->indexB;\n\t\tb2Vec2 vB = stateB->linearVelocity;\n\t\tfloat wB = stateB->angularVelocity;\n\n\t\tb2Vec2 normal = constraint->normal;\n\t\tint pointCount = constraint->pointCount;\n\n\t\t// it is possible to get more accurate restitution by iterating\n\t\t// this only makes a difference if there are two contact points\n\t\t// for (int iter = 0; iter < 10; ++iter)\n\t\t{\n\t\t\tfor ( int j = 0; j < pointCount; ++j )\n\t\t\t{\n\t\t\t\tb2ContactConstraintPoint* cp = constraint->points + j;\n\n\t\t\t\t// if the normal impulse is zero then there was no collision\n\t\t\t\t// this skips speculative contact points that didn't generate an impulse\n\t\t\t\t// The max normal impulse is used in case there was a collision that moved away within the sub-step process\n\t\t\t\tif ( cp->relativeVelocity > -threshold || cp->totalNormalImpulse == 0.0f )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// fixed anchor points\n\t\t\t\tb2Vec2 rA = cp->anchorA;\n\t\t\t\tb2Vec2 rB = cp->anchorB;\n\n\t\t\t\t// relative normal velocity at contact\n\t\t\t\tb2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );\n\t\t\t\tb2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );\n\t\t\t\tfloat vn = b2Dot( b2Sub( vrB, vrA ), normal );\n\n\t\t\t\t// compute normal impulse\n\t\t\t\tfloat impulse = -cp->normalMass * ( vn + restitution * cp->relativeVelocity );\n\n\t\t\t\t// clamp the accumulated impulse\n\t\t\t\t// todo should this be stored?\n\t\t\t\tfloat newImpulse = b2MaxFloat( cp->normalImpulse + impulse, 0.0f );\n\t\t\t\timpulse = newImpulse - cp->normalImpulse;\n\t\t\t\tcp->normalImpulse = newImpulse;\n\n\t\t\t\t// Add the incremental impulse rather than the full impulse because this is not a sub-step\n\t\t\t\tcp->totalNormalImpulse += impulse;\n\n\t\t\t\t// apply contact impulse\n\t\t\t\tb2Vec2 P = b2MulSV( impulse, normal );\n\t\t\t\tvA = b2MulSub( vA, mA, P );\n\t\t\t\twA -= iA * b2Cross( rA, P );\n\t\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t\t\twB += iB * b2Cross( rB, P );\n\t\t\t}\n\t\t}\n\n\t\tif ( stateA->flags & b2_dynamicFlag )\n\t\t{\n\t\t\tstateA->linearVelocity = vA;\n\t\t\tstateA->angularVelocity = wA;\n\t\t}\n\n\t\tif ( stateB->flags & b2_dynamicFlag )\n\t\t{\n\t\t\tstateB->linearVelocity = vB;\n\t\t\tstateB->angularVelocity = wB;\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( overflow_resitution );\n}\n\nvoid b2StoreOverflowImpulses( b2StepContext* context )\n{\n\tb2TracyCZoneNC( store_impulses, \"Store\", b2_colorFireBrick, true );\n\n\tb2ConstraintGraph* graph = context->graph;\n\tb2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;\n\tb2ContactConstraint* constraints = color->overflowConstraints;\n\tb2ContactSim* contacts = color->contactSims.data;\n\tint contactCount = color->contactSims.count;\n\n\tfor ( int i = 0; i < contactCount; ++i )\n\t{\n\t\tconst b2ContactConstraint* constraint = constraints + i;\n\t\tb2ContactSim* contact = contacts + i;\n\t\tb2Manifold* manifold = &contact->manifold;\n\t\tint pointCount = manifold->pointCount;\n\n\t\tfor ( int j = 0; j < pointCount; ++j )\n\t\t{\n\t\t\tmanifold->points[j].normalImpulse = constraint->points[j].normalImpulse;\n\t\t\tmanifold->points[j].tangentImpulse = constraint->points[j].tangentImpulse;\n\t\t\tmanifold->points[j].totalNormalImpulse = constraint->points[j].totalNormalImpulse;\n\t\t\tmanifold->points[j].normalVelocity = constraint->points[j].relativeVelocity;\n\t\t}\n\n\t\tmanifold->rollingImpulse = constraint->rollingImpulse;\n\t}\n\n\tb2TracyCZoneEnd( store_impulses );\n}\n\n#if defined( B2_SIMD_AVX2 )\n\n#include <immintrin.h>\n\n// wide float holds 8 numbers\ntypedef __m256 b2FloatW;\n\n#elif defined( B2_SIMD_NEON )\n\n#include <arm_neon.h>\n\n// wide float holds 4 numbers\ntypedef float32x4_t b2FloatW;\n\n#elif defined( B2_SIMD_SSE2 )\n\n#include <emmintrin.h>\n\n// wide float holds 4 numbers\ntypedef __m128 b2FloatW;\n\n#else\n\n// scalar math\ntypedef struct b2FloatW\n{\n\tfloat x, y, z, w;\n} b2FloatW;\n\n#endif\n\n// Wide vec2\ntypedef struct b2Vec2W\n{\n\tb2FloatW X, Y;\n} b2Vec2W;\n\n// Wide rotation\ntypedef struct b2RotW\n{\n\tb2FloatW C, S;\n} b2RotW;\n\n#if defined( B2_SIMD_AVX2 )\n\nstatic inline b2FloatW b2ZeroW( void )\n{\n\treturn _mm256_setzero_ps();\n}\n\nstatic inline b2FloatW b2SplatW( float scalar )\n{\n\treturn _mm256_set1_ps( scalar );\n}\n\nstatic inline b2FloatW b2AddW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm256_add_ps( a, b );\n}\n\nstatic inline b2FloatW b2SubW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm256_sub_ps( a, b );\n}\n\nstatic inline b2FloatW b2MulW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm256_mul_ps( a, b );\n}\n\nstatic inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c )\n{\n\t// FMA can be emulated: https://github.com/lattera/glibc/blob/master/sysdeps/ieee754/dbl-64/s_fmaf.c#L34\n\t// return _mm256_fmadd_ps( b, c, a );\n\treturn _mm256_add_ps( _mm256_mul_ps( b, c ), a );\n}\n\nstatic inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c )\n{\n\t// return _mm256_fnmadd_ps(b, c, a);\n\treturn _mm256_sub_ps( a, _mm256_mul_ps( b, c ) );\n}\n\nstatic inline b2FloatW b2MinW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm256_min_ps( a, b );\n}\n\nstatic inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm256_max_ps( a, b );\n}\n\n// a = clamp(a, -b, b)\nstatic inline b2FloatW b2SymClampW( b2FloatW a, b2FloatW b )\n{\n\tb2FloatW nb = _mm256_sub_ps( _mm256_setzero_ps(), b );\n\treturn _mm256_max_ps( nb, _mm256_min_ps( a, b ) );\n}\n\nstatic inline b2FloatW b2OrW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm256_or_ps( a, b );\n}\n\nstatic inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm256_cmp_ps( a, b, _CMP_GT_OQ );\n}\n\nstatic inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm256_cmp_ps( a, b, _CMP_EQ_OQ );\n}\n\nstatic inline bool b2AllZeroW( b2FloatW a )\n{\n\t// Compare each element with zero\n\tb2FloatW zero = _mm256_setzero_ps();\n\tb2FloatW cmp = _mm256_cmp_ps( a, zero, _CMP_EQ_OQ );\n\n\t// Create a mask from the comparison results\n\tint mask = _mm256_movemask_ps( cmp );\n\n\t// If all elements are zero, the mask will be 0xFF (11111111 in binary)\n\treturn mask == 0xFF;\n}\n\n// component-wise returns mask ? b : a\nstatic inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask )\n{\n\treturn _mm256_blendv_ps( a, b, mask );\n}\n\n#elif defined( B2_SIMD_NEON )\n\nstatic inline b2FloatW b2ZeroW( void )\n{\n\treturn vdupq_n_f32( 0.0f );\n}\n\nstatic inline b2FloatW b2SplatW( float scalar )\n{\n\treturn vdupq_n_f32( scalar );\n}\n\nstatic inline b2FloatW b2SetW( float a, float b, float c, float d )\n{\n\tfloat32_t array[4] = { a, b, c, d };\n\treturn vld1q_f32( array );\n}\n\nstatic inline b2FloatW b2AddW( b2FloatW a, b2FloatW b )\n{\n\treturn vaddq_f32( a, b );\n}\n\nstatic inline b2FloatW b2SubW( b2FloatW a, b2FloatW b )\n{\n\treturn vsubq_f32( a, b );\n}\n\nstatic inline b2FloatW b2MulW( b2FloatW a, b2FloatW b )\n{\n\treturn vmulq_f32( a, b );\n}\n\nstatic inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c )\n{\n\treturn vmlaq_f32( a, b, c );\n}\n\nstatic inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c )\n{\n\treturn vmlsq_f32( a, b, c );\n}\n\nstatic inline b2FloatW b2MinW( b2FloatW a, b2FloatW b )\n{\n\treturn vminq_f32( a, b );\n}\n\nstatic inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b )\n{\n\treturn vmaxq_f32( a, b );\n}\n\n// a = clamp(a, -b, b)\nstatic inline b2FloatW b2SymClampW( b2FloatW a, b2FloatW b )\n{\n\tb2FloatW nb = vnegq_f32( b );\n\treturn vmaxq_f32( nb, vminq_f32( a, b ) );\n}\n\nstatic inline b2FloatW b2OrW( b2FloatW a, b2FloatW b )\n{\n\treturn vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32( a ), vreinterpretq_u32_f32( b ) ) );\n}\n\nstatic inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b )\n{\n\treturn vreinterpretq_f32_u32( vcgtq_f32( a, b ) );\n}\n\nstatic inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b )\n{\n\treturn vreinterpretq_f32_u32( vceqq_f32( a, b ) );\n}\n\nstatic inline bool b2AllZeroW( b2FloatW a )\n{\n\t// Create a zero vector for comparison\n\tb2FloatW zero = vdupq_n_f32( 0.0f );\n\n\t// Compare the input vector with zero\n\tuint32x4_t cmp_result = vceqq_f32( a, zero );\n\n// Check if all comparison results are non-zero using vminvq\n#ifdef __ARM_FEATURE_SVE\n\t// ARM v8.2+ has horizontal minimum instruction\n\treturn vminvq_u32( cmp_result ) != 0;\n#else\n\t// For older ARM architectures, we need to manually check all lanes\n\treturn vgetq_lane_u32( cmp_result, 0 ) != 0 && vgetq_lane_u32( cmp_result, 1 ) != 0 && vgetq_lane_u32( cmp_result, 2 ) != 0 &&\n\t\t   vgetq_lane_u32( cmp_result, 3 ) != 0;\n#endif\n}\n\n// component-wise returns mask ? b : a\nstatic inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask )\n{\n\tuint32x4_t mask32 = vreinterpretq_u32_f32( mask );\n\treturn vbslq_f32( mask32, b, a );\n}\n\nstatic inline b2FloatW b2LoadW( const float32_t* data )\n{\n\treturn vld1q_f32( data );\n}\n\nstatic inline void b2StoreW( float32_t* data, b2FloatW a )\n{\n\tvst1q_f32( data, a );\n}\n\nstatic inline b2FloatW b2UnpackLoW( b2FloatW a, b2FloatW b )\n{\n#if defined( __aarch64__ )\n\treturn vzip1q_f32( a, b );\n#else\n\tfloat32x2_t a1 = vget_low_f32( a );\n\tfloat32x2_t b1 = vget_low_f32( b );\n\tfloat32x2x2_t result = vzip_f32( a1, b1 );\n\treturn vcombine_f32( result.val[0], result.val[1] );\n#endif\n}\n\nstatic inline b2FloatW b2UnpackHiW( b2FloatW a, b2FloatW b )\n{\n#if defined( __aarch64__ )\n\treturn vzip2q_f32( a, b );\n#else\n\tfloat32x2_t a1 = vget_high_f32( a );\n\tfloat32x2_t b1 = vget_high_f32( b );\n\tfloat32x2x2_t result = vzip_f32( a1, b1 );\n\treturn vcombine_f32( result.val[0], result.val[1] );\n#endif\n}\n\n#elif defined( B2_SIMD_SSE2 )\n\nstatic inline b2FloatW b2ZeroW( void )\n{\n\treturn _mm_setzero_ps();\n}\n\nstatic inline b2FloatW b2SplatW( float scalar )\n{\n\treturn _mm_set1_ps( scalar );\n}\n\nstatic inline b2FloatW b2SetW( float a, float b, float c, float d )\n{\n\treturn _mm_setr_ps( a, b, c, d );\n}\n\nstatic inline b2FloatW b2AddW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_add_ps( a, b );\n}\n\nstatic inline b2FloatW b2SubW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_sub_ps( a, b );\n}\n\nstatic inline b2FloatW b2MulW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_mul_ps( a, b );\n}\n\nstatic inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c )\n{\n\treturn _mm_add_ps( a, _mm_mul_ps( b, c ) );\n}\n\nstatic inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c )\n{\n\treturn _mm_sub_ps( a, _mm_mul_ps( b, c ) );\n}\n\nstatic inline b2FloatW b2MinW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_min_ps( a, b );\n}\n\nstatic inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_max_ps( a, b );\n}\n\n// a = clamp(a, -b, b)\nstatic inline b2FloatW b2SymClampW( b2FloatW a, b2FloatW b )\n{\n\t// Create a mask with the sign bit set for each element\n\t__m128 mask = _mm_set1_ps( -0.0f );\n\n\t// XOR the input with the mask to negate each element\n\t__m128 nb = _mm_xor_ps( b, mask );\n\n\treturn _mm_max_ps( nb, _mm_min_ps( a, b ) );\n}\n\nstatic inline b2FloatW b2OrW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_or_ps( a, b );\n}\n\nstatic inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_cmpgt_ps( a, b );\n}\n\nstatic inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_cmpeq_ps( a, b );\n}\n\nstatic inline bool b2AllZeroW( b2FloatW a )\n{\n\t// Compare each element with zero\n\tb2FloatW zero = _mm_setzero_ps();\n\tb2FloatW cmp = _mm_cmpeq_ps( a, zero );\n\n\t// Create a mask from the comparison results\n\tint mask = _mm_movemask_ps( cmp );\n\n\t// If all elements are zero, the mask will be 0xF (1111 in binary)\n\treturn mask == 0xF;\n}\n\n// component-wise returns mask ? b : a\nstatic inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask )\n{\n\treturn _mm_or_ps( _mm_and_ps( mask, b ), _mm_andnot_ps( mask, a ) );\n}\n\nstatic inline b2FloatW b2LoadW( const float* data )\n{\n\treturn _mm_load_ps( data );\n}\n\nstatic inline void b2StoreW( float* data, b2FloatW a )\n{\n\t_mm_store_ps( data, a );\n}\n\nstatic inline b2FloatW b2UnpackLoW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_unpacklo_ps( a, b );\n}\n\nstatic inline b2FloatW b2UnpackHiW( b2FloatW a, b2FloatW b )\n{\n\treturn _mm_unpackhi_ps( a, b );\n}\n\n#else\n\nstatic inline b2FloatW b2ZeroW( void )\n{\n\treturn (b2FloatW){ 0.0f, 0.0f, 0.0f, 0.0f };\n}\n\nstatic inline b2FloatW b2SplatW( float scalar )\n{\n\treturn (b2FloatW){ scalar, scalar, scalar, scalar };\n}\n\nstatic inline b2FloatW b2AddW( b2FloatW a, b2FloatW b )\n{\n\treturn (b2FloatW){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w };\n}\n\nstatic inline b2FloatW b2SubW( b2FloatW a, b2FloatW b )\n{\n\treturn (b2FloatW){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w };\n}\n\nstatic inline b2FloatW b2MulW( b2FloatW a, b2FloatW b )\n{\n\treturn (b2FloatW){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w };\n}\n\nstatic inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c )\n{\n\treturn (b2FloatW){ a.x + b.x * c.x, a.y + b.y * c.y, a.z + b.z * c.z, a.w + b.w * c.w };\n}\n\nstatic inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c )\n{\n\treturn (b2FloatW){ a.x - b.x * c.x, a.y - b.y * c.y, a.z - b.z * c.z, a.w - b.w * c.w };\n}\n\nstatic inline b2FloatW b2MinW( b2FloatW a, b2FloatW b )\n{\n\tb2FloatW r;\n\tr.x = a.x <= b.x ? a.x : b.x;\n\tr.y = a.y <= b.y ? a.y : b.y;\n\tr.z = a.z <= b.z ? a.z : b.z;\n\tr.w = a.w <= b.w ? a.w : b.w;\n\treturn r;\n}\n\nstatic inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b )\n{\n\tb2FloatW r;\n\tr.x = a.x >= b.x ? a.x : b.x;\n\tr.y = a.y >= b.y ? a.y : b.y;\n\tr.z = a.z >= b.z ? a.z : b.z;\n\tr.w = a.w >= b.w ? a.w : b.w;\n\treturn r;\n}\n\n// a = clamp(a, -b, b)\nstatic inline b2FloatW b2SymClampW( b2FloatW a, b2FloatW b )\n{\n\tb2FloatW r;\n\tr.x = b2ClampFloat( a.x, -b.x, b.x );\n\tr.y = b2ClampFloat( a.y, -b.y, b.y );\n\tr.z = b2ClampFloat( a.z, -b.z, b.z );\n\tr.w = b2ClampFloat( a.w, -b.w, b.w );\n\treturn r;\n}\n\nstatic inline b2FloatW b2OrW( b2FloatW a, b2FloatW b )\n{\n\tb2FloatW r;\n\tr.x = a.x != 0.0f || b.x != 0.0f ? 1.0f : 0.0f;\n\tr.y = a.y != 0.0f || b.y != 0.0f ? 1.0f : 0.0f;\n\tr.z = a.z != 0.0f || b.z != 0.0f ? 1.0f : 0.0f;\n\tr.w = a.w != 0.0f || b.w != 0.0f ? 1.0f : 0.0f;\n\treturn r;\n}\n\nstatic inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b )\n{\n\tb2FloatW r;\n\tr.x = a.x > b.x ? 1.0f : 0.0f;\n\tr.y = a.y > b.y ? 1.0f : 0.0f;\n\tr.z = a.z > b.z ? 1.0f : 0.0f;\n\tr.w = a.w > b.w ? 1.0f : 0.0f;\n\treturn r;\n}\n\nstatic inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b )\n{\n\tb2FloatW r;\n\tr.x = a.x == b.x ? 1.0f : 0.0f;\n\tr.y = a.y == b.y ? 1.0f : 0.0f;\n\tr.z = a.z == b.z ? 1.0f : 0.0f;\n\tr.w = a.w == b.w ? 1.0f : 0.0f;\n\treturn r;\n}\n\nstatic inline bool b2AllZeroW( b2FloatW a )\n{\n\treturn a.x == 0.0f && a.y == 0.0f && a.z == 0.0f && a.w == 0.0f;\n}\n\n// component-wise returns mask ? b : a\nstatic inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask )\n{\n\tb2FloatW r;\n\tr.x = mask.x != 0.0f ? b.x : a.x;\n\tr.y = mask.y != 0.0f ? b.y : a.y;\n\tr.z = mask.z != 0.0f ? b.z : a.z;\n\tr.w = mask.w != 0.0f ? b.w : a.w;\n\treturn r;\n}\n\n#endif\n\nstatic inline b2FloatW b2DotW( b2Vec2W a, b2Vec2W b )\n{\n\treturn b2AddW( b2MulW( a.X, b.X ), b2MulW( a.Y, b.Y ) );\n}\n\nstatic inline b2FloatW b2CrossW( b2Vec2W a, b2Vec2W b )\n{\n\treturn b2SubW( b2MulW( a.X, b.Y ), b2MulW( a.Y, b.X ) );\n}\n\nstatic inline b2Vec2W b2RotateVectorW( b2RotW q, b2Vec2W v )\n{\n\treturn (b2Vec2W){ b2SubW( b2MulW( q.C, v.X ), b2MulW( q.S, v.Y ) ), b2AddW( b2MulW( q.S, v.X ), b2MulW( q.C, v.Y ) ) };\n}\n\n// Soft contact constraints with sub-stepping support\n// Uses fixed anchors for Jacobians for better behavior on rolling shapes (circles & capsules)\n// http://mmacklin.com/smallsteps.pdf\n// https://box2d.org/files/ErinCatto_SoftConstraints_GDC2011.pdf\n\ntypedef struct b2ContactConstraintSIMD\n{\n\tint indexA[B2_SIMD_WIDTH];\n\tint indexB[B2_SIMD_WIDTH];\n\n\tb2FloatW invMassA, invMassB;\n\tb2FloatW invIA, invIB;\n\tb2Vec2W normal;\n\tb2FloatW friction;\n\tb2FloatW tangentSpeed;\n\tb2FloatW rollingResistance;\n\tb2FloatW rollingMass;\n\tb2FloatW rollingImpulse;\n\tb2FloatW biasRate;\n\tb2FloatW massScale;\n\tb2FloatW impulseScale;\n\tb2Vec2W anchorA1, anchorB1;\n\tb2FloatW normalMass1, tangentMass1;\n\tb2FloatW baseSeparation1;\n\tb2FloatW normalImpulse1;\n\tb2FloatW totalNormalImpulse1;\n\tb2FloatW tangentImpulse1;\n\tb2Vec2W anchorA2, anchorB2;\n\tb2FloatW baseSeparation2;\n\tb2FloatW normalImpulse2;\n\tb2FloatW totalNormalImpulse2;\n\tb2FloatW tangentImpulse2;\n\tb2FloatW normalMass2, tangentMass2;\n\tb2FloatW restitution;\n\tb2FloatW relativeVelocity1, relativeVelocity2;\n} b2ContactConstraintSIMD;\n\nint b2GetContactConstraintSIMDByteCount( void )\n{\n\treturn sizeof( b2ContactConstraintSIMD );\n}\n\n// wide version of b2BodyState\ntypedef struct b2BodyStateW\n{\n\tb2Vec2W v;\n\tb2FloatW w;\n\tb2FloatW flags;\n\tb2Vec2W dp;\n\tb2RotW dq;\n} b2BodyStateW;\n\n// Custom gather/scatter for each SIMD type\n#if defined( B2_SIMD_AVX2 )\n\n// This is a load and 8x8 transpose\nstatic b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices )\n{\n\t_Static_assert( sizeof( b2BodyState ) == 32, \"b2BodyState not 32 bytes\" );\n\tB2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );\n\t// b2BodyState b2_identityBodyState = {{0.0f, 0.0f}, 0.0f, 0, {0.0f, 0.0f}, {1.0f, 0.0f}};\n\tb2FloatW identity = _mm256_setr_ps( 0.0f, 0.0f, 0.0f, 0, 0.0f, 0.0f, 1.0f, 0.0f );\n\tb2FloatW b0 = indices[0] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[0] ) );\n\tb2FloatW b1 = indices[1] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[1] ) );\n\tb2FloatW b2 = indices[2] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[2] ) );\n\tb2FloatW b3 = indices[3] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[3] ) );\n\tb2FloatW b4 = indices[4] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[4] ) );\n\tb2FloatW b5 = indices[5] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[5] ) );\n\tb2FloatW b6 = indices[6] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[6] ) );\n\tb2FloatW b7 = indices[7] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[7] ) );\n\n\tb2FloatW t0 = _mm256_unpacklo_ps( b0, b1 );\n\tb2FloatW t1 = _mm256_unpackhi_ps( b0, b1 );\n\tb2FloatW t2 = _mm256_unpacklo_ps( b2, b3 );\n\tb2FloatW t3 = _mm256_unpackhi_ps( b2, b3 );\n\tb2FloatW t4 = _mm256_unpacklo_ps( b4, b5 );\n\tb2FloatW t5 = _mm256_unpackhi_ps( b4, b5 );\n\tb2FloatW t6 = _mm256_unpacklo_ps( b6, b7 );\n\tb2FloatW t7 = _mm256_unpackhi_ps( b6, b7 );\n\tb2FloatW tt0 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) );\n\tb2FloatW tt1 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) );\n\tb2FloatW tt2 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) );\n\tb2FloatW tt3 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) );\n\tb2FloatW tt4 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 1, 0, 1, 0 ) );\n\tb2FloatW tt5 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 3, 2, 3, 2 ) );\n\tb2FloatW tt6 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 1, 0, 1, 0 ) );\n\tb2FloatW tt7 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 3, 2, 3, 2 ) );\n\n\tb2BodyStateW simdBody;\n\tsimdBody.v.X = _mm256_permute2f128_ps( tt0, tt4, 0x20 );\n\tsimdBody.v.Y = _mm256_permute2f128_ps( tt1, tt5, 0x20 );\n\tsimdBody.w = _mm256_permute2f128_ps( tt2, tt6, 0x20 );\n\tsimdBody.flags = _mm256_permute2f128_ps( tt3, tt7, 0x20 );\n\tsimdBody.dp.X = _mm256_permute2f128_ps( tt0, tt4, 0x31 );\n\tsimdBody.dp.Y = _mm256_permute2f128_ps( tt1, tt5, 0x31 );\n\tsimdBody.dq.C = _mm256_permute2f128_ps( tt2, tt6, 0x31 );\n\tsimdBody.dq.S = _mm256_permute2f128_ps( tt3, tt7, 0x31 );\n\treturn simdBody;\n}\n\n// This writes everything back to the solver bodies but only the velocities change\nstatic void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody )\n{\n\t_Static_assert( sizeof( b2BodyState ) == 32, \"b2BodyState not 32 bytes\" );\n\tB2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );\n\tb2FloatW t0 = _mm256_unpacklo_ps( simdBody->v.X, simdBody->v.Y );\n\tb2FloatW t1 = _mm256_unpackhi_ps( simdBody->v.X, simdBody->v.Y );\n\tb2FloatW t2 = _mm256_unpacklo_ps( simdBody->w, simdBody->flags );\n\tb2FloatW t3 = _mm256_unpackhi_ps( simdBody->w, simdBody->flags );\n\tb2FloatW t4 = _mm256_unpacklo_ps( simdBody->dp.X, simdBody->dp.Y );\n\tb2FloatW t5 = _mm256_unpackhi_ps( simdBody->dp.X, simdBody->dp.Y );\n\tb2FloatW t6 = _mm256_unpacklo_ps( simdBody->dq.C, simdBody->dq.S );\n\tb2FloatW t7 = _mm256_unpackhi_ps( simdBody->dq.C, simdBody->dq.S );\n\tb2FloatW tt0 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) );\n\tb2FloatW tt1 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) );\n\tb2FloatW tt2 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) );\n\tb2FloatW tt3 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) );\n\tb2FloatW tt4 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 1, 0, 1, 0 ) );\n\tb2FloatW tt5 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 3, 2, 3, 2 ) );\n\tb2FloatW tt6 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 1, 0, 1, 0 ) );\n\tb2FloatW tt7 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 3, 2, 3, 2 ) );\n\n\t// I don't use any dummy body in the body array because this will lead to multithreaded sharing and the\n\t// associated cache flushing.\n\t// todo could add a check for kinematic bodies here\n\n\tif ( indices[0] != B2_NULL_INDEX && ( states[indices[0]].flags & b2_dynamicFlag ) != 0 )\n\t\t_mm256_store_ps( (float*)( states + indices[0] ), _mm256_permute2f128_ps( tt0, tt4, 0x20 ) );\n\tif ( indices[1] != B2_NULL_INDEX && ( states[indices[1]].flags & b2_dynamicFlag ) != 0 )\n\t\t_mm256_store_ps( (float*)( states + indices[1] ), _mm256_permute2f128_ps( tt1, tt5, 0x20 ) );\n\tif ( indices[2] != B2_NULL_INDEX && ( states[indices[2]].flags & b2_dynamicFlag ) != 0 )\n\t\t_mm256_store_ps( (float*)( states + indices[2] ), _mm256_permute2f128_ps( tt2, tt6, 0x20 ) );\n\tif ( indices[3] != B2_NULL_INDEX && ( states[indices[3]].flags & b2_dynamicFlag ) != 0 )\n\t\t_mm256_store_ps( (float*)( states + indices[3] ), _mm256_permute2f128_ps( tt3, tt7, 0x20 ) );\n\tif ( indices[4] != B2_NULL_INDEX && ( states[indices[4]].flags & b2_dynamicFlag ) != 0 )\n\t\t_mm256_store_ps( (float*)( states + indices[4] ), _mm256_permute2f128_ps( tt0, tt4, 0x31 ) );\n\tif ( indices[5] != B2_NULL_INDEX && ( states[indices[5]].flags & b2_dynamicFlag ) != 0 )\n\t\t_mm256_store_ps( (float*)( states + indices[5] ), _mm256_permute2f128_ps( tt1, tt5, 0x31 ) );\n\tif ( indices[6] != B2_NULL_INDEX && ( states[indices[6]].flags & b2_dynamicFlag ) != 0 )\n\t\t_mm256_store_ps( (float*)( states + indices[6] ), _mm256_permute2f128_ps( tt2, tt6, 0x31 ) );\n\tif ( indices[7] != B2_NULL_INDEX && ( states[indices[7]].flags & b2_dynamicFlag ) != 0 )\n\t\t_mm256_store_ps( (float*)( states + indices[7] ), _mm256_permute2f128_ps( tt3, tt7, 0x31 ) );\n}\n\n#elif defined( B2_SIMD_NEON )\n\n// This is a load and transpose\nstatic b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices )\n{\n\t_Static_assert( sizeof( b2BodyState ) == 32, \"b2BodyState not 32 bytes\" );\n\tB2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );\n\n\t// [vx vy w flags]\n\tb2FloatW identityA = b2ZeroW();\n\n\t// [dpx dpy dqc dqs]\n\n\tb2FloatW identityB = b2SetW( 0.0f, 0.0f, 1.0f, 0.0f );\n\n\tb2FloatW b1a = indices[0] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[0] ) + 0 );\n\tb2FloatW b1b = indices[0] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[0] ) + 4 );\n\tb2FloatW b2a = indices[1] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[1] ) + 0 );\n\tb2FloatW b2b = indices[1] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[1] ) + 4 );\n\tb2FloatW b3a = indices[2] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[2] ) + 0 );\n\tb2FloatW b3b = indices[2] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[2] ) + 4 );\n\tb2FloatW b4a = indices[3] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[3] ) + 0 );\n\tb2FloatW b4b = indices[3] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[3] ) + 4 );\n\n\t// [vx1 vx3 vy1 vy3]\n\tb2FloatW t1a = b2UnpackLoW( b1a, b3a );\n\n\t// [vx2 vx4 vy2 vy4]\n\tb2FloatW t2a = b2UnpackLoW( b2a, b4a );\n\n\t// [w1 w3 f1 f3]\n\tb2FloatW t3a = b2UnpackHiW( b1a, b3a );\n\n\t// [w2 w4 f2 f4]\n\tb2FloatW t4a = b2UnpackHiW( b2a, b4a );\n\n\tb2BodyStateW simdBody;\n\tsimdBody.v.X = b2UnpackLoW( t1a, t2a );\n\tsimdBody.v.Y = b2UnpackHiW( t1a, t2a );\n\tsimdBody.w = b2UnpackLoW( t3a, t4a );\n\tsimdBody.flags = b2UnpackHiW( t3a, t4a );\n\n\tb2FloatW t1b = b2UnpackLoW( b1b, b3b );\n\tb2FloatW t2b = b2UnpackLoW( b2b, b4b );\n\tb2FloatW t3b = b2UnpackHiW( b1b, b3b );\n\tb2FloatW t4b = b2UnpackHiW( b2b, b4b );\n\n\tsimdBody.dp.X = b2UnpackLoW( t1b, t2b );\n\tsimdBody.dp.Y = b2UnpackHiW( t1b, t2b );\n\tsimdBody.dq.C = b2UnpackLoW( t3b, t4b );\n\tsimdBody.dq.S = b2UnpackHiW( t3b, t4b );\n\n\treturn simdBody;\n}\n\n// This writes only the velocities back to the solver bodies\n// https://developer.arm.com/documentation/102107a/0100/Floating-point-4x4-matrix-transposition\nstatic void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody )\n{\n\t_Static_assert( sizeof( b2BodyState ) == 32, \"b2BodyState not 32 bytes\" );\n\tB2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );\n\n\t//\tb2FloatW x = b2SetW(0.0f, 1.0f, 2.0f, 3.0f);\n\t//\tb2FloatW y = b2SetW(4.0f, 5.0f, 6.0f, 7.0f);\n\t//\tb2FloatW z = b2SetW(8.0f, 9.0f, 10.0f, 11.0f);\n\t//\tb2FloatW w = b2SetW(12.0f, 13.0f, 14.0f, 15.0f);\n\t//\n\t//\tfloat32x4x2_t rr1 = vtrnq_f32( x, y );\n\t//\tfloat32x4x2_t rr2 = vtrnq_f32( z, w );\n\t//\n\t//\tfloat32x4_t b1 = vcombine_f32(vget_low_f32(rr1.val[0]), vget_low_f32(rr2.val[0]));\n\t//\tfloat32x4_t b2 = vcombine_f32(vget_low_f32(rr1.val[1]), vget_low_f32(rr2.val[1]));\n\t//\tfloat32x4_t b3 = vcombine_f32(vget_high_f32(rr1.val[0]), vget_high_f32(rr2.val[0]));\n\t//\tfloat32x4_t b4 = vcombine_f32(vget_high_f32(rr1.val[1]), vget_high_f32(rr2.val[1]));\n\n\t// transpose\n\tfloat32x4x2_t r1 = vtrnq_f32( simdBody->v.X, simdBody->v.Y );\n\tfloat32x4x2_t r2 = vtrnq_f32( simdBody->w, simdBody->flags );\n\n\t// I don't use any dummy body in the body array because this will lead to multithreaded sharing and the\n\t// associated cache flushing.\n\tif ( indices[0] != B2_NULL_INDEX && ( states[indices[0]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\tfloat32x4_t body1 = vcombine_f32( vget_low_f32( r1.val[0] ), vget_low_f32( r2.val[0] ) );\n\t\tb2StoreW( (float*)( states + indices[0] ), body1 );\n\t}\n\n\tif ( indices[1] != B2_NULL_INDEX && ( states[indices[1]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\tfloat32x4_t body2 = vcombine_f32( vget_low_f32( r1.val[1] ), vget_low_f32( r2.val[1] ) );\n\t\tb2StoreW( (float*)( states + indices[1] ), body2 );\n\t}\n\n\tif ( indices[2] != B2_NULL_INDEX && ( states[indices[2]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\tfloat32x4_t body3 = vcombine_f32( vget_high_f32( r1.val[0] ), vget_high_f32( r2.val[0] ) );\n\t\tb2StoreW( (float*)( states + indices[2] ), body3 );\n\t}\n\n\tif ( indices[3] != B2_NULL_INDEX && ( states[indices[3]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\tfloat32x4_t body4 = vcombine_f32( vget_high_f32( r1.val[1] ), vget_high_f32( r2.val[1] ) );\n\t\tb2StoreW( (float*)( states + indices[3] ), body4 );\n\t}\n}\n\n#elif defined( B2_SIMD_SSE2 )\n\n// This is a load and transpose\nstatic b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices )\n{\n\t_Static_assert( sizeof( b2BodyState ) == 32, \"b2BodyState not 32 bytes\" );\n\tB2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );\n\n\t// [vx vy w flags]\n\tb2FloatW identityA = b2ZeroW();\n\n\t// [dpx dpy dqc dqs]\n\tb2FloatW identityB = b2SetW( 0.0f, 0.0f, 1.0f, 0.0f );\n\n\tb2FloatW b1a = indices[0] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[0] ) + 0 );\n\tb2FloatW b1b = indices[0] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[0] ) + 4 );\n\tb2FloatW b2a = indices[1] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[1] ) + 0 );\n\tb2FloatW b2b = indices[1] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[1] ) + 4 );\n\tb2FloatW b3a = indices[2] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[2] ) + 0 );\n\tb2FloatW b3b = indices[2] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[2] ) + 4 );\n\tb2FloatW b4a = indices[3] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[3] ) + 0 );\n\tb2FloatW b4b = indices[3] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[3] ) + 4 );\n\n\t// [vx1 vx3 vy1 vy3]\n\tb2FloatW t1a = b2UnpackLoW( b1a, b3a );\n\n\t// [vx2 vx4 vy2 vy4]\n\tb2FloatW t2a = b2UnpackLoW( b2a, b4a );\n\n\t// [w1 w3 f1 f3]\n\tb2FloatW t3a = b2UnpackHiW( b1a, b3a );\n\n\t// [w2 w4 f2 f4]\n\tb2FloatW t4a = b2UnpackHiW( b2a, b4a );\n\n\tb2BodyStateW simdBody;\n\tsimdBody.v.X = b2UnpackLoW( t1a, t2a );\n\tsimdBody.v.Y = b2UnpackHiW( t1a, t2a );\n\tsimdBody.w = b2UnpackLoW( t3a, t4a );\n\tsimdBody.flags = b2UnpackHiW( t3a, t4a );\n\n\tb2FloatW t1b = b2UnpackLoW( b1b, b3b );\n\tb2FloatW t2b = b2UnpackLoW( b2b, b4b );\n\tb2FloatW t3b = b2UnpackHiW( b1b, b3b );\n\tb2FloatW t4b = b2UnpackHiW( b2b, b4b );\n\n\tsimdBody.dp.X = b2UnpackLoW( t1b, t2b );\n\tsimdBody.dp.Y = b2UnpackHiW( t1b, t2b );\n\tsimdBody.dq.C = b2UnpackLoW( t3b, t4b );\n\tsimdBody.dq.S = b2UnpackHiW( t3b, t4b );\n\n\treturn simdBody;\n}\n\n// This writes only the velocities back to the solver bodies\nstatic void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody )\n{\n\t_Static_assert( sizeof( b2BodyState ) == 32, \"b2BodyState not 32 bytes\" );\n\tB2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );\n\n\t// [vx1 vy1 vx2 vy2]\n\tb2FloatW t1 = b2UnpackLoW( simdBody->v.X, simdBody->v.Y );\n\t// [vx3 vy3 vx4 vy4]\n\tb2FloatW t2 = b2UnpackHiW( simdBody->v.X, simdBody->v.Y );\n\t// [w1 f1 w2 f2]\n\tb2FloatW t3 = b2UnpackLoW( simdBody->w, simdBody->flags );\n\t// [w3 f3 w4 f4]\n\tb2FloatW t4 = b2UnpackHiW( simdBody->w, simdBody->flags );\n\n#if 1\n\n\t// I don't use any dummy body in the body array because this will lead to multithreaded cache coherence problems.\n\tif ( indices[0] != B2_NULL_INDEX && ( states[indices[0]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\t// [t1.x t1.y t3.x t3.y]\n\t\tb2StoreW( (float*)( states + indices[0] ), _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ) );\n\t}\n\n\tif ( indices[1] != B2_NULL_INDEX && ( states[indices[1]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\t// [t1.z t1.w t3.z t3.w]\n\t\tb2StoreW( (float*)( states + indices[1] ), _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) ) );\n\t}\n\n\tif ( indices[2] != B2_NULL_INDEX && ( states[indices[2]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\t// [t2.x t2.y t4.x t4.y]\n\t\tb2StoreW( (float*)( states + indices[2] ), _mm_shuffle_ps( t2, t4, _MM_SHUFFLE( 1, 0, 1, 0 ) ) );\n\t}\n\n\tif ( indices[3] != B2_NULL_INDEX && ( states[indices[3]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\t// [t2.z t2.w t4.z t4.w]\n\t\tb2StoreW( (float*)( states + indices[3] ), _mm_shuffle_ps( t2, t4, _MM_SHUFFLE( 3, 2, 3, 2 ) ) );\n\t}\n\n#else\n\n\t// todo_erin this is here to test the impact of unsafe writes\n\n\tif ( indices[0] != B2_NULL_INDEX )\n\t{\n\t\t// [t1.x t1.y t3.x t3.y]\n\t\tb2StoreW( (float*)( states + indices[0] ), _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ) );\n\t}\n\n\tif ( indices[1] != B2_NULL_INDEX )\n\t{\n\t\t// [t1.z t1.w t3.z t3.w]\n\t\tb2StoreW( (float*)( states + indices[1] ), _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) ) );\n\t}\n\n\tif ( indices[2] != B2_NULL_INDEX)\n\t{\n\t\t// [t2.x t2.y t4.x t4.y]\n\t\tb2StoreW( (float*)( states + indices[2] ), _mm_shuffle_ps( t2, t4, _MM_SHUFFLE( 1, 0, 1, 0 ) ) );\n\t}\n\n\tif ( indices[3] != B2_NULL_INDEX )\n\t{\n\t\t// [t2.z t2.w t4.z t4.w]\n\t\tb2StoreW( (float*)( states + indices[3] ), _mm_shuffle_ps( t2, t4, _MM_SHUFFLE( 3, 2, 3, 2 ) ) );\n\t}\n\n#endif\n}\n\n#else\n\n// This is a load and transpose\nstatic b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices )\n{\n\tb2BodyState identity = b2_identityBodyState;\n\n\tb2BodyState s1 = indices[0] == B2_NULL_INDEX ? identity : states[indices[0]];\n\tb2BodyState s2 = indices[1] == B2_NULL_INDEX ? identity : states[indices[1]];\n\tb2BodyState s3 = indices[2] == B2_NULL_INDEX ? identity : states[indices[2]];\n\tb2BodyState s4 = indices[3] == B2_NULL_INDEX ? identity : states[indices[3]];\n\n\tb2BodyStateW simdBody;\n\tsimdBody.v.X = (b2FloatW){ s1.linearVelocity.x, s2.linearVelocity.x, s3.linearVelocity.x, s4.linearVelocity.x };\n\tsimdBody.v.Y = (b2FloatW){ s1.linearVelocity.y, s2.linearVelocity.y, s3.linearVelocity.y, s4.linearVelocity.y };\n\tsimdBody.w = (b2FloatW){ s1.angularVelocity, s2.angularVelocity, s3.angularVelocity, s4.angularVelocity };\n\tsimdBody.flags = (b2FloatW){ (float)s1.flags, (float)s2.flags, (float)s3.flags, (float)s4.flags };\n\tsimdBody.dp.X = (b2FloatW){ s1.deltaPosition.x, s2.deltaPosition.x, s3.deltaPosition.x, s4.deltaPosition.x };\n\tsimdBody.dp.Y = (b2FloatW){ s1.deltaPosition.y, s2.deltaPosition.y, s3.deltaPosition.y, s4.deltaPosition.y };\n\tsimdBody.dq.C = (b2FloatW){ s1.deltaRotation.c, s2.deltaRotation.c, s3.deltaRotation.c, s4.deltaRotation.c };\n\tsimdBody.dq.S = (b2FloatW){ s1.deltaRotation.s, s2.deltaRotation.s, s3.deltaRotation.s, s4.deltaRotation.s };\n\n\treturn simdBody;\n}\n\n// This writes only the velocities back to the solver bodies\nstatic void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody )\n{\n\tif ( indices[0] != B2_NULL_INDEX && ( states[indices[0]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\tb2BodyState* state = states + indices[0];\n\t\tstate->linearVelocity.x = simdBody->v.X.x;\n\t\tstate->linearVelocity.y = simdBody->v.Y.x;\n\t\tstate->angularVelocity = simdBody->w.x;\n\t}\n\n\tif ( indices[1] != B2_NULL_INDEX && ( states[indices[1]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\tb2BodyState* state = states + indices[1];\n\t\tstate->linearVelocity.x = simdBody->v.X.y;\n\t\tstate->linearVelocity.y = simdBody->v.Y.y;\n\t\tstate->angularVelocity = simdBody->w.y;\n\t}\n\n\tif ( indices[2] != B2_NULL_INDEX && ( states[indices[2]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\tb2BodyState* state = states + indices[2];\n\t\tstate->linearVelocity.x = simdBody->v.X.z;\n\t\tstate->linearVelocity.y = simdBody->v.Y.z;\n\t\tstate->angularVelocity = simdBody->w.z;\n\t}\n\n\tif ( indices[3] != B2_NULL_INDEX && ( states[indices[3]].flags & b2_dynamicFlag ) != 0 )\n\t{\n\t\tb2BodyState* state = states + indices[3];\n\t\tstate->linearVelocity.x = simdBody->v.X.w;\n\t\tstate->linearVelocity.y = simdBody->v.Y.w;\n\t\tstate->angularVelocity = simdBody->w.w;\n\t}\n}\n\n#endif\n\nvoid b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context )\n{\n\tb2TracyCZoneNC( prepare_contact, \"Prepare Contact\", b2_colorYellow, true );\n\tb2World* world = context->world;\n\tb2ContactSim** contacts = context->contacts;\n\tb2ContactConstraintSIMD* constraints = context->simdContactConstraints;\n\tb2BodyState* awakeStates = context->states;\n#if B2_VALIDATE\n\tb2Body* bodies = world->bodies.data;\n#endif\n\n\t// Stiffer for static contacts to avoid bodies getting pushed through the ground\n\tb2Softness contactSoftness = context->contactSoftness;\n\tb2Softness staticSoftness = context->staticSoftness;\n\tbool enableSoftening = world->enableContactSoftening;\n\n\tfloat warmStartScale = world->enableWarmStarting ? 1.0f : 0.0f;\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tb2ContactConstraintSIMD* constraint = constraints + i;\n\n\t\tfor ( int j = 0; j < B2_SIMD_WIDTH; ++j )\n\t\t{\n\t\t\tb2ContactSim* contactSim = contacts[B2_SIMD_WIDTH * i + j];\n\n\t\t\tif ( contactSim != NULL )\n\t\t\t{\n\t\t\t\tconst b2Manifold* manifold = &contactSim->manifold;\n\n\t\t\t\tint indexA = contactSim->bodySimIndexA;\n\t\t\t\tint indexB = contactSim->bodySimIndexB;\n\n#if B2_VALIDATE\n\t\t\t\tb2Body* bodyA = bodies + contactSim->bodyIdA;\n\t\t\t\tint validIndexA = bodyA->setIndex == b2_awakeSet ? bodyA->localIndex : B2_NULL_INDEX;\n\t\t\t\tb2Body* bodyB = bodies + contactSim->bodyIdB;\n\t\t\t\tint validIndexB = bodyB->setIndex == b2_awakeSet ? bodyB->localIndex : B2_NULL_INDEX;\n\n\t\t\t\tB2_ASSERT( indexA == validIndexA );\n\t\t\t\tB2_ASSERT( indexB == validIndexB );\n#endif\n\t\t\t\tconstraint->indexA[j] = indexA;\n\t\t\t\tconstraint->indexB[j] = indexB;\n\n\t\t\t\tb2Vec2 vA = b2Vec2_zero;\n\t\t\t\tfloat wA = 0.0f;\n\t\t\t\tfloat mA = contactSim->invMassA;\n\t\t\t\tfloat iA = contactSim->invIA;\n\t\t\t\tif ( indexA != B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\tb2BodyState* stateA = awakeStates + indexA;\n\t\t\t\t\tvA = stateA->linearVelocity;\n\t\t\t\t\twA = stateA->angularVelocity;\n\t\t\t\t}\n\n\t\t\t\tb2Vec2 vB = b2Vec2_zero;\n\t\t\t\tfloat wB = 0.0f;\n\t\t\t\tfloat mB = contactSim->invMassB;\n\t\t\t\tfloat iB = contactSim->invIB;\n\t\t\t\tif ( indexB != B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\tb2BodyState* stateB = awakeStates + indexB;\n\t\t\t\t\tvB = stateB->linearVelocity;\n\t\t\t\t\twB = stateB->angularVelocity;\n\t\t\t\t}\n\n\t\t\t\t( (float*)&constraint->invMassA )[j] = mA;\n\t\t\t\t( (float*)&constraint->invMassB )[j] = mB;\n\t\t\t\t( (float*)&constraint->invIA )[j] = iA;\n\t\t\t\t( (float*)&constraint->invIB )[j] = iB;\n\n\t\t\t\t{\n\t\t\t\t\tfloat k = iA + iB;\n\t\t\t\t\t( (float*)&constraint->rollingMass )[j] = k > 0.0f ? 1.0f / k : 0.0f;\n\t\t\t\t}\n\n\t\t\t\tb2Softness soft = contactSoftness;\n\t\t\t\tif (indexA == B2_NULL_INDEX || indexB == B2_NULL_INDEX)\n\t\t\t\t{\n\t\t\t\t\tsoft = staticSoftness;\n\t\t\t\t}\n\t\t\t\telse if (enableSoftening)\n\t\t\t\t{\n\t\t\t\t\t// todo experimental feature\n\t\t\t\t\tfloat contactHertz = b2MinFloat( world->contactHertz, 0.125f * context->inv_h );\n\t\t\t\t\tfloat ratio = 1.0f;\n\t\t\t\t\tif ( mA < mB )\n\t\t\t\t\t{\n\t\t\t\t\t\tratio = b2MaxFloat( 0.5f, mA / mB );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( mB < mA )\n\t\t\t\t\t{\n\t\t\t\t\t\tratio = b2MaxFloat( 0.5f, mB / mA );\n\t\t\t\t\t}\n\t\t\t\t\tsoft = b2MakeSoft( ratio * contactHertz, ratio * world->contactDampingRatio, context->h );\n\t\t\t\t}\n\n\t\t\t\tb2Vec2 normal = manifold->normal;\n\t\t\t\t( (float*)&constraint->normal.X )[j] = normal.x;\n\t\t\t\t( (float*)&constraint->normal.Y )[j] = normal.y;\n\n\t\t\t\t( (float*)&constraint->friction )[j] = contactSim->friction;\n\t\t\t\t( (float*)&constraint->tangentSpeed )[j] = contactSim->tangentSpeed;\n\t\t\t\t( (float*)&constraint->restitution )[j] = contactSim->restitution;\n\t\t\t\t( (float*)&constraint->rollingResistance )[j] = contactSim->rollingResistance;\n\t\t\t\t( (float*)&constraint->rollingImpulse )[j] = warmStartScale * manifold->rollingImpulse;\n\n\t\t\t\t( (float*)&constraint->biasRate )[j] = soft.biasRate;\n\t\t\t\t( (float*)&constraint->massScale )[j] = soft.massScale;\n\t\t\t\t( (float*)&constraint->impulseScale )[j] = soft.impulseScale;\n\n\t\t\t\tb2Vec2 tangent = b2RightPerp( normal );\n\n\t\t\t\t{\n\t\t\t\t\tconst b2ManifoldPoint* mp = manifold->points + 0;\n\n\t\t\t\t\tb2Vec2 rA = mp->anchorA;\n\t\t\t\t\tb2Vec2 rB = mp->anchorB;\n\n\t\t\t\t\t( (float*)&constraint->anchorA1.X )[j] = rA.x;\n\t\t\t\t\t( (float*)&constraint->anchorA1.Y )[j] = rA.y;\n\t\t\t\t\t( (float*)&constraint->anchorB1.X )[j] = rB.x;\n\t\t\t\t\t( (float*)&constraint->anchorB1.Y )[j] = rB.y;\n\n\t\t\t\t\t( (float*)&constraint->baseSeparation1 )[j] = mp->separation - b2Dot( b2Sub( rB, rA ), normal );\n\n\t\t\t\t\t( (float*)&constraint->normalImpulse1 )[j] = warmStartScale * mp->normalImpulse;\n\t\t\t\t\t( (float*)&constraint->tangentImpulse1 )[j] = warmStartScale * mp->tangentImpulse;\n\t\t\t\t\t( (float*)&constraint->totalNormalImpulse1 )[j] = 0.0f;\n\n\t\t\t\t\tfloat rnA = b2Cross( rA, normal );\n\t\t\t\t\tfloat rnB = b2Cross( rB, normal );\n\t\t\t\t\tfloat kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB;\n\t\t\t\t\t( (float*)&constraint->normalMass1 )[j] = kNormal > 0.0f ? 1.0f / kNormal : 0.0f;\n\n\t\t\t\t\tfloat rtA = b2Cross( rA, tangent );\n\t\t\t\t\tfloat rtB = b2Cross( rB, tangent );\n\t\t\t\t\tfloat kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB;\n\t\t\t\t\t( (float*)&constraint->tangentMass1 )[j] = kTangent > 0.0f ? 1.0f / kTangent : 0.0f;\n\n\t\t\t\t\t// relative velocity for restitution\n\t\t\t\t\tb2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );\n\t\t\t\t\tb2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );\n\t\t\t\t\t( (float*)&constraint->relativeVelocity1 )[j] = b2Dot( normal, b2Sub( vrB, vrA ) );\n\t\t\t\t}\n\n\t\t\t\tint pointCount = manifold->pointCount;\n\t\t\t\tB2_ASSERT( 0 < pointCount && pointCount <= 2 );\n\n\t\t\t\tif ( pointCount == 2 )\n\t\t\t\t{\n\t\t\t\t\tconst b2ManifoldPoint* mp = manifold->points + 1;\n\n\t\t\t\t\tb2Vec2 rA = mp->anchorA;\n\t\t\t\t\tb2Vec2 rB = mp->anchorB;\n\n\t\t\t\t\t( (float*)&constraint->anchorA2.X )[j] = rA.x;\n\t\t\t\t\t( (float*)&constraint->anchorA2.Y )[j] = rA.y;\n\t\t\t\t\t( (float*)&constraint->anchorB2.X )[j] = rB.x;\n\t\t\t\t\t( (float*)&constraint->anchorB2.Y )[j] = rB.y;\n\n\t\t\t\t\t( (float*)&constraint->baseSeparation2 )[j] = mp->separation - b2Dot( b2Sub( rB, rA ), normal );\n\n\t\t\t\t\t( (float*)&constraint->normalImpulse2 )[j] = warmStartScale * mp->normalImpulse;\n\t\t\t\t\t( (float*)&constraint->tangentImpulse2 )[j] = warmStartScale * mp->tangentImpulse;\n\t\t\t\t\t( (float*)&constraint->totalNormalImpulse2 )[j] = 0.0f;\n\n\t\t\t\t\tfloat rnA = b2Cross( rA, normal );\n\t\t\t\t\tfloat rnB = b2Cross( rB, normal );\n\t\t\t\t\tfloat kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB;\n\t\t\t\t\t( (float*)&constraint->normalMass2 )[j] = kNormal > 0.0f ? 1.0f / kNormal : 0.0f;\n\n\t\t\t\t\tfloat rtA = b2Cross( rA, tangent );\n\t\t\t\t\tfloat rtB = b2Cross( rB, tangent );\n\t\t\t\t\tfloat kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB;\n\t\t\t\t\t( (float*)&constraint->tangentMass2 )[j] = kTangent > 0.0f ? 1.0f / kTangent : 0.0f;\n\n\t\t\t\t\t// relative velocity for restitution\n\t\t\t\t\tb2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );\n\t\t\t\t\tb2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );\n\t\t\t\t\t( (float*)&constraint->relativeVelocity2 )[j] = b2Dot( normal, b2Sub( vrB, vrA ) );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// dummy data that has no effect\n\t\t\t\t\t( (float*)&constraint->baseSeparation2 )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->normalImpulse2 )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->tangentImpulse2 )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->totalNormalImpulse2 )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->anchorA2.X )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->anchorA2.Y )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->anchorB2.X )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->anchorB2.Y )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->normalMass2 )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->tangentMass2 )[j] = 0.0f;\n\t\t\t\t\t( (float*)&constraint->relativeVelocity2 )[j] = 0.0f;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// SIMD remainder\n\t\t\t\tconstraint->indexA[j] = B2_NULL_INDEX;\n\t\t\t\tconstraint->indexB[j] = B2_NULL_INDEX;\n\n\t\t\t\t( (float*)&constraint->invMassA )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->invMassB )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->invIA )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->invIB )[j] = 0.0f;\n\n\t\t\t\t( (float*)&constraint->normal.X )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->normal.Y )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->friction )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->tangentSpeed )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->rollingResistance )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->rollingMass )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->rollingImpulse )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->biasRate )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->massScale )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->impulseScale )[j] = 0.0f;\n\n\t\t\t\t( (float*)&constraint->anchorA1.X )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->anchorA1.Y )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->anchorB1.X )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->anchorB1.Y )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->baseSeparation1 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->normalImpulse1 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->tangentImpulse1 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->totalNormalImpulse1 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->normalMass1 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->tangentMass1 )[j] = 0.0f;\n\n\t\t\t\t( (float*)&constraint->anchorA2.X )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->anchorA2.Y )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->anchorB2.X )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->anchorB2.Y )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->baseSeparation2 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->normalImpulse2 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->tangentImpulse2 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->totalNormalImpulse2 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->normalMass2 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->tangentMass2 )[j] = 0.0f;\n\n\t\t\t\t( (float*)&constraint->restitution )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->relativeVelocity1 )[j] = 0.0f;\n\t\t\t\t( (float*)&constraint->relativeVelocity2 )[j] = 0.0f;\n\t\t\t}\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( prepare_contact );\n}\n\nvoid b2WarmStartContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex )\n{\n\tb2TracyCZoneNC( warm_start_contact, \"Warm Start\", b2_colorGreen, true );\n\n\tb2BodyState* states = context->states;\n\tb2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints;\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tb2ContactConstraintSIMD* c = constraints + i;\n\t\tb2BodyStateW bA = b2GatherBodies( states, c->indexA );\n\t\tb2BodyStateW bB = b2GatherBodies( states, c->indexB );\n\n\t\tb2FloatW tangentX = c->normal.Y;\n\t\tb2FloatW tangentY = b2SubW( b2ZeroW(), c->normal.X );\n\n\t\t{\n\t\t\t// fixed anchors\n\t\t\tb2Vec2W rA = c->anchorA1;\n\t\t\tb2Vec2W rB = c->anchorB1;\n\n\t\t\tb2Vec2W P;\n\t\t\tP.X = b2AddW( b2MulW( c->normalImpulse1, c->normal.X ), b2MulW( c->tangentImpulse1, tangentX ) );\n\t\t\tP.Y = b2AddW( b2MulW( c->normalImpulse1, c->normal.Y ), b2MulW( c->tangentImpulse1, tangentY ) );\n\t\t\tbA.w = b2MulSubW( bA.w, c->invIA, b2CrossW( rA, P ) );\n\t\t\tbA.v.X = b2MulSubW( bA.v.X, c->invMassA, P.X );\n\t\t\tbA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, P.Y );\n\t\t\tbB.w = b2MulAddW( bB.w, c->invIB, b2CrossW( rB, P ) );\n\t\t\tbB.v.X = b2MulAddW( bB.v.X, c->invMassB, P.X );\n\t\t\tbB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, P.Y );\n\n\t\t\tc->totalNormalImpulse1 = b2AddW( c->totalNormalImpulse1, c->normalImpulse1 );\n\t\t}\n\n\t\t{\n\t\t\t// fixed anchors\n\t\t\tb2Vec2W rA = c->anchorA2;\n\t\t\tb2Vec2W rB = c->anchorB2;\n\n\t\t\tb2Vec2W P;\n\t\t\tP.X = b2AddW( b2MulW( c->normalImpulse2, c->normal.X ), b2MulW( c->tangentImpulse2, tangentX ) );\n\t\t\tP.Y = b2AddW( b2MulW( c->normalImpulse2, c->normal.Y ), b2MulW( c->tangentImpulse2, tangentY ) );\n\t\t\tbA.w = b2MulSubW( bA.w, c->invIA, b2CrossW( rA, P ) );\n\t\t\tbA.v.X = b2MulSubW( bA.v.X, c->invMassA, P.X );\n\t\t\tbA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, P.Y );\n\t\t\tbB.w = b2MulAddW( bB.w, c->invIB, b2CrossW( rB, P ) );\n\t\t\tbB.v.X = b2MulAddW( bB.v.X, c->invMassB, P.X );\n\t\t\tbB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, P.Y );\n\n\t\t\tc->totalNormalImpulse2 = b2AddW( c->totalNormalImpulse2, c->normalImpulse2 );\n\t\t}\n\n\t\tbA.w = b2MulSubW( bA.w, c->invIA, c->rollingImpulse );\n\t\tbB.w = b2MulAddW( bB.w, c->invIB, c->rollingImpulse );\n\n\t\tb2ScatterBodies( states, c->indexA, &bA );\n\t\tb2ScatterBodies( states, c->indexB, &bB );\n\t}\n\n\tb2TracyCZoneEnd( warm_start_contact );\n}\n\nvoid b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex, bool useBias )\n{\n\tb2TracyCZoneNC( solve_contact, \"Solve Contact\", b2_colorAliceBlue, true );\n\n\tb2BodyState* states = context->states;\n\tb2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints;\n\tb2FloatW inv_h = b2SplatW( context->inv_h );\n\tb2FloatW contactSpeed = b2SplatW( -context->world->contactSpeed );\n\tb2FloatW oneW = b2SplatW( 1.0f );\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tb2ContactConstraintSIMD* c = constraints + i;\n\n\t\tb2BodyStateW bA = b2GatherBodies( states, c->indexA );\n\t\tb2BodyStateW bB = b2GatherBodies( states, c->indexB );\n\n\t\tb2FloatW biasRate, massScale, impulseScale;\n\t\tif ( useBias )\n\t\t{\n\t\t\tbiasRate = b2MulW( c->massScale, c->biasRate );\n\t\t\tmassScale = c->massScale;\n\t\t\timpulseScale = c->impulseScale;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbiasRate = b2ZeroW();\n\t\t\tmassScale = oneW;\n\t\t\timpulseScale = b2ZeroW();\n\t\t}\n\n\t\tb2FloatW totalNormalImpulse = b2ZeroW();\n\n\t\tb2Vec2W dp = { b2SubW( bB.dp.X, bA.dp.X ), b2SubW( bB.dp.Y, bA.dp.Y ) };\n\n\t\t// point1 non-penetration constraint\n\t\t{\n\t\t\t// Fixed anchors for impulses\n\t\t\tb2Vec2W rA = c->anchorA1;\n\t\t\tb2Vec2W rB = c->anchorB1;\n\n\t\t\t// Moving anchors for current separation\n\t\t\tb2Vec2W rsA = b2RotateVectorW( bA.dq, rA );\n\t\t\tb2Vec2W rsB = b2RotateVectorW( bB.dq, rB );\n\n\t\t\t// compute current separation\n\t\t\t// this is subject to round-off error if the anchor is far from the body center of mass\n\t\t\tb2Vec2W ds = { b2AddW( dp.X, b2SubW( rsB.X, rsA.X ) ), b2AddW( dp.Y, b2SubW( rsB.Y, rsA.Y ) ) };\n\t\t\tb2FloatW s = b2AddW( b2DotW( c->normal, ds ), c->baseSeparation1 );\n\n\t\t\t// Apply speculative bias if separation is greater than zero, otherwise apply soft constraint bias\n\t\t\t// The contactSpeed is meant to limit stiffness, not increase it.\n\t\t\tb2FloatW mask = b2GreaterThanW( s, b2ZeroW() );\n\t\t\tb2FloatW specBias = b2MulW( s, inv_h );\n\t\t\tb2FloatW softBias = b2MaxW( b2MulW( biasRate, s ), contactSpeed );\n\n\t\t\t// todo try b2MaxW(softBias, specBias);\n\t\t\tb2FloatW bias = b2BlendW( softBias, specBias, mask );\n\n\t\t\tb2FloatW pointMassScale = b2BlendW( massScale, oneW, mask );\n\t\t\tb2FloatW pointImpulseScale = b2BlendW( impulseScale, b2ZeroW(), mask );\n\n\t\t\t// Relative velocity at contact\n\t\t\tb2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );\n\t\t\tb2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );\n\t\t\tb2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) );\n\n\t\t\t// Compute normal impulse\n\t\t\tb2FloatW negImpulse = b2AddW( b2MulW( c->normalMass1, b2AddW( b2MulW( pointMassScale, vn ), bias ) ),\n\t\t\t\t\t\t\t\t\t\t  b2MulW( pointImpulseScale, c->normalImpulse1 ) );\n\n\t\t\t// Clamp the accumulated impulse\n\t\t\tb2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse1, negImpulse ), b2ZeroW() );\n\t\t\tb2FloatW impulse = b2SubW( newImpulse, c->normalImpulse1 );\n\t\t\tc->normalImpulse1 = newImpulse;\n\t\t\tc->totalNormalImpulse1 = b2AddW( c->totalNormalImpulse1, impulse );\n\n\t\t\ttotalNormalImpulse = b2AddW( totalNormalImpulse, newImpulse );\n\n\t\t\t// Apply contact impulse\n\t\t\tb2FloatW Px = b2MulW( impulse, c->normal.X );\n\t\t\tb2FloatW Py = b2MulW( impulse, c->normal.Y );\n\n\t\t\tbA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );\n\t\t\tbA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );\n\t\t\tbA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );\n\n\t\t\tbB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );\n\t\t\tbB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );\n\t\t\tbB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );\n\t\t}\n\n\t\t// second point non-penetration constraint\n\t\t{\n\t\t\t// moving anchors for current separation\n\t\t\tb2Vec2W rsA = b2RotateVectorW( bA.dq, c->anchorA2 );\n\t\t\tb2Vec2W rsB = b2RotateVectorW( bB.dq, c->anchorB2 );\n\n\t\t\t// compute current separation\n\t\t\tb2Vec2W ds = { b2AddW( dp.X, b2SubW( rsB.X, rsA.X ) ), b2AddW( dp.Y, b2SubW( rsB.Y, rsA.Y ) ) };\n\t\t\tb2FloatW s = b2AddW( b2DotW( c->normal, ds ), c->baseSeparation2 );\n\n\t\t\tb2FloatW mask = b2GreaterThanW( s, b2ZeroW() );\n\t\t\tb2FloatW specBias = b2MulW( s, inv_h );\n\t\t\tb2FloatW softBias = b2MaxW( b2MulW( biasRate, s ), contactSpeed );\n\t\t\tb2FloatW bias = b2BlendW( softBias, specBias, mask );\n\n\t\t\tb2FloatW pointMassScale = b2BlendW( massScale, oneW, mask );\n\t\t\tb2FloatW pointImpulseScale = b2BlendW( impulseScale, b2ZeroW(), mask );\n\n\t\t\t// fixed anchors for Jacobians\n\t\t\tb2Vec2W rA = c->anchorA2;\n\t\t\tb2Vec2W rB = c->anchorB2;\n\n\t\t\t// Relative velocity at contact\n\t\t\tb2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );\n\t\t\tb2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );\n\t\t\tb2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) );\n\n\t\t\t// Compute normal impulse\n\t\t\tb2FloatW negImpulse = b2AddW( b2MulW( c->normalMass2, b2AddW( b2MulW( pointMassScale, vn ), bias ) ),\n\t\t\t\t\t\t\t\t\t\t  b2MulW( pointImpulseScale, c->normalImpulse2 ) );\n\n\t\t\t// Clamp the accumulated impulse\n\t\t\tb2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse2, negImpulse ), b2ZeroW() );\n\t\t\tb2FloatW impulse = b2SubW( newImpulse, c->normalImpulse2 );\n\t\t\tc->normalImpulse2 = newImpulse;\n\t\t\tc->totalNormalImpulse2 = b2AddW( c->totalNormalImpulse2, impulse );\n\n\t\t\ttotalNormalImpulse = b2AddW( totalNormalImpulse, newImpulse );\n\n\t\t\t// Apply contact impulse\n\t\t\tb2FloatW Px = b2MulW( impulse, c->normal.X );\n\t\t\tb2FloatW Py = b2MulW( impulse, c->normal.Y );\n\n\t\t\tbA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );\n\t\t\tbA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );\n\t\t\tbA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );\n\n\t\t\tbB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );\n\t\t\tbB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );\n\t\t\tbB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );\n\t\t}\n\n\t\tb2FloatW tangentX = c->normal.Y;\n\t\tb2FloatW tangentY = b2SubW( b2ZeroW(), c->normal.X );\n\n\t\t// point 1 friction constraint\n\t\t{\n\t\t\t// fixed anchors for Jacobians\n\t\t\tb2Vec2W rA = c->anchorA1;\n\t\t\tb2Vec2W rB = c->anchorB1;\n\n\t\t\t// Relative velocity at contact\n\t\t\tb2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );\n\t\t\tb2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );\n\t\t\tb2FloatW vt = b2AddW( b2MulW( dvx, tangentX ), b2MulW( dvy, tangentY ) );\n\n\t\t\t// Tangent speed (conveyor belt)\n\t\t\tvt = b2SubW( vt, c->tangentSpeed );\n\n\t\t\t// Compute tangent force\n\t\t\tb2FloatW negImpulse = b2MulW( c->tangentMass1, vt );\n\n\t\t\t// Clamp the accumulated force\n\t\t\tb2FloatW maxFriction = b2MulW( c->friction, c->normalImpulse1 );\n\t\t\tb2FloatW newImpulse = b2SubW( c->tangentImpulse1, negImpulse );\n\t\t\tnewImpulse = b2MaxW( b2SubW( b2ZeroW(), maxFriction ), b2MinW( newImpulse, maxFriction ) );\n\t\t\tb2FloatW impulse = b2SubW( newImpulse, c->tangentImpulse1 );\n\t\t\tc->tangentImpulse1 = newImpulse;\n\n\t\t\t// Apply contact impulse\n\t\t\tb2FloatW Px = b2MulW( impulse, tangentX );\n\t\t\tb2FloatW Py = b2MulW( impulse, tangentY );\n\n\t\t\tbA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );\n\t\t\tbA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );\n\t\t\tbA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );\n\n\t\t\tbB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );\n\t\t\tbB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );\n\t\t\tbB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );\n\t\t}\n\n\t\t// second point friction constraint\n\t\t{\n\t\t\t// fixed anchors for Jacobians\n\t\t\tb2Vec2W rA = c->anchorA2;\n\t\t\tb2Vec2W rB = c->anchorB2;\n\n\t\t\t// Relative velocity at contact\n\t\t\tb2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );\n\t\t\tb2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );\n\t\t\tb2FloatW vt = b2AddW( b2MulW( dvx, tangentX ), b2MulW( dvy, tangentY ) );\n\n\t\t\t// Tangent speed (conveyor belt)\n\t\t\tvt = b2SubW( vt, c->tangentSpeed );\n\n\t\t\t// Compute tangent force\n\t\t\tb2FloatW negImpulse = b2MulW( c->tangentMass2, vt );\n\n\t\t\t// Clamp the accumulated force\n\t\t\tb2FloatW maxFriction = b2MulW( c->friction, c->normalImpulse2 );\n\t\t\tb2FloatW newImpulse = b2SubW( c->tangentImpulse2, negImpulse );\n\t\t\tnewImpulse = b2MaxW( b2SubW( b2ZeroW(), maxFriction ), b2MinW( newImpulse, maxFriction ) );\n\t\t\tb2FloatW impulse = b2SubW( newImpulse, c->tangentImpulse2 );\n\t\t\tc->tangentImpulse2 = newImpulse;\n\n\t\t\t// Apply contact impulse\n\t\t\tb2FloatW Px = b2MulW( impulse, tangentX );\n\t\t\tb2FloatW Py = b2MulW( impulse, tangentY );\n\n\t\t\tbA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );\n\t\t\tbA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );\n\t\t\tbA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );\n\n\t\t\tbB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );\n\t\t\tbB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );\n\t\t\tbB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );\n\t\t}\n\n\t\t// Rolling resistance\n\t\t{\n\t\t\tb2FloatW deltaLambda = b2MulW( c->rollingMass, b2SubW( bA.w, bB.w ) );\n\t\t\tb2FloatW lambda = c->rollingImpulse;\n\t\t\tb2FloatW maxLambda = b2MulW( c->rollingResistance, totalNormalImpulse );\n\t\t\tc->rollingImpulse = b2SymClampW( b2AddW( lambda, deltaLambda ), maxLambda );\n\t\t\tdeltaLambda = b2SubW( c->rollingImpulse, lambda );\n\n\t\t\tbA.w = b2MulSubW( bA.w, c->invIA, deltaLambda );\n\t\t\tbB.w = b2MulAddW( bB.w, c->invIB, deltaLambda );\n\t\t}\n\n\t\tb2ScatterBodies( states, c->indexA, &bA );\n\t\tb2ScatterBodies( states, c->indexB, &bB );\n\t}\n\n\tb2TracyCZoneEnd( solve_contact );\n}\n\nvoid b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex )\n{\n\tb2TracyCZoneNC( restitution, \"Restitution\", b2_colorDodgerBlue, true );\n\n\tb2BodyState* states = context->states;\n\tb2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints;\n\tb2FloatW threshold = b2SplatW( context->world->restitutionThreshold );\n\tb2FloatW zero = b2ZeroW();\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tb2ContactConstraintSIMD* c = constraints + i;\n\n\t\tif ( b2AllZeroW( c->restitution ) )\n\t\t{\n\t\t\t// No lanes have restitution. Common case.\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Create a mask based on restitution so that lanes with no restitution are not affected\n\t\t// by the calculations below.\n\t\tb2FloatW restitutionMask = b2EqualsW( c->restitution, zero );\n\n\t\tb2BodyStateW bA = b2GatherBodies( states, c->indexA );\n\t\tb2BodyStateW bB = b2GatherBodies( states, c->indexB );\n\n\t\t// first point non-penetration constraint\n\t\t{\n\t\t\t// Set effective mass to zero if restitution should not be applied\n\t\t\tb2FloatW mask1 = b2GreaterThanW( b2AddW( c->relativeVelocity1, threshold ), zero );\n\t\t\tb2FloatW mask2 = b2EqualsW( c->totalNormalImpulse1, zero );\n\t\t\tb2FloatW mask = b2OrW( b2OrW( mask1, mask2 ), restitutionMask );\n\t\t\tb2FloatW mass = b2BlendW( c->normalMass1, zero, mask );\n\n\t\t\t// fixed anchors for Jacobians\n\t\t\tb2Vec2W rA = c->anchorA1;\n\t\t\tb2Vec2W rB = c->anchorB1;\n\n\t\t\t// Relative velocity at contact\n\t\t\tb2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );\n\t\t\tb2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );\n\t\t\tb2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) );\n\n\t\t\t// Compute normal impulse\n\t\t\tb2FloatW negImpulse = b2MulW( mass, b2AddW( vn, b2MulW( c->restitution, c->relativeVelocity1 ) ) );\n\n\t\t\t// Clamp the accumulated impulse\n\t\t\tb2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse1, negImpulse ), b2ZeroW() );\n\t\t\tb2FloatW deltaImpulse = b2SubW( newImpulse, c->normalImpulse1 );\n\t\t\tc->normalImpulse1 = newImpulse;\n\n\t\t\tc->totalNormalImpulse1 = b2AddW( c->totalNormalImpulse1, deltaImpulse );\n\n\t\t\t// Apply contact impulse\n\t\t\tb2FloatW Px = b2MulW( deltaImpulse, c->normal.X );\n\t\t\tb2FloatW Py = b2MulW( deltaImpulse, c->normal.Y );\n\n\t\t\tbA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );\n\t\t\tbA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );\n\t\t\tbA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );\n\n\t\t\tbB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );\n\t\t\tbB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );\n\t\t\tbB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );\n\t\t}\n\n\t\t// second point non-penetration constraint\n\t\t{\n\t\t\t// Set effective mass to zero if restitution should not be applied\n\t\t\tb2FloatW mask1 = b2GreaterThanW( b2AddW( c->relativeVelocity2, threshold ), zero );\n\t\t\tb2FloatW mask2 = b2EqualsW( c->totalNormalImpulse2, zero );\n\t\t\tb2FloatW mask = b2OrW( b2OrW( mask1, mask2 ), restitutionMask );\n\t\t\tb2FloatW mass = b2BlendW( c->normalMass2, zero, mask );\n\n\t\t\t// fixed anchors for Jacobians\n\t\t\tb2Vec2W rA = c->anchorA2;\n\t\t\tb2Vec2W rB = c->anchorB2;\n\n\t\t\t// Relative velocity at contact\n\t\t\tb2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );\n\t\t\tb2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );\n\t\t\tb2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) );\n\n\t\t\t// Compute normal impulse\n\t\t\tb2FloatW negImpulse = b2MulW( mass, b2AddW( vn, b2MulW( c->restitution, c->relativeVelocity2 ) ) );\n\n\t\t\t// Clamp the accumulated impulse\n\t\t\tb2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse2, negImpulse ), b2ZeroW() );\n\t\t\tb2FloatW deltaImpulse = b2SubW( newImpulse, c->normalImpulse2 );\n\t\t\tc->normalImpulse2 = newImpulse;\n\n\t\t\tc->totalNormalImpulse2 = b2AddW( c->totalNormalImpulse2, deltaImpulse );\n\n\t\t\t// Apply contact impulse\n\t\t\tb2FloatW Px = b2MulW( deltaImpulse, c->normal.X );\n\t\t\tb2FloatW Py = b2MulW( deltaImpulse, c->normal.Y );\n\n\t\t\tbA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );\n\t\t\tbA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );\n\t\t\tbA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );\n\n\t\t\tbB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );\n\t\t\tbB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );\n\t\t\tbB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );\n\t\t}\n\n\t\tb2ScatterBodies( states, c->indexA, &bA );\n\t\tb2ScatterBodies( states, c->indexB, &bB );\n\t}\n\n\tb2TracyCZoneEnd( restitution );\n}\n\nvoid b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context )\n{\n\tb2TracyCZoneNC( store_impulses, \"Store\", b2_colorFireBrick, true );\n\n\tb2ContactSim** contacts = context->contacts;\n\tconst b2ContactConstraintSIMD* constraints = context->simdContactConstraints;\n\n\tb2Manifold dummy = { 0 };\n\n\tfor ( int constraintIndex = startIndex; constraintIndex < endIndex; ++constraintIndex )\n\t{\n\t\tconst b2ContactConstraintSIMD* c = constraints + constraintIndex;\n\t\tconst float* rollingImpulse = (float*)&c->rollingImpulse;\n\t\tconst float* normalImpulse1 = (float*)&c->normalImpulse1;\n\t\tconst float* normalImpulse2 = (float*)&c->normalImpulse2;\n\t\tconst float* tangentImpulse1 = (float*)&c->tangentImpulse1;\n\t\tconst float* tangentImpulse2 = (float*)&c->tangentImpulse2;\n\t\tconst float* totalNormalImpulse1 = (float*)&c->totalNormalImpulse1;\n\t\tconst float* totalNormalImpulse2 = (float*)&c->totalNormalImpulse2;\n\t\tconst float* normalVelocity1 = (float*)&c->relativeVelocity1;\n\t\tconst float* normalVelocity2 = (float*)&c->relativeVelocity2;\n\n\t\tint baseIndex = B2_SIMD_WIDTH * constraintIndex;\n\n\t\tfor ( int laneIndex = 0; laneIndex < B2_SIMD_WIDTH; ++laneIndex )\n\t\t{\n\t\t\tb2Manifold* m = contacts[baseIndex + laneIndex] == NULL ? &dummy : &contacts[baseIndex + laneIndex]->manifold;\n\t\t\tm->rollingImpulse = rollingImpulse[laneIndex];\n\n\t\t\tm->points[0].normalImpulse = normalImpulse1[laneIndex];\n\t\t\tm->points[0].tangentImpulse = tangentImpulse1[laneIndex];\n\t\t\tm->points[0].totalNormalImpulse = totalNormalImpulse1[laneIndex];\n\t\t\tm->points[0].normalVelocity = normalVelocity1[laneIndex];\n\n\t\t\tm->points[1].normalImpulse = normalImpulse2[laneIndex];\n\t\t\tm->points[1].tangentImpulse = tangentImpulse2[laneIndex];\n\t\t\tm->points[1].totalNormalImpulse = totalNormalImpulse2[laneIndex];\n\t\t\tm->points[1].normalVelocity = normalVelocity2[laneIndex];\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( store_impulses );\n}\n"
  },
  {
    "path": "src/contact_solver.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"solver.h\"\n\ntypedef struct b2ContactSim b2ContactSim;\n\ntypedef struct b2ContactConstraintPoint\n{\n\tb2Vec2 anchorA, anchorB;\n\tfloat baseSeparation;\n\tfloat relativeVelocity;\n\tfloat normalImpulse;\n\tfloat tangentImpulse;\n\tfloat totalNormalImpulse;\n\tfloat normalMass;\n\tfloat tangentMass;\n} b2ContactConstraintPoint;\n\ntypedef struct b2ContactConstraint\n{\n\tint indexA;\n\tint indexB;\n\tb2ContactConstraintPoint points[2];\n\tb2Vec2 normal;\n\tfloat invMassA, invMassB;\n\tfloat invIA, invIB;\n\tfloat friction;\n\tfloat restitution;\n\tfloat tangentSpeed;\n\tfloat rollingResistance;\n\tfloat rollingMass;\n\tfloat rollingImpulse;\n\tb2Softness softness;\n\tint pointCount;\n} b2ContactConstraint;\n\nint b2GetContactConstraintSIMDByteCount( void );\n\n// Overflow contacts don't fit into the constraint graph coloring\nvoid b2PrepareOverflowContacts( b2StepContext* context );\nvoid b2WarmStartOverflowContacts( b2StepContext* context );\nvoid b2SolveOverflowContacts( b2StepContext* context, bool useBias );\nvoid b2ApplyOverflowRestitution( b2StepContext* context );\nvoid b2StoreOverflowImpulses( b2StepContext* context );\n\n// Contacts that live within the constraint graph coloring\nvoid b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context );\nvoid b2WarmStartContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex );\nvoid b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex, bool useBias );\nvoid b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex );\nvoid b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context );\n"
  },
  {
    "path": "src/core.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"core.h\"\n\n#include \"box2d/math_functions.h\"\n\n#if defined( B2_COMPILER_MSVC )\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#include <stdlib.h>\n#else\n#include <stdlib.h>\n#endif\n\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef BOX2D_PROFILE\n\n#include <tracy/TracyC.h>\n#define b2TracyCAlloc( ptr, size ) TracyCAlloc( ptr, size )\n#define b2TracyCFree( ptr ) TracyCFree( ptr )\n\n#else\n\n#define b2TracyCAlloc( ptr, size )\n#define b2TracyCFree( ptr )\n\n#endif\n\n#include \"atomic.h\"\n\n// This allows the user to change the length units at runtime\nfloat b2_lengthUnitsPerMeter = 1.0f;\n\nvoid b2SetLengthUnitsPerMeter( float lengthUnits )\n{\n\tB2_ASSERT( b2IsValidFloat( lengthUnits ) && lengthUnits > 0.0f );\n\tb2_lengthUnitsPerMeter = lengthUnits;\n}\n\nfloat b2GetLengthUnitsPerMeter( void )\n{\n\treturn b2_lengthUnitsPerMeter;\n}\n\nstatic int b2DefaultAssertFcn( const char* condition, const char* fileName, int lineNumber )\n{\n\tprintf( \"BOX2D ASSERTION: %s, %s, line %d\\n\", condition, fileName, lineNumber );\n\n\t// return non-zero to break to debugger\n\treturn 1;\n}\n\nstatic b2AssertFcn* b2AssertHandler = b2DefaultAssertFcn;\n\nvoid b2SetAssertFcn( b2AssertFcn* assertFcn )\n{\n\tB2_ASSERT( assertFcn != NULL );\n\tb2AssertHandler = assertFcn;\n}\n\n#if !defined( NDEBUG ) || defined( B2_ENABLE_ASSERT )\nint b2InternalAssertFcn( const char* condition, const char* fileName, int lineNumber )\n{\n\treturn b2AssertHandler( condition, fileName, lineNumber );\n}\n#endif\n\nstatic void b2DefaultLogFcn( const char* message )\n{\n\tprintf( \"Box2D: %s\\n\", message );\n}\n\nb2LogFcn* b2LogHandler = b2DefaultLogFcn;\n\nvoid b2SetLogFcn( b2LogFcn* logFcn )\n{\n\tB2_ASSERT( logFcn != NULL );\n\tb2LogHandler = logFcn;\n}\n\nvoid b2Log( const char* format, ... )\n{\n\tva_list args;\n\tva_start( args, format );\n\tchar buffer[512];\n\tvsnprintf( buffer, sizeof( buffer ), format, args );\n\tb2LogHandler( buffer );\n\tva_end( args );\n}\n\nb2Version b2GetVersion( void )\n{\n\treturn (b2Version){\n\t\t.major = 3,\n\t\t.minor = 2,\n\t\t.revision = 0,\n\t};\n}\n\nstatic b2AllocFcn* b2_allocFcn = NULL;\nstatic b2FreeFcn* b2_freeFcn = NULL;\n\nstatic b2AtomicInt b2_byteCount;\n\nvoid b2SetAllocator( b2AllocFcn* allocFcn, b2FreeFcn* freeFcn )\n{\n\tb2_allocFcn = allocFcn;\n\tb2_freeFcn = freeFcn;\n}\n\n// Use 32 byte alignment for everything. Works with 256bit SIMD.\n#define B2_ALIGNMENT 32\n\nvoid* b2Alloc( int size )\n{\n\tif ( size == 0 )\n\t{\n\t\treturn NULL;\n\t}\n\n\t// This could cause some sharing issues, however Box2D rarely calls b2Alloc.\n\tb2AtomicFetchAddInt( &b2_byteCount, size );\n\n\t// Allocation must be a multiple of 32 or risk a seg fault\n\t// https://en.cppreference.com/w/c/memory/aligned_alloc\n\tint size32 = ( ( size - 1 ) | 0x1F ) + 1;\n\n\tif ( b2_allocFcn != NULL )\n\t{\n\t\tvoid* ptr = b2_allocFcn( size32, B2_ALIGNMENT );\n\t\tb2TracyCAlloc( ptr, size );\n\n\t\tB2_ASSERT( ptr != NULL );\n\t\tB2_ASSERT( ( (uintptr_t)ptr & 0x1F ) == 0 );\n\n\t\treturn ptr;\n\t}\n\n#ifdef B2_PLATFORM_WINDOWS\n\tvoid* ptr = _aligned_malloc( size32, B2_ALIGNMENT );\n#elif defined( B2_PLATFORM_ANDROID )\n\tvoid* ptr = NULL;\n\tif ( posix_memalign( &ptr, B2_ALIGNMENT, size32 ) != 0 )\n\t{\n\t\t// allocation failed, exit the application\n\t\texit( EXIT_FAILURE );\n\t}\n#else\n\tvoid* ptr = aligned_alloc( B2_ALIGNMENT, size32 );\n#endif\n\n\tb2TracyCAlloc( ptr, size );\n\n\tB2_ASSERT( ptr != NULL );\n\tB2_ASSERT( ( (uintptr_t)ptr & 0x1F ) == 0 );\n\n\treturn ptr;\n}\n\nvoid b2Free( void* mem, int size )\n{\n\tif ( mem == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2TracyCFree( mem );\n\n\tif ( b2_freeFcn != NULL )\n\t{\n\t\tb2_freeFcn( mem, size );\n\t}\n\telse\n\t{\n#ifdef B2_PLATFORM_WINDOWS\n\t\t_aligned_free( mem );\n#else\n\t\tfree( mem );\n#endif\n\t}\n\n\tb2AtomicFetchAddInt( &b2_byteCount, -size );\n}\n\nvoid* b2GrowAlloc( void* oldMem, int oldSize, int newSize )\n{\n\tB2_ASSERT( newSize > oldSize );\n\tvoid* newMem = b2Alloc( newSize );\n\tif ( oldSize > 0 )\n\t{\n\t\tmemcpy( newMem, oldMem, oldSize );\n\t\tb2Free( oldMem, oldSize );\n\t}\n\treturn newMem;\n}\n\nint b2GetByteCount( void )\n{\n\treturn b2AtomicLoadInt( &b2_byteCount );\n}\n"
  },
  {
    "path": "src/core.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"box2d/base.h\"\n\n// clang-format off\n\n#define B2_NULL_INDEX ( -1 )\n\n// for performance comparisons\n#define B2_RESTRICT restrict\n\n#ifdef NDEBUG\n\t#define B2_DEBUG 0\n#else\n\t#define B2_DEBUG 1\n#endif\n\n#if defined( BOX2D_VALIDATE ) && !defined( NDEBUG )\n\t#define B2_VALIDATE 1\n#else\n\t#define B2_VALIDATE 0\n#endif\n\n// Define platform\n#if defined(_WIN32) || defined(_WIN64)\n\t#define B2_PLATFORM_WINDOWS\n#elif defined( __ANDROID__ )\n\t#define B2_PLATFORM_ANDROID\n#elif defined( __linux__ )\n\t#define B2_PLATFORM_LINUX\n#elif defined( __APPLE__ )\n\t#include <TargetConditionals.h>\n\t#if defined( TARGET_OS_IPHONE ) && !TARGET_OS_IPHONE\n\t\t#define B2_PLATFORM_MACOS\n\t#else\n\t\t#define B2_PLATFORM_IOS\n\t#endif\n#elif defined( __EMSCRIPTEN__ )\n\t#define B2_PLATFORM_WASM\n#else\n\t#define B2_PLATFORM_UNKNOWN\n#endif\n\n// Define CPU\n#if defined( __x86_64__ ) || defined( _M_X64 ) || defined( __i386__ ) || defined( _M_IX86 )\n\t#define B2_CPU_X86_X64\n#elif defined( __aarch64__ ) || defined( _M_ARM64 ) || defined( __arm__ ) || defined( _M_ARM )\n\t#define B2_CPU_ARM\n#elif defined( __EMSCRIPTEN__ )\n\t#define B2_CPU_WASM\n#else\n\t#define B2_CPU_UNKNOWN\n#endif\n\n// Define SIMD\n#if defined( BOX2D_DISABLE_SIMD )\n\t#define B2_SIMD_NONE\n\t// note: I tried width of 1 and got no performance change\n\t#define B2_SIMD_WIDTH 4\n#else\n\t#if defined( B2_CPU_X86_X64 )\n\t\t#if defined( BOX2D_AVX2 )\n\t\t\t#define B2_SIMD_AVX2\n\t\t\t#define B2_SIMD_WIDTH 8\n\t\t#else\n\t\t\t#define B2_SIMD_SSE2\n\t\t\t#define B2_SIMD_WIDTH 4\n\t\t#endif\n\t#elif defined( B2_CPU_ARM )\n\t\t#define B2_SIMD_NEON\n\t\t#define B2_SIMD_WIDTH 4\n\t#elif defined( B2_CPU_WASM )\n\t\t#define B2_CPU_WASM\n\t\t#define B2_SIMD_SSE2\n\t\t#define B2_SIMD_WIDTH 4\n\t#else\n\t\t#define B2_SIMD_NONE\n\t\t#define B2_SIMD_WIDTH 4\n\t#endif\n#endif\n\n// Define compiler\n#if defined( __clang__ )\n\t#define B2_COMPILER_CLANG\n#elif defined( __GNUC__ )\n\t#define B2_COMPILER_GCC\n#elif defined( _MSC_VER )\n\t#define B2_COMPILER_MSVC\n#endif\n\n/// Tracy profiler instrumentation\n/// https://github.com/wolfpld/tracy\n#ifdef BOX2D_PROFILE\n\t#include <tracy/TracyC.h>\n\t#define b2TracyCZoneC( ctx, color, active ) TracyCZoneC( ctx, color, active )\n\t#define b2TracyCZoneNC( ctx, name, color, active ) TracyCZoneNC( ctx, name, color, active )\n\t#define b2TracyCZoneEnd( ctx ) TracyCZoneEnd( ctx )\n\t#define b2TracyCFrame TracyCFrameMark\n#else\n\t#define b2TracyCZoneC( ctx, color, active )\n\t#define b2TracyCZoneNC( ctx, name, color, active )\n\t#define b2TracyCZoneEnd( ctx )\n\t#define b2TracyCFrame\n#endif\n\n// clang-format on\n\n// Returns the number of elements of an array\n#define B2_ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) )\n\n// Used to prevent the compiler from warning about unused variables\n#define B2_UNUSED( ... ) (void)sizeof( ( __VA_ARGS__, 0 ) )\n\n// Use to validate definitions. Do not take my cookie.\n#define B2_SECRET_COOKIE 1152023\n\n// Snoop counters. These should be disabled in optimized builds because they are expensive.\n#if defined( box2d_EXPORTS )\n#define B2_SNOOP_TABLE_COUNTERS B2_DEBUG\n#define B2_SNOOP_PAIR_COUNTERS B2_DEBUG\n#define B2_SNOOP_TOI_COUNTERS B2_DEBUG\n#else\n#define B2_SNOOP_TABLE_COUNTERS 0\n#define B2_SNOOP_PAIR_COUNTERS 0\n#define B2_SNOOP_TOI_COUNTERS 0\n#endif\n\n#define B2_CHECK_DEF( DEF ) B2_ASSERT( DEF->internalValue == B2_SECRET_COOKIE )\n\ntypedef struct b2AtomicInt\n{\n\tint value;\n} b2AtomicInt;\n\ntypedef struct b2AtomicU32\n{\n\tuint32_t value;\n} b2AtomicU32;\n\nvoid* b2Alloc( int size );\n#define B2_ALLOC_STRUCT( type ) b2Alloc(sizeof(type))\n#define B2_ALLOC_ARRAY( count, type ) b2Alloc(count * sizeof(type))\n\nvoid b2Free( void* mem, int size );\n#define B2_FREE_STRUCT( mem, type ) b2Free( mem, sizeof(type));\n#define B2_FREE_ARRAY( mem, count, type ) b2Free(mem, count * sizeof(type))\n\nvoid* b2GrowAlloc( void* oldMem, int oldSize, int newSize );\n\nvoid b2Log( const char* format, ... );\n\ntypedef struct b2Mutex b2Mutex;\n\nb2Mutex* b2CreateMutex( void );\nvoid b2DestroyMutex( b2Mutex* m );\nvoid b2LockMutex( b2Mutex* m );\nvoid b2UnlockMutex( b2Mutex* m );\n"
  },
  {
    "path": "src/ctz.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#if defined( _MSC_VER ) && !defined( __clang__ )\n\n#include <intrin.h>\n\n// https://en.wikipedia.org/wiki/Find_first_set\n\nstatic inline uint32_t b2CTZ32( uint32_t block )\n{\n\tunsigned long index;\n\t_BitScanForward( &index, block );\n\treturn index;\n}\n\n// This function doesn't need to be fast, so using the Ivy Bridge fallback.\nstatic inline uint32_t b2CLZ32( uint32_t value )\n{\n\t#if 1\n\n\t// Use BSR (Bit Scan Reverse) which is available on Ivy Bridge\n\tunsigned long index;\n\tif ( _BitScanReverse( &index, value ) )\n\t{\n\t\t// BSR gives the index of the most significant 1-bit\n\t\t// We need to invert this to get the number of leading zeros\n\t\treturn 31 - index;\n\t}\n\telse\n\t{\n\t\t// If x is 0, BSR sets the zero flag and doesn't modify index\n\t\t// LZCNT should return 32 for an input of 0\n\t\treturn 32;\n\t}\n\n\t#else\n\n\treturn __lzcnt( value );\n\n\t#endif\n}\n\nstatic inline uint32_t b2CTZ64( uint64_t block )\n{\n\tunsigned long index;\n\n\t#ifdef _WIN64\n\t_BitScanForward64( &index, block );\n\t#else\n\t// 32-bit fall back\n\tif ( (uint32_t)block != 0 )\n\t{\n\t\t_BitScanForward( &index, (uint32_t)block );\n\t}\n\telse\n\t{\n\t\t_BitScanForward( &index, (uint32_t)( block >> 32 ) );\n\t\tindex += 32;\n\t}\n\t#endif\n\n\treturn index;\n}\n\nstatic inline int b2PopCount64( uint64_t block )\n{\n\treturn (int)__popcnt64( block );\n}\n#else\n\nstatic inline uint32_t b2CTZ32( uint32_t block )\n{\n\treturn __builtin_ctz( block );\n}\n\nstatic inline uint32_t b2CLZ32( uint32_t value )\n{\n\treturn __builtin_clz( value );\n}\n\nstatic inline uint32_t b2CTZ64( uint64_t block )\n{\n\treturn __builtin_ctzll( block );\n}\n\nstatic inline int b2PopCount64( uint64_t block )\n{\n\treturn __builtin_popcountll( block );\n}\n#endif\n\nstatic inline bool b2IsPowerOf2( int x )\n{\n\treturn ( x & ( x - 1 ) ) == 0;\n}\n\nstatic inline int b2BoundingPowerOf2( int x )\n{\n\tif ( x <= 1 )\n\t{\n\t\treturn 1;\n\t}\n\n\treturn 32 - (int)b2CLZ32( (uint32_t)x - 1 );\n}\n\nstatic inline int b2RoundUpPowerOf2( int x )\n{\n\tif ( x <= 1 )\n\t{\n\t\treturn 1;\n\t}\n\n\treturn 1 << ( 32 - (int)b2CLZ32( (uint32_t)x - 1 ) );\n}\n"
  },
  {
    "path": "src/distance.c",
    "content": "\n// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"constants.h\"\n#include \"core.h\"\n\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n#include <stddef.h>\n\nb2Transform b2GetSweepTransform( const b2Sweep* sweep, float time )\n{\n\t// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/\n\tb2Transform xf;\n\txf.p = b2Add( b2MulSV( 1.0f - time, sweep->c1 ), b2MulSV( time, sweep->c2 ) );\n\n\tb2Rot q = {\n\t\t( 1.0f - time ) * sweep->q1.c + time * sweep->q2.c,\n\t\t( 1.0f - time ) * sweep->q1.s + time * sweep->q2.s,\n\t};\n\n\txf.q = b2NormalizeRot( q );\n\n\t// Shift to origin\n\txf.p = b2Sub( xf.p, b2RotateVector( xf.q, sweep->localCenter ) );\n\treturn xf;\n}\n\n/// Follows Ericson 5.1.9 Closest Points of Two Line Segments\nb2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 )\n{\n\tb2SegmentDistanceResult result = { 0 };\n\n\tb2Vec2 d1 = b2Sub( q1, p1 );\n\tb2Vec2 d2 = b2Sub( q2, p2 );\n\tb2Vec2 r = b2Sub( p1, p2 );\n\tfloat dd1 = b2Dot( d1, d1 );\n\tfloat dd2 = b2Dot( d2, d2 );\n\tfloat rd1 = b2Dot( r, d1 );\n\tfloat rd2 = b2Dot( r, d2 );\n\n\tconst float epsSqr = FLT_EPSILON * FLT_EPSILON;\n\n\tif ( dd1 < epsSqr || dd2 < epsSqr )\n\t{\n\t\t// Handle all degeneracies\n\t\tif ( dd1 >= epsSqr )\n\t\t{\n\t\t\t// Segment 2 is degenerate\n\t\t\tresult.fraction1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f );\n\t\t\tresult.fraction2 = 0.0f;\n\t\t}\n\t\telse if ( dd2 >= epsSqr )\n\t\t{\n\t\t\t// Segment 1 is degenerate\n\t\t\tresult.fraction1 = 0.0f;\n\t\t\tresult.fraction2 = b2ClampFloat( rd2 / dd2, 0.0f, 1.0f );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tresult.fraction1 = 0.0f;\n\t\t\tresult.fraction2 = 0.0f;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Non-degenerate segments\n\t\tfloat d12 = b2Dot( d1, d2 );\n\n\t\tfloat denominator = dd1 * dd2 - d12 * d12;\n\n\t\t// Fraction on segment 1\n\t\tfloat f1 = 0.0f;\n\t\tif ( denominator != 0.0f )\n\t\t{\n\t\t\t// not parallel\n\t\t\tf1 = b2ClampFloat( ( d12 * rd2 - rd1 * dd2 ) / denominator, 0.0f, 1.0f );\n\t\t}\n\n\t\t// Compute point on segment 2 closest to p1 + f1 * d1\n\t\tfloat f2 = ( d12 * f1 + rd2 ) / dd2;\n\n\t\t// Clamping of segment 2 requires a do over on segment 1\n\t\tif ( f2 < 0.0f )\n\t\t{\n\t\t\tf2 = 0.0f;\n\t\t\tf1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f );\n\t\t}\n\t\telse if ( f2 > 1.0f )\n\t\t{\n\t\t\tf2 = 1.0f;\n\t\t\tf1 = b2ClampFloat( ( d12 - rd1 ) / dd1, 0.0f, 1.0f );\n\t\t}\n\n\t\tresult.fraction1 = f1;\n\t\tresult.fraction2 = f2;\n\t}\n\n\tresult.closest1 = b2MulAdd( p1, result.fraction1, d1 );\n\tresult.closest2 = b2MulAdd( p2, result.fraction2, d2 );\n\tresult.distanceSquared = b2DistanceSquared( result.closest1, result.closest2 );\n\treturn result;\n}\n\nb2ShapeProxy b2MakeProxy( const b2Vec2* points, int count, float radius )\n{\n\tcount = b2MinInt( count, B2_MAX_POLYGON_VERTICES );\n\tb2ShapeProxy proxy;\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tproxy.points[i] = points[i];\n\t}\n\tproxy.count = count;\n\tproxy.radius = radius;\n\treturn proxy;\n}\n\nb2ShapeProxy b2MakeOffsetProxy( const b2Vec2* points, int count, float radius, b2Vec2 position, b2Rot rotation )\n{\n\tcount = b2MinInt( count, B2_MAX_POLYGON_VERTICES );\n\tb2Transform transform = {\n\t\t.p = position,\n\t\t.q = rotation,\n\t};\n\tb2ShapeProxy proxy;\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tproxy.points[i] = b2TransformPoint( transform, points[i] );\n\t}\n\tproxy.count = count;\n\tproxy.radius = radius;\n\treturn proxy;\n}\n\nstatic inline b2Vec2 b2Weight2( float a1, b2Vec2 w1, float a2, b2Vec2 w2 )\n{\n\treturn (b2Vec2){ a1 * w1.x + a2 * w2.x, a1 * w1.y + a2 * w2.y };\n}\n\nstatic inline b2Vec2 b2Weight3( float a1, b2Vec2 w1, float a2, b2Vec2 w2, float a3, b2Vec2 w3 )\n{\n\treturn (b2Vec2){ a1 * w1.x + a2 * w2.x + a3 * w3.x, a1 * w1.y + a2 * w2.y + a3 * w3.y };\n}\n\nstatic inline int b2FindSupport( const b2ShapeProxy* proxy, b2Vec2 direction )\n{\n\tconst b2Vec2* points = proxy->points;\n\tint count = proxy->count;\n\n\tint bestIndex = 0;\n\tfloat bestValue = b2Dot( points[0], direction );\n\tfor ( int i = 1; i < count; ++i )\n\t{\n\t\tfloat value = b2Dot( points[i], direction );\n\t\tif ( value > bestValue )\n\t\t{\n\t\t\tbestIndex = i;\n\t\t\tbestValue = value;\n\t\t}\n\t}\n\n\treturn bestIndex;\n}\n\nstatic b2Simplex b2MakeSimplexFromCache( const b2SimplexCache* cache, const b2ShapeProxy* proxyA, const b2ShapeProxy* proxyB )\n{\n\tB2_ASSERT( cache->count <= 3 );\n\tb2Simplex s;\n\n\t// Copy data from cache.\n\ts.count = cache->count;\n\n\tb2SimplexVertex* vertices[] = { &s.v1, &s.v2, &s.v3 };\n\tfor ( int i = 0; i < s.count; ++i )\n\t{\n\t\tb2SimplexVertex* v = vertices[i];\n\t\tv->indexA = cache->indexA[i];\n\t\tv->indexB = cache->indexB[i];\n\t\tv->wA = proxyA->points[v->indexA];\n\t\tv->wB = proxyB->points[v->indexB];\n\t\tv->w = b2Sub( v->wA, v->wB );\n\n\t\t// invalid\n\t\tv->a = -1.0f;\n\t}\n\n\t// If the cache is empty or invalid ...\n\tif ( s.count == 0 )\n\t{\n\t\tb2SimplexVertex* v = vertices[0];\n\t\tv->indexA = 0;\n\t\tv->indexB = 0;\n\t\tv->wA = proxyA->points[0];\n\t\tv->wB = proxyB->points[0];\n\t\tv->w = b2Sub( v->wA, v->wB );\n\t\tv->a = 1.0f;\n\t\ts.count = 1;\n\t}\n\n\treturn s;\n}\n\nstatic void b2MakeSimplexCache( b2SimplexCache* cache, const b2Simplex* simplex )\n{\n\tcache->count = (uint16_t)simplex->count;\n\tconst b2SimplexVertex* vertices[] = { &simplex->v1, &simplex->v2, &simplex->v3 };\n\tfor ( int i = 0; i < simplex->count; ++i )\n\t{\n\t\tcache->indexA[i] = (uint8_t)vertices[i]->indexA;\n\t\tcache->indexB[i] = (uint8_t)vertices[i]->indexB;\n\t}\n}\n\nstatic void b2ComputeWitnessPoints( const b2Simplex* s, b2Vec2* a, b2Vec2* b )\n{\n\tswitch ( s->count )\n\t{\n\t\tcase 1:\n\t\t\t*a = s->v1.wA;\n\t\t\t*b = s->v1.wB;\n\t\t\tbreak;\n\n\t\tcase 2:\n\t\t\t*a = b2Weight2( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA );\n\t\t\t*b = b2Weight2( s->v1.a, s->v1.wB, s->v2.a, s->v2.wB );\n\t\t\tbreak;\n\n\t\tcase 3:\n\t\t\t*a = b2Weight3( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA, s->v3.a, s->v3.wA );\n\t\t\t// todo why are these not equal?\n\t\t\t//*b = b2Weight3(s->v1.a, s->v1.wB, s->v2.a, s->v2.wB, s->v3.a, s->v3.wB);\n\t\t\t*b = *a;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\t*a = b2Vec2_zero;\n\t\t\t*b = b2Vec2_zero;\n\t\t\tB2_ASSERT( false );\n\t\t\tbreak;\n\t}\n}\n\n// Solve a line segment using barycentric coordinates.\n//\n// p = a1 * w1 + a2 * w2\n// a1 + a2 = 1\n//\n// The vector from the origin to the closest point on the line is\n// perpendicular to the line.\n// e12 = w2 - w1\n// dot(p, e) = 0\n// a1 * dot(w1, e) + a2 * dot(w2, e) = 0\n//\n// 2-by-2 linear system\n// [1      1     ][a1] = [1]\n// [w1.e12 w2.e12][a2] = [0]\n//\n// Define\n// d12_1 =  dot(w2, e12)\n// d12_2 = -dot(w1, e12)\n// d12 = d12_1 + d12_2\n//\n// Solution\n// a1 = d12_1 / d12\n// a2 = d12_2 / d12\n//\n// returns a vector that points towards the origin\nstatic b2Vec2 b2SolveSimplex2( b2Simplex* s )\n{\n\tb2Vec2 w1 = s->v1.w;\n\tb2Vec2 w2 = s->v2.w;\n\tb2Vec2 e12 = b2Sub( w2, w1 );\n\n\t// w1 region\n\tfloat d12_2 = -b2Dot( w1, e12 );\n\tif ( d12_2 <= 0.0f )\n\t{\n\t\t// a2 <= 0, so we clamp it to 0\n\t\ts->v1.a = 1.0f;\n\t\ts->count = 1;\n\t\treturn b2Neg( w1 );\n\t}\n\n\t// w2 region\n\tfloat d12_1 = b2Dot( w2, e12 );\n\tif ( d12_1 <= 0.0f )\n\t{\n\t\t// a1 <= 0, so we clamp it to 0\n\t\ts->v2.a = 1.0f;\n\t\ts->count = 1;\n\t\ts->v1 = s->v2;\n\t\treturn b2Neg( w2 );\n\t}\n\n\t// Must be in e12 region.\n\tfloat inv_d12 = 1.0f / ( d12_1 + d12_2 );\n\ts->v1.a = d12_1 * inv_d12;\n\ts->v2.a = d12_2 * inv_d12;\n\ts->count = 2;\n\treturn b2CrossSV( b2Cross( b2Add( w1, w2 ), e12 ), e12 );\n}\n\nstatic b2Vec2 b2SolveSimplex3( b2Simplex* s )\n{\n\tb2Vec2 w1 = s->v1.w;\n\tb2Vec2 w2 = s->v2.w;\n\tb2Vec2 w3 = s->v3.w;\n\n\t// Edge12\n\t// [1      1     ][a1] = [1]\n\t// [w1.e12 w2.e12][a2] = [0]\n\t// a3 = 0\n\tb2Vec2 e12 = b2Sub( w2, w1 );\n\tfloat w1e12 = b2Dot( w1, e12 );\n\tfloat w2e12 = b2Dot( w2, e12 );\n\tfloat d12_1 = w2e12;\n\tfloat d12_2 = -w1e12;\n\n\t// Edge13\n\t// [1      1     ][a1] = [1]\n\t// [w1.e13 w3.e13][a3] = [0]\n\t// a2 = 0\n\tb2Vec2 e13 = b2Sub( w3, w1 );\n\tfloat w1e13 = b2Dot( w1, e13 );\n\tfloat w3e13 = b2Dot( w3, e13 );\n\tfloat d13_1 = w3e13;\n\tfloat d13_2 = -w1e13;\n\n\t// Edge23\n\t// [1      1     ][a2] = [1]\n\t// [w2.e23 w3.e23][a3] = [0]\n\t// a1 = 0\n\tb2Vec2 e23 = b2Sub( w3, w2 );\n\tfloat w2e23 = b2Dot( w2, e23 );\n\tfloat w3e23 = b2Dot( w3, e23 );\n\tfloat d23_1 = w3e23;\n\tfloat d23_2 = -w2e23;\n\n\t// Triangle123\n\tfloat n123 = b2Cross( e12, e13 );\n\n\tfloat d123_1 = n123 * b2Cross( w2, w3 );\n\tfloat d123_2 = n123 * b2Cross( w3, w1 );\n\tfloat d123_3 = n123 * b2Cross( w1, w2 );\n\n\t// w1 region\n\tif ( d12_2 <= 0.0f && d13_2 <= 0.0f )\n\t{\n\t\ts->v1.a = 1.0f;\n\t\ts->count = 1;\n\t\treturn b2Neg( w1 );\n\t}\n\n\t// e12\n\tif ( d12_1 > 0.0f && d12_2 > 0.0f && d123_3 <= 0.0f )\n\t{\n\t\tfloat inv_d12 = 1.0f / ( d12_1 + d12_2 );\n\t\ts->v1.a = d12_1 * inv_d12;\n\t\ts->v2.a = d12_2 * inv_d12;\n\t\ts->count = 2;\n\t\treturn b2CrossSV( b2Cross( b2Add( w1, w2 ), e12 ), e12 );\n\t}\n\n\t// e13\n\tif ( d13_1 > 0.0f && d13_2 > 0.0f && d123_2 <= 0.0f )\n\t{\n\t\tfloat inv_d13 = 1.0f / ( d13_1 + d13_2 );\n\t\ts->v1.a = d13_1 * inv_d13;\n\t\ts->v3.a = d13_2 * inv_d13;\n\t\ts->count = 2;\n\t\ts->v2 = s->v3;\n\t\treturn b2CrossSV( b2Cross( b2Add( w1, w3 ), e13 ), e13 );\n\t}\n\n\t// w2 region\n\tif ( d12_1 <= 0.0f && d23_2 <= 0.0f )\n\t{\n\t\ts->v2.a = 1.0f;\n\t\ts->count = 1;\n\t\ts->v1 = s->v2;\n\t\treturn b2Neg( w2 );\n\t}\n\n\t// w3 region\n\tif ( d13_1 <= 0.0f && d23_1 <= 0.0f )\n\t{\n\t\ts->v3.a = 1.0f;\n\t\ts->count = 1;\n\t\ts->v1 = s->v3;\n\t\treturn b2Neg( w3 );\n\t}\n\n\t// e23\n\tif ( d23_1 > 0.0f && d23_2 > 0.0f && d123_1 <= 0.0f )\n\t{\n\t\tfloat inv_d23 = 1.0f / ( d23_1 + d23_2 );\n\t\ts->v2.a = d23_1 * inv_d23;\n\t\ts->v3.a = d23_2 * inv_d23;\n\t\ts->count = 2;\n\t\ts->v1 = s->v3;\n\t\treturn b2CrossSV( b2Cross( b2Add( w2, w3 ), e23 ), e23 );\n\t}\n\n\t// Must be in triangle123\n\tfloat inv_d123 = 1.0f / ( d123_1 + d123_2 + d123_3 );\n\ts->v1.a = d123_1 * inv_d123;\n\ts->v2.a = d123_2 * inv_d123;\n\ts->v3.a = d123_3 * inv_d123;\n\ts->count = 3;\n\n\t// No search direction\n\treturn b2Vec2_zero;\n}\n\n// Uses GJK for computing the distance between convex shapes.\n// https://box2d.org/files/ErinCatto_GJK_GDC2010.pdf\n// I spent time optimizing this and could find no further significant gains 3/30/2025\nb2DistanceOutput b2ShapeDistance( const b2DistanceInput* input, b2SimplexCache* cache, b2Simplex* simplexes, int simplexCapacity )\n{\n\tB2_UNUSED( simplexes, simplexCapacity );\n\tB2_ASSERT( input->proxyA.count > 0 && input->proxyB.count > 0 );\n\tB2_ASSERT( input->proxyA.radius >= 0.0f );\n\tB2_ASSERT( input->proxyB.radius >= 0.0f );\n\n\tb2DistanceOutput output = { 0 };\n\n\tconst b2ShapeProxy* proxyA = &input->proxyA;\n\n\t// Get proxyB in frame A to avoid further transforms in the main loop.\n\t// This is still a performance gain at 8 points.\n\tb2ShapeProxy localProxyB;\n\t{\n\t\tb2Transform transform = b2InvMulTransforms( input->transformA, input->transformB );\n\t\tlocalProxyB.count = input->proxyB.count;\n\t\tlocalProxyB.radius = input->proxyB.radius;\n\t\tfor ( int i = 0; i < localProxyB.count; ++i )\n\t\t{\n\t\t\tlocalProxyB.points[i] = b2TransformPoint( transform, input->proxyB.points[i] );\n\t\t}\n\t}\n\n\t// Initialize the simplex.\n\tb2Simplex simplex = b2MakeSimplexFromCache( cache, proxyA, &localProxyB );\n\n\tint simplexIndex = 0;\n\tif ( simplexes != NULL && simplexIndex < simplexCapacity )\n\t{\n\t\tsimplexes[simplexIndex] = simplex;\n\t\tsimplexIndex += 1;\n\t}\n\n\t// Get simplex vertices as an array.\n\tb2SimplexVertex* vertices[] = { &simplex.v1, &simplex.v2, &simplex.v3 };\n\n\tb2Vec2 nonUnitNormal = b2Vec2_zero;\n\n\t// These store the vertices of the last simplex so that we can check for duplicates and prevent cycling.\n\tint saveA[3], saveB[3];\n\n\t// Main iteration loop. All computations are done in frame A.\n\tconst int maxIterations = 20;\n\tint iteration = 0;\n\twhile ( iteration < maxIterations )\n\t{\n\t\t// Copy simplex so we can identify duplicates.\n\t\tint saveCount = simplex.count;\n\t\tfor ( int i = 0; i < saveCount; ++i )\n\t\t{\n\t\t\tsaveA[i] = vertices[i]->indexA;\n\t\t\tsaveB[i] = vertices[i]->indexB;\n\t\t}\n\n\t\tb2Vec2 d = { 0 };\n\t\tswitch ( simplex.count )\n\t\t{\n\t\t\tcase 1:\n\t\t\t\td = b2Neg( simplex.v1.w );\n\t\t\t\tbreak;\n\n\t\t\tcase 2:\n\t\t\t\td = b2SolveSimplex2( &simplex );\n\t\t\t\tbreak;\n\n\t\t\tcase 3:\n\t\t\t\td = b2SolveSimplex3( &simplex );\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tB2_ASSERT( false );\n\t\t}\n\n\t\t// If we have 3 points, then the origin is in the corresponding triangle.\n\t\tif ( simplex.count == 3 )\n\t\t{\n\t\t\t// Overlap\n\t\t\tb2Vec2 localPointA, localPointB;\n\t\t\tb2ComputeWitnessPoints( &simplex, &localPointA, &localPointB );\n\t\t\toutput.pointA = b2TransformPoint( input->transformA, localPointA );\n\t\t\toutput.pointB = b2TransformPoint( input->transformA, localPointB );\n\t\t\treturn output;\n\t\t}\n\n#ifndef NDEBUG\n\t\tif ( simplexes != NULL && simplexIndex < simplexCapacity )\n\t\t{\n\t\t\tsimplexes[simplexIndex] = simplex;\n\t\t\tsimplexIndex += 1;\n\t\t}\n#endif\n\n\t\t// Ensure the search direction is numerically fit.\n\t\tif ( b2Dot( d, d ) < FLT_EPSILON * FLT_EPSILON )\n\t\t{\n\t\t\t// This is unlikely but could lead to bad cycling.\n\t\t\t// The branch predictor seems to make this check have low cost.\n\n\t\t\t// The origin is probably contained by a line segment\n\t\t\t// or triangle. Thus the shapes are overlapped.\n\n\t\t\t// Must return overlap due to invalid normal.\n\t\t\tb2Vec2 localPointA, localPointB;\n\t\t\tb2ComputeWitnessPoints( &simplex, &localPointA, &localPointB );\n\t\t\toutput.pointA = b2TransformPoint( input->transformA, localPointA );\n\t\t\toutput.pointB = b2TransformPoint( input->transformA, localPointB );\n\t\t\treturn output;\n\t\t}\n\n\t\t// Save the normal\n\t\tnonUnitNormal = d;\n\n\t\t// Compute a tentative new simplex vertex using support points.\n\t\t// support = support(a, d) - support(b, -d)\n\t\tb2SimplexVertex* vertex = vertices[simplex.count];\n\t\tvertex->indexA = b2FindSupport( proxyA, d );\n\t\tvertex->wA = proxyA->points[vertex->indexA];\n\t\tvertex->indexB = b2FindSupport( &localProxyB, b2Neg( d ) );\n\t\tvertex->wB = localProxyB.points[vertex->indexB];\n\t\tvertex->w = b2Sub( vertex->wA, vertex->wB );\n\n\t\t// Iteration count is equated to the number of support point calls.\n\t\t++iteration;\n\n\t\t// Check for duplicate support points. This is the main termination criteria.\n\t\tbool duplicate = false;\n\t\tfor ( int i = 0; i < saveCount; ++i )\n\t\t{\n\t\t\tif ( vertex->indexA == saveA[i] && vertex->indexB == saveB[i] )\n\t\t\t{\n\t\t\t\tduplicate = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// If we found a duplicate support point we must exit to avoid cycling.\n\t\tif ( duplicate )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t// New vertex is valid and needed.\n\t\tsimplex.count += 1;\n\t}\n\n#ifndef NDEBUG\n\tif ( simplexes != NULL && simplexIndex < simplexCapacity )\n\t{\n\t\tsimplexes[simplexIndex] = simplex;\n\t\tsimplexIndex += 1;\n\t}\n#endif\n\n\t// Prepare output\n\tb2Vec2 normal = b2Normalize( nonUnitNormal );\n\tB2_ASSERT( b2IsNormalized( normal ) );\n\tnormal = b2RotateVector( input->transformA.q, normal );\n\n\tb2Vec2 localPointA, localPointB;\n\tb2ComputeWitnessPoints( &simplex, &localPointA, &localPointB );\n\toutput.normal = normal;\n\toutput.distance = b2Distance( localPointA, localPointB );\n\toutput.pointA = b2TransformPoint( input->transformA, localPointA );\n\toutput.pointB = b2TransformPoint( input->transformA, localPointB );\n\toutput.iterations = iteration;\n\toutput.simplexCount = simplexIndex;\n\n\t// Cache the simplex\n\tb2MakeSimplexCache( cache, &simplex );\n\n\t// Apply radii if requested\n\tif ( input->useRadii )\n\t{\n\t\tfloat radiusA = input->proxyA.radius;\n\t\tfloat radiusB = input->proxyB.radius;\n\t\toutput.distance = b2MaxFloat( 0.0f, output.distance - radiusA - radiusB );\n\n\t\t// Keep closest points on perimeter even if overlapped, this way the points move smoothly.\n\t\toutput.pointA = b2MulAdd( output.pointA, radiusA, normal );\n\t\toutput.pointB = b2MulSub( output.pointB, radiusB, normal );\n\t}\n\n\treturn output;\n}\n\n// Shape cast using conservative advancement\nb2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input )\n{\n\t// Compute tolerance\n\tfloat linearSlop = B2_LINEAR_SLOP;\n\tfloat totalRadius = input->proxyA.radius + input->proxyB.radius;\n\tfloat target = b2MaxFloat( linearSlop, totalRadius - linearSlop );\n\tfloat tolerance = 0.25f * linearSlop;\n\n\tB2_ASSERT( target > tolerance );\n\n\t// Prepare input for distance query\n\tb2SimplexCache cache = { 0 };\n\n\tfloat fraction = 0.0f;\n\n\tb2DistanceInput distanceInput = { 0 };\n\tdistanceInput.proxyA = input->proxyA;\n\tdistanceInput.proxyB = input->proxyB;\n\tdistanceInput.transformA = input->transformA;\n\tdistanceInput.transformB = input->transformB;\n\tdistanceInput.useRadii = false;\n\n\tb2Vec2 delta2 = input->translationB;\n\tb2CastOutput output = { 0 };\n\n\tint iteration = 0;\n\tconst int maxIterations = 20;\n\n\tfor ( ; iteration < maxIterations; ++iteration )\n\t{\n\t\toutput.iterations += 1;\n\n\t\tb2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );\n\n\t\tif ( distanceOutput.distance < target + tolerance )\n\t\t{\n\t\t\tif ( iteration == 0 )\n\t\t\t{\n\t\t\t\tif ( input->canEncroach && distanceOutput.distance > 2.0f * linearSlop )\n\t\t\t\t{\n\t\t\t\t\ttarget = distanceOutput.distance - linearSlop;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Initial overlap\n\t\t\t\t\toutput.hit = true;\n\n\t\t\t\t\t// Compute a common point\n\t\t\t\t\tb2Vec2 c1 = b2MulAdd( distanceOutput.pointA, input->proxyA.radius, distanceOutput.normal );\n\t\t\t\t\tb2Vec2 c2 = b2MulAdd( distanceOutput.pointB, -input->proxyB.radius, distanceOutput.normal );\n\t\t\t\t\toutput.point = b2Lerp( c1, c2, 0.5f );\n\t\t\t\t\treturn output;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Regular hit\n\t\t\t\tB2_ASSERT( distanceOutput.distance > 0.0f && b2IsNormalized( distanceOutput.normal ) );\n\t\t\t\toutput.fraction = fraction;\n\t\t\t\toutput.point = b2MulAdd( distanceOutput.pointA, input->proxyA.radius, distanceOutput.normal );\n\t\t\t\toutput.normal = distanceOutput.normal;\n\t\t\t\toutput.hit = true;\n\t\t\t\treturn output;\n\t\t\t}\n\t\t}\n\n\t\tB2_ASSERT( distanceOutput.distance > 0.0f );\n\t\tB2_ASSERT( b2IsNormalized( distanceOutput.normal ) );\n\n\t\t// Check if shapes are approaching each other\n\t\tfloat denominator = b2Dot( delta2, distanceOutput.normal );\n\t\tif ( denominator >= 0.0f )\n\t\t{\n\t\t\t// Miss\n\t\t\treturn output;\n\t\t}\n\n\t\t// Advance sweep\n\t\tfraction += ( target - distanceOutput.distance ) / denominator;\n\t\tif ( fraction >= input->maxFraction )\n\t\t{\n\t\t\t// Miss\n\t\t\treturn output;\n\t\t}\n\n\t\tdistanceInput.transformB.p = b2MulAdd( input->transformB.p, fraction, delta2 );\n\t}\n\n\t// Failure!\n\treturn output;\n}\n\n#if 0\nstatic inline b2Vec2 b2ComputeSimplexClosestPoint( const b2Simplex* s )\n{\n\tif ( s->count == 1 )\n\t{\n\t\treturn s->v1.w;\n\t}\n\n\tif ( s->count == 2 )\n\t{\n\t\treturn b2Weight2( s->v1.a, s->v1.w, s->v2.a, s->v2.w );\n\t}\n\n\treturn b2Vec2_zero;\n}\n\ntypedef struct b2ShapeCastData\n{\n\tb2Simplex simplex;\n\tb2Vec2 closestA, closestB;\n\tb2Vec2 normal;\n\tb2Vec2 p0;\n\tfloat fraction;\n} b2ShapeCastData;\n\n// GJK-raycast\n// Algorithm by Gino van den Bergen.\n// \"Smooth Mesh Contacts with GJK\" in Game Physics Pearls. 2010\n// This needs the simplex of A - B because the translation is for B and this\n// is how the relative motion works out when both shapes are translating.\n// This is similar to ray vs polygon and involves plane clipping. See b2RayCastPolygon.\n// In this case the polygon is just points and there are no planes. This uses a modified\n// version of GJK to generate planes for clipping.\n// The algorithm works by incrementally building clipping planes using GJK. Once a valid\n// clip plane is found the simplex origin is moved to the current fraction on the ray.\n// This resets the simplex after every clip. Later I should compare performance.\n// However, adapting this to work with encroachment is tricky and confusing because encroachment\n// needs distance.\n// Note: this algorithm is difficult to debug and not worth the effort in my opinion 4/1/2025\nb2CastOutput b2ShapeCastMerged( const b2ShapeCastPairInput* input, b2ShapeCastData* debugData, int debugCapacity )\n{\n\tB2_UNUSED( debugData, debugCapacity );\n\n\tb2CastOutput output = { 0 };\n\toutput.fraction = input->maxFraction;\n\n\tb2ShapeProxy proxyA = input->proxyA;\n\n\tb2Transform xf = b2InvMulTransforms( input->transformA, input->transformB );\n\n\t// Put proxyB in proxyA's frame to reduce round-off error\n\tb2ShapeProxy proxyB;\n\tproxyB.count = input->proxyB.count;\n\tproxyB.radius = input->proxyB.radius;\n\tB2_ASSERT( proxyB.count <= B2_MAX_POLYGON_VERTICES );\n\n\tfor ( int i = 0; i < proxyB.count; ++i )\n\t{\n\t\tproxyB.points[i] = b2TransformPoint( xf, input->proxyB.points[i] );\n\t}\n\n\tfloat radius = proxyA.radius + proxyB.radius;\n\n\tb2Vec2 r = b2RotateVector( xf.q, input->translationB );\n\tfloat lambda = 0.0f;\n\tfloat maxFraction = input->maxFraction;\n\n\t// Initial simplex\n\tb2Simplex simplex;\n\tsimplex.count = 0;\n\n\t// Get simplex vertices as an array.\n\tb2SimplexVertex* vertices[] = { &simplex.v1, &simplex.v2, &simplex.v3 };\n\n\t// Get an initial point in A - B\n\tb2Vec2 wA = proxyA.points[0];\n\tb2Vec2 wB = proxyB.points[0];\n\tb2Vec2 v = b2Sub( wA, wB );\n\tb2Vec2 d = b2Neg( v );\n\n\t// Sigma is the target distance between proxies\n\tconst float linearSlop = B2_LINEAR_SLOP;\n\tconst float sigma = b2MaxFloat( linearSlop, radius - linearSlop );\n\tfloat tolerance = 0.5f * linearSlop;\n\tfloat stolSquared = ( sigma + tolerance ) * ( sigma + tolerance );\n\n\t// Main iteration loop.\n\tconst int maxIterations = 20;\n\tint iteration = 0;\n\twhile ( iteration < maxIterations && b2LengthSquared( v ) > stolSquared )\n\t{\n\t\tB2_ASSERT( simplex.count < 3 );\n\n\t\t// Support in direction d (A - B)\n\t\tint indexA = b2FindSupport( &proxyA, d );\n\t\twA = proxyA.points[indexA];\n\t\tint indexB = b2FindSupport( &proxyB, b2Neg( d ) );\n\t\twB = proxyB.points[indexB];\n\t\tb2Vec2 p0 = b2Sub( wA, wB );\n\n\t\t// d is a normal at p, normalize to work with sigma\n\t\tb2Vec2 normal = b2Normalize( d );\n\n\t\t// Intersect ray with plane\n\t\t// p = origin + t * r\n\t\t// dot(n, p - p0) = sigma\n\t\t// dot(n, origin - p0) + t * dot(n, r) = sigma\n\t\t// t = ( dot(n, p0) + sigma) / dot(n, r)\n\t\t// if t < (dot(n, p0) + sigma) / dot(n, r) then t can be increased\n\t\t// or (flipping sign because dot(n,r) < 0)\n\t\t// dot(n, p0) + sigma < t * dot(n, r) && dot(n, r) < 0\n\t\tfloat np0 = b2Dot( normal, p0 );\n\t\tfloat nr = b2Dot( normal, r );\n\t\tif ( np0 + sigma < lambda * nr )\n\t\t{\n\t\t\tif ( nr >= 0.0f )\n\t\t\t{\n\t\t\t\t// miss\n\t\t\t\treturn output;\n\t\t\t}\n\n\t\t\tlambda = ( np0 + sigma ) / nr;\n\t\t\tif ( lambda > maxFraction )\n\t\t\t{\n\t\t\t\t// too far\n\t\t\t\treturn output;\n\t\t\t}\n\n\t\t\t// reset the simplex\n\t\t\tsimplex.count = 0;\n\t\t}\n\n\t\t// Shift by lambda * r because we want the closest point to the current clip point.\n\t\t// Note that the support point p is not shifted because we want the plane equation\n\t\t// to be formed in un-shifted space.\n\t\tb2SimplexVertex* vertex = vertices[simplex.count];\n\t\tvertex->indexA = indexB;\n\t\tvertex->wA = wA;\n\t\tvertex->indexB = indexA;\n\t\tvertex->wB = (b2Vec2){ wB.x + lambda * r.x, wB.y + lambda * r.y };\n\t\tvertex->w = b2Sub( vertex->wA, vertex->wB );\n\t\tvertex->a = 1.0f;\n\t\tsimplex.count += 1;\n\n\t\tswitch ( simplex.count )\n\t\t{\n\t\t\tcase 1:\n\t\t\t\td = b2Neg( simplex.v1.w );\n\t\t\t\tbreak;\n\n\t\t\tcase 2:\n\t\t\t\td = b2SolveSimplex2( &simplex );\n\t\t\t\tbreak;\n\n\t\t\tcase 3:\n\t\t\t\td = b2SolveSimplex3( &simplex );\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tB2_ASSERT( false );\n\t\t}\n\n#ifndef NDEBUG\n\t\tif ( debugData != NULL && output.iterations < debugCapacity )\n\t\t{\n\t\t\tdebugData[output.iterations].simplex = simplex;\n\t\t\tdebugData[output.iterations].normal = normal;\n\t\t\tdebugData[output.iterations].p0 = p0;\n\t\t\tb2Vec2 cA, cB;\n\t\t\tb2ComputeSimplexWitnessPoints( &cA, &cB, &simplex );\n\t\t\tdebugData[output.iterations].closestA = cA;\n\t\t\tdebugData[output.iterations].closestB = cB;\n\t\t\tdebugData[output.iterations].fraction = lambda;\n\t\t}\n#endif\n\n\t\toutput.iterations += 1;\n\n\t\t// If we have 3 points, then the origin is in the corresponding triangle.\n\t\tif ( simplex.count == 3 )\n\t\t{\n\t\t\t// Overlap\n\t\t\treturn output;\n\t\t}\n\n\t\t// Get distance vector\n\t\tv = b2ComputeSimplexClosestPoint( &simplex );\n\n\t\t// Iteration count is equated to the number of support point calls.\n\t\t++iteration;\n\t}\n\n\tif ( iteration == 0 || lambda == 0.0f )\n\t{\n\t\t// Initial overlap\n\t\treturn output;\n\t}\n\n\t// Prepare output.\n\tb2Vec2 pointA, pointB;\n\tb2ComputeSimplexWitnessPoints( &pointB, &pointA, &simplex );\n\n\tb2Vec2 n = b2Normalize( b2Neg( v ) );\n\tb2Vec2 point = { pointA.x + proxyA.radius * n.x, pointA.y + proxyA.radius * n.y };\n\n\toutput.point = b2TransformPoint( input->transformA, point );\n\toutput.normal = b2RotateVector( input->transformA.q, n );\n\toutput.fraction = lambda;\n\toutput.iterations = iteration;\n\toutput.hit = true;\n\treturn output;\n}\n#endif\n\n// Warning: writing to these globals significantly slows multithreading performance\n#if B2_SNOOP_TOI_COUNTERS\nfloat b2_toiTime, b2_toiMaxTime;\nint b2_toiCalls, b2_toiDistanceIterations, b2_toiMaxDistanceIterations;\nint b2_toiRootIterations, b2_toiMaxRootIterations;\nint b2_toiFailedCount;\nint b2_toiOverlappedCount;\nint b2_toiHitCount;\nint b2_toiSeparatedCount;\n#endif\n\ntypedef enum b2SeparationType\n{\n\tb2_pointsType,\n\tb2_faceAType,\n\tb2_faceBType\n} b2SeparationType;\n\ntypedef struct b2SeparationFunction\n{\n\tconst b2ShapeProxy* proxyA;\n\tconst b2ShapeProxy* proxyB;\n\tb2Sweep sweepA, sweepB;\n\tb2Vec2 localPoint;\n\tb2Vec2 axis;\n\tb2SeparationType type;\n} b2SeparationFunction;\n\nstatic b2SeparationFunction b2MakeSeparationFunction( const b2SimplexCache* cache, const b2ShapeProxy* proxyA,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  const b2Sweep* sweepA, const b2ShapeProxy* proxyB, const b2Sweep* sweepB,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  float t1 )\n{\n\tb2SeparationFunction f;\n\n\tf.proxyA = proxyA;\n\tf.proxyB = proxyB;\n\tint count = cache->count;\n\tB2_ASSERT( 0 < count && count < 3 );\n\n\tf.sweepA = *sweepA;\n\tf.sweepB = *sweepB;\n\n\tb2Transform xfA = b2GetSweepTransform( sweepA, t1 );\n\tb2Transform xfB = b2GetSweepTransform( sweepB, t1 );\n\n\tif ( count == 1 )\n\t{\n\t\tf.type = b2_pointsType;\n\t\tb2Vec2 localPointA = proxyA->points[cache->indexA[0]];\n\t\tb2Vec2 localPointB = proxyB->points[cache->indexB[0]];\n\t\tb2Vec2 pointA = b2TransformPoint( xfA, localPointA );\n\t\tb2Vec2 pointB = b2TransformPoint( xfB, localPointB );\n\t\tf.axis = b2Normalize( b2Sub( pointB, pointA ) );\n\t\tf.localPoint = b2Vec2_zero;\n\t\treturn f;\n\t}\n\n\tif ( cache->indexA[0] == cache->indexA[1] )\n\t{\n\t\t// Two points on B and one on A.\n\t\tf.type = b2_faceBType;\n\t\tb2Vec2 localPointB1 = proxyB->points[cache->indexB[0]];\n\t\tb2Vec2 localPointB2 = proxyB->points[cache->indexB[1]];\n\n\t\tf.axis = b2CrossVS( b2Sub( localPointB2, localPointB1 ), 1.0f );\n\t\tf.axis = b2Normalize( f.axis );\n\t\tb2Vec2 normal = b2RotateVector( xfB.q, f.axis );\n\n\t\tf.localPoint = (b2Vec2){ 0.5f * ( localPointB1.x + localPointB2.x ), 0.5f * ( localPointB1.y + localPointB2.y ) };\n\t\tb2Vec2 pointB = b2TransformPoint( xfB, f.localPoint );\n\n\t\tb2Vec2 localPointA = proxyA->points[cache->indexA[0]];\n\t\tb2Vec2 pointA = b2TransformPoint( xfA, localPointA );\n\n\t\tfloat s = b2Dot( b2Sub( pointA, pointB ), normal );\n\t\tif ( s < 0.0f )\n\t\t{\n\t\t\tf.axis = b2Neg( f.axis );\n\t\t}\n\t\treturn f;\n\t}\n\n\t// Two points on A and one or two points on B.\n\tf.type = b2_faceAType;\n\tb2Vec2 localPointA1 = proxyA->points[cache->indexA[0]];\n\tb2Vec2 localPointA2 = proxyA->points[cache->indexA[1]];\n\n\tf.axis = b2CrossVS( b2Sub( localPointA2, localPointA1 ), 1.0f );\n\tf.axis = b2Normalize( f.axis );\n\tb2Vec2 normal = b2RotateVector( xfA.q, f.axis );\n\n\tf.localPoint = (b2Vec2){ 0.5f * ( localPointA1.x + localPointA2.x ), 0.5f * ( localPointA1.y + localPointA2.y ) };\n\tb2Vec2 pointA = b2TransformPoint( xfA, f.localPoint );\n\n\tb2Vec2 localPointB = proxyB->points[cache->indexB[0]];\n\tb2Vec2 pointB = b2TransformPoint( xfB, localPointB );\n\n\tfloat s = b2Dot( b2Sub( pointB, pointA ), normal );\n\tif ( s < 0.0f )\n\t{\n\t\tf.axis = b2Neg( f.axis );\n\t}\n\treturn f;\n}\n\nstatic float b2FindMinSeparation( const b2SeparationFunction* f, int* indexA, int* indexB, float t )\n{\n\tb2Transform xfA = b2GetSweepTransform( &f->sweepA, t );\n\tb2Transform xfB = b2GetSweepTransform( &f->sweepB, t );\n\n\tswitch ( f->type )\n\t{\n\t\tcase b2_pointsType:\n\t\t{\n\t\t\tb2Vec2 axisA = b2InvRotateVector( xfA.q, f->axis );\n\t\t\tb2Vec2 axisB = b2InvRotateVector( xfB.q, b2Neg( f->axis ) );\n\n\t\t\t*indexA = b2FindSupport( f->proxyA, axisA );\n\t\t\t*indexB = b2FindSupport( f->proxyB, axisB );\n\n\t\t\tb2Vec2 localPointA = f->proxyA->points[*indexA];\n\t\t\tb2Vec2 localPointB = f->proxyB->points[*indexB];\n\n\t\t\tb2Vec2 pointA = b2TransformPoint( xfA, localPointA );\n\t\t\tb2Vec2 pointB = b2TransformPoint( xfB, localPointB );\n\n\t\t\tfloat separation = b2Dot( b2Sub( pointB, pointA ), f->axis );\n\t\t\treturn separation;\n\t\t}\n\n\t\tcase b2_faceAType:\n\t\t{\n\t\t\tb2Vec2 normal = b2RotateVector( xfA.q, f->axis );\n\t\t\tb2Vec2 pointA = b2TransformPoint( xfA, f->localPoint );\n\n\t\t\tb2Vec2 axisB = b2InvRotateVector( xfB.q, b2Neg( normal ) );\n\n\t\t\t*indexA = -1;\n\t\t\t*indexB = b2FindSupport( f->proxyB, axisB );\n\n\t\t\tb2Vec2 localPointB = f->proxyB->points[*indexB];\n\t\t\tb2Vec2 pointB = b2TransformPoint( xfB, localPointB );\n\n\t\t\tfloat separation = b2Dot( b2Sub( pointB, pointA ), normal );\n\t\t\treturn separation;\n\t\t}\n\n\t\tcase b2_faceBType:\n\t\t{\n\t\t\tb2Vec2 normal = b2RotateVector( xfB.q, f->axis );\n\t\t\tb2Vec2 pointB = b2TransformPoint( xfB, f->localPoint );\n\n\t\t\tb2Vec2 axisA = b2InvRotateVector( xfA.q, b2Neg( normal ) );\n\n\t\t\t*indexB = -1;\n\t\t\t*indexA = b2FindSupport( f->proxyA, axisA );\n\n\t\t\tb2Vec2 localPointA = f->proxyA->points[*indexA];\n\t\t\tb2Vec2 pointA = b2TransformPoint( xfA, localPointA );\n\n\t\t\tfloat separation = b2Dot( b2Sub( pointA, pointB ), normal );\n\t\t\treturn separation;\n\t\t}\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t\t\t*indexA = -1;\n\t\t\t*indexB = -1;\n\t\t\treturn 0.0f;\n\t}\n}\n\n//\nstatic float b2EvaluateSeparation( const b2SeparationFunction* f, int indexA, int indexB, float t )\n{\n\tb2Transform xfA = b2GetSweepTransform( &f->sweepA, t );\n\tb2Transform xfB = b2GetSweepTransform( &f->sweepB, t );\n\n\tswitch ( f->type )\n\t{\n\t\tcase b2_pointsType:\n\t\t{\n\t\t\tb2Vec2 localPointA = f->proxyA->points[indexA];\n\t\t\tb2Vec2 localPointB = f->proxyB->points[indexB];\n\n\t\t\tb2Vec2 pointA = b2TransformPoint( xfA, localPointA );\n\t\t\tb2Vec2 pointB = b2TransformPoint( xfB, localPointB );\n\n\t\t\tfloat separation = b2Dot( b2Sub( pointB, pointA ), f->axis );\n\t\t\treturn separation;\n\t\t}\n\n\t\tcase b2_faceAType:\n\t\t{\n\t\t\tb2Vec2 normal = b2RotateVector( xfA.q, f->axis );\n\t\t\tb2Vec2 pointA = b2TransformPoint( xfA, f->localPoint );\n\n\t\t\tb2Vec2 localPointB = f->proxyB->points[indexB];\n\t\t\tb2Vec2 pointB = b2TransformPoint( xfB, localPointB );\n\n\t\t\tfloat separation = b2Dot( b2Sub( pointB, pointA ), normal );\n\t\t\treturn separation;\n\t\t}\n\n\t\tcase b2_faceBType:\n\t\t{\n\t\t\tb2Vec2 normal = b2RotateVector( xfB.q, f->axis );\n\t\t\tb2Vec2 pointB = b2TransformPoint( xfB, f->localPoint );\n\n\t\t\tb2Vec2 localPointA = f->proxyA->points[indexA];\n\t\t\tb2Vec2 pointA = b2TransformPoint( xfA, localPointA );\n\n\t\t\tfloat separation = b2Dot( b2Sub( pointA, pointB ), normal );\n\t\t\treturn separation;\n\t\t}\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t\t\treturn 0.0f;\n\t}\n}\n\n// CCD via the local separating axis method. This seeks progression\n// by computing the largest time at which separation is maintained.\nb2TOIOutput b2TimeOfImpact( const b2TOIInput* input )\n{\n#if B2_SNOOP_TOI_COUNTERS\n\tuint64_t ticks = b2GetTicks();\n\t++b2_toiCalls;\n#endif\n\n\tb2TOIOutput output;\n\toutput.state = b2_toiStateUnknown;\n\toutput.fraction = input->maxFraction;\n\n\tb2Sweep sweepA = input->sweepA;\n\tb2Sweep sweepB = input->sweepB;\n\tB2_ASSERT( b2IsNormalizedRot( sweepA.q1 ) && b2IsNormalizedRot( sweepA.q2 ) );\n\tB2_ASSERT( b2IsNormalizedRot( sweepB.q1 ) && b2IsNormalizedRot( sweepB.q2 ) );\n\n\t// todo_erin\n\t// c1 can be at the origin yet the points are far away\n\t// b2Vec2 origin = b2Add(sweepA.c1, input->proxyA.points[0]);\n\n\tconst b2ShapeProxy* proxyA = &input->proxyA;\n\tconst b2ShapeProxy* proxyB = &input->proxyB;\n\n\tfloat tMax = input->maxFraction;\n\n\tfloat totalRadius = proxyA->radius + proxyB->radius;\n\tfloat target = b2MaxFloat( B2_LINEAR_SLOP, totalRadius - B2_LINEAR_SLOP );\n\tfloat tolerance = 0.25f * B2_LINEAR_SLOP;\n\tB2_ASSERT( target > tolerance );\n\n\tfloat t1 = 0.0f;\n\tconst int k_maxIterations = 20;\n\tint distanceIterations = 0;\n\n\t// Prepare input for distance query.\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceInput distanceInput;\n\tdistanceInput.proxyA = input->proxyA;\n\tdistanceInput.proxyB = input->proxyB;\n\tdistanceInput.useRadii = false;\n\n\t// The outer loop progressively attempts to compute new separating axes.\n\t// This loop terminates when an axis is repeated (no progress is made).\n\tfor ( ;; )\n\t{\n\t\tb2Transform xfA = b2GetSweepTransform( &sweepA, t1 );\n\t\tb2Transform xfB = b2GetSweepTransform( &sweepB, t1 );\n\n\t\t// Get the distance between shapes. We can also use the results\n\t\t// to get a separating axis.\n\t\tdistanceInput.transformA = xfA;\n\t\tdistanceInput.transformB = xfB;\n\t\tb2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );\n\n\t\t// Progressive time of impact. This handles slender geometry well but introduces\n\t\t// significant time loss.\n\t\t// if (distanceIterations == 0)\n\t\t//{\n\t\t//\tif ( distanceOutput.distance > totalRadius + B2_SPECULATIVE_DISTANCE )\n\t\t//\t{\n\t\t//\t\ttarget = totalRadius + B2_SPECULATIVE_DISTANCE - tolerance;\n\t\t//\t}\n\t\t//\telse\n\t\t//\t{\n\t\t//\t\ttarget = distanceOutput.distance - 1.5f * tolerance;\n\t\t//\t\ttarget = b2MaxFloat( target, 2.0f * tolerance );\n\t\t//\t}\n\t\t//}\n\n\t\tdistanceIterations += 1;\n#if B2_SNOOP_TOI_COUNTERS\n\t\tb2_toiDistanceIterations += 1;\n#endif\n\n\t\t// If the shapes are overlapped, we give up on continuous collision.\n\t\tif ( distanceOutput.distance <= 0.0f )\n\t\t{\n\t\t\t// Failure!\n\t\t\toutput.state = b2_toiStateOverlapped;\n#if B2_SNOOP_TOI_COUNTERS\n\t\t\tb2_toiOverlappedCount += 1;\n#endif\n\t\t\toutput.fraction = 0.0f;\n\t\t\tbreak;\n\t\t}\n\n\t\tif ( distanceOutput.distance <= target + tolerance )\n\t\t{\n\t\t\t// Victory!\n\t\t\toutput.state = b2_toiStateHit;\n#if B2_SNOOP_TOI_COUNTERS\n\t\t\tb2_toiHitCount += 1;\n#endif\n\t\t\t// Averaged hit point\n\t\t\tb2Vec2 pA = b2MulAdd( distanceOutput.pointA, proxyA->radius, distanceOutput.normal );\n\t\t\tb2Vec2 pB = b2MulAdd( distanceOutput.pointB, -proxyB->radius, distanceOutput.normal );\n\t\t\toutput.point = b2Lerp( pA, pB, 0.5f );\n\t\t\toutput.normal = distanceOutput.normal;\n\t\t\toutput.fraction = t1;\n\t\t\tbreak;\n\t\t}\n\n\t\t// Initialize the separating axis.\n\t\tb2SeparationFunction fcn = b2MakeSeparationFunction( &cache, proxyA, &sweepA, proxyB, &sweepB, t1 );\n#if 0\n\t\t// Dump the curve seen by the root finder\n\t\t{\n\t\t\tconst int N = 100;\n\t\t\tfloat dx = 1.0f / N;\n\t\t\tfloat xs[N + 1];\n\t\t\tfloat fs[N + 1];\n\n\t\t\tfloat x = 0.0f;\n\n\t\t\tfor (int i = 0; i <= N; ++i)\n\t\t\t{\n\t\t\t\tsweepA.GetTransform(&xfA, x);\n\t\t\t\tsweepB.GetTransform(&xfB, x);\n\t\t\t\tfloat f = fcn.Evaluate(xfA, xfB) - target;\n\n\t\t\t\tprintf(\"%g %g\\n\", x, f);\n\n\t\t\t\txs[i] = x;\n\t\t\t\tfs[i] = f;\n\n\t\t\t\tx += dx;\n\t\t\t}\n\t\t}\n#endif\n\n\t\t// Compute the TOI on the separating axis. We do this by successively\n\t\t// resolving the deepest point. This loop is bounded by the number of vertices.\n\t\tbool done = false;\n\t\tfloat t2 = tMax;\n\t\tint pushBackIterations = 0;\n\t\tfor ( ;; )\n\t\t{\n\t\t\t// Find the deepest point at t2. Store the witness point indices.\n\t\t\tint indexA, indexB;\n\t\t\tfloat s2 = b2FindMinSeparation( &fcn, &indexA, &indexB, t2 );\n\n\t\t\t// Is the final configuration separated?\n\t\t\tif ( s2 > target + tolerance )\n\t\t\t{\n\t\t\t\t// Victory!\n\t\t\t\toutput.state = b2_toiStateSeparated;\n#if B2_SNOOP_TOI_COUNTERS\n\t\t\t\tb2_toiSeparatedCount += 1;\n#endif\n\t\t\t\toutput.fraction = tMax;\n\t\t\t\tdone = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Has the separation reached tolerance?\n\t\t\tif ( s2 > target - tolerance )\n\t\t\t{\n\t\t\t\t// Advance the sweeps\n\t\t\t\tt1 = t2;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Compute the initial separation of the witness points.\n\t\t\tfloat s1 = b2EvaluateSeparation( &fcn, indexA, indexB, t1 );\n\n\t\t\t// Check for initial overlap. This might happen if the root finder\n\t\t\t// runs out of iterations.\n\t\t\tif ( s1 < target - tolerance )\n\t\t\t{\n\t\t\t\toutput.state = b2_toiStateFailed;\n#if B2_SNOOP_TOI_COUNTERS\n\t\t\t\tb2_toiFailedCount += 1;\n#endif\n\t\t\t\toutput.fraction = t1;\n\t\t\t\tdone = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Check for touching\n\t\t\tif ( s1 <= target + tolerance )\n\t\t\t{\n\t\t\t\t// Victory! t1 should hold the TOI (could be 0.0).\n\t\t\t\toutput.state = b2_toiStateHit;\n#if B2_SNOOP_TOI_COUNTERS\n\t\t\t\tb2_toiHitCount += 1;\n#endif\n\t\t\t\t// Averaged hit point\n\t\t\t\tb2Vec2 pA = b2MulAdd( distanceOutput.pointA, proxyA->radius, distanceOutput.normal );\n\t\t\t\tb2Vec2 pB = b2MulAdd( distanceOutput.pointB, -proxyB->radius, distanceOutput.normal );\n\t\t\t\toutput.point = b2Lerp( pA, pB, 0.5f );\n\t\t\t\toutput.normal = distanceOutput.normal;\n\t\t\t\toutput.fraction = t1;\n\t\t\t\tdone = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Compute 1D root of: f(x) - target = 0\n\t\t\tint rootIterationCount = 0;\n\t\t\tfloat a1 = t1, a2 = t2;\n\t\t\tfor ( ;; )\n\t\t\t{\n\t\t\t\t// Use a mix of the secant rule and bisection.\n\t\t\t\tfloat t;\n\t\t\t\tif ( rootIterationCount & 1 )\n\t\t\t\t{\n\t\t\t\t\t// Secant rule to improve convergence.\n\t\t\t\t\tt = a1 + ( target - s1 ) * ( a2 - a1 ) / ( s2 - s1 );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Bisection to guarantee progress.\n\t\t\t\t\tt = 0.5f * ( a1 + a2 );\n\t\t\t\t}\n\n\t\t\t\trootIterationCount += 1;\n\n#if B2_SNOOP_TOI_COUNTERS\n\t\t\t\t++b2_toiRootIterations;\n#endif\n\n\t\t\t\tfloat s = b2EvaluateSeparation( &fcn, indexA, indexB, t );\n\n\t\t\t\tif ( b2AbsFloat( s - target ) < tolerance )\n\t\t\t\t{\n\t\t\t\t\t// t2 holds a tentative value for t1\n\t\t\t\t\tt2 = t;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Ensure we continue to bracket the root.\n\t\t\t\tif ( s > target )\n\t\t\t\t{\n\t\t\t\t\ta1 = t;\n\t\t\t\t\ts1 = s;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ta2 = t;\n\t\t\t\t\ts2 = s;\n\t\t\t\t}\n\n\t\t\t\tif ( rootIterationCount == 50 )\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n#if B2_SNOOP_TOI_COUNTERS\n\t\t\tb2_toiMaxRootIterations = b2MaxInt( b2_toiMaxRootIterations, rootIterationCount );\n#endif\n\n\t\t\tpushBackIterations += 1;\n\n\t\t\tif ( pushBackIterations == B2_MAX_POLYGON_VERTICES )\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif ( done )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tif ( distanceIterations == k_maxIterations )\n\t\t{\n\t\t\t// Root finder got stuck. Semi-victory.\n\t\t\toutput.state = b2_toiStateFailed;\n#if B2_SNOOP_TOI_COUNTERS\n\t\t\tb2_toiFailedCount += 1;\n#endif\n\t\t\t// Averaged hit point\n\t\t\tb2Vec2 pA = b2MulAdd( distanceOutput.pointA, proxyA->radius, distanceOutput.normal );\n\t\t\tb2Vec2 pB = b2MulAdd( distanceOutput.pointB, -proxyB->radius, distanceOutput.normal );\n\t\t\toutput.point = b2Lerp( pA, pB, 0.5f );\n\t\t\toutput.normal = distanceOutput.normal;\n\t\t\toutput.fraction = t1;\n\t\t\tbreak;\n\t\t}\n\t}\n\n#if B2_SNOOP_TOI_COUNTERS\n\tb2_toiMaxDistanceIterations = b2MaxInt( b2_toiMaxDistanceIterations, distanceIterations );\n\n\tfloat time = b2GetMilliseconds( ticks );\n\tb2_toiMaxTime = b2MaxFloat( b2_toiMaxTime, time );\n\tb2_toiTime += time;\n#endif\n\n\treturn output;\n}\n"
  },
  {
    "path": "src/distance_joint.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"body.h\"\n#include \"core.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"solver.h\"\n#include \"solver_set.h\"\n\n// needed for dll export\n#include \"box2d/box2d.h\"\n\nvoid b2DistanceJoint_SetLength( b2JointId jointId, float length )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\n\tjoint->length = b2ClampFloat( length, B2_LINEAR_SLOP, B2_HUGE );\n\tjoint->impulse = 0.0f;\n\tjoint->lowerImpulse = 0.0f;\n\tjoint->upperImpulse = 0.0f;\n}\n\nfloat b2DistanceJoint_GetLength( b2JointId jointId )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\treturn joint->length;\n}\n\nvoid b2DistanceJoint_EnableLimit( b2JointId jointId, bool enableLimit )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\tjoint->enableLimit = enableLimit;\n}\n\nbool b2DistanceJoint_IsLimitEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\treturn joint->distanceJoint.enableLimit;\n}\n\nvoid b2DistanceJoint_SetLengthRange( b2JointId jointId, float minLength, float maxLength )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\n\tminLength = b2ClampFloat( minLength, B2_LINEAR_SLOP, B2_HUGE );\n\tmaxLength = b2ClampFloat( maxLength, B2_LINEAR_SLOP, B2_HUGE );\n\tjoint->minLength = b2MinFloat( minLength, maxLength );\n\tjoint->maxLength = b2MaxFloat( minLength, maxLength );\n\tjoint->impulse = 0.0f;\n\tjoint->lowerImpulse = 0.0f;\n\tjoint->upperImpulse = 0.0f;\n}\n\nfloat b2DistanceJoint_GetMinLength( b2JointId jointId )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\treturn joint->minLength;\n}\n\nfloat b2DistanceJoint_GetMaxLength( b2JointId jointId )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\treturn joint->maxLength;\n}\n\nfloat b2DistanceJoint_GetCurrentLength( b2JointId jointId )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn 0.0f;\n\t}\n\n\tb2Transform transformA = b2GetBodyTransform( world, base->bodyIdA );\n\tb2Transform transformB = b2GetBodyTransform( world, base->bodyIdB );\n\n\tb2Vec2 pA = b2TransformPoint( transformA, base->localFrameA.p );\n\tb2Vec2 pB = b2TransformPoint( transformB, base->localFrameB.p );\n\tb2Vec2 d = b2Sub( pB, pA );\n\tfloat length = b2Length( d );\n\treturn length;\n}\n\nvoid b2DistanceJoint_EnableSpring( b2JointId jointId, bool enableSpring )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tbase->distanceJoint.enableSpring = enableSpring;\n}\n\nbool b2DistanceJoint_IsSpringEnabled( b2JointId jointId )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\treturn base->distanceJoint.enableSpring;\n}\n\nvoid b2DistanceJoint_SetSpringForceRange( b2JointId jointId, float lowerForce, float upperForce )\n{\n\tB2_ASSERT( lowerForce <= upperForce );\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tbase->distanceJoint.lowerSpringForce = lowerForce;\n\tbase->distanceJoint.upperSpringForce = upperForce;\n}\n\nvoid b2DistanceJoint_GetSpringForceRange( b2JointId jointId, float* lowerForce, float* upperForce )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\t*lowerForce = base->distanceJoint.lowerSpringForce;\n\t*upperForce = base->distanceJoint.upperSpringForce;\n}\n\nvoid b2DistanceJoint_SetSpringHertz( b2JointId jointId, float hertz )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tbase->distanceJoint.hertz = hertz;\n}\n\nvoid b2DistanceJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tbase->distanceJoint.dampingRatio = dampingRatio;\n}\n\nfloat b2DistanceJoint_GetSpringHertz( b2JointId jointId )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\treturn joint->hertz;\n}\n\nfloat b2DistanceJoint_GetSpringDampingRatio( b2JointId jointId )\n{\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\treturn joint->dampingRatio;\n}\n\nvoid b2DistanceJoint_EnableMotor( b2JointId jointId, bool enableMotor )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tif ( enableMotor != joint->distanceJoint.enableMotor )\n\t{\n\t\tjoint->distanceJoint.enableMotor = enableMotor;\n\t\tjoint->distanceJoint.motorImpulse = 0.0f;\n\t}\n}\n\nbool b2DistanceJoint_IsMotorEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\treturn joint->distanceJoint.enableMotor;\n}\n\nvoid b2DistanceJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tjoint->distanceJoint.motorSpeed = motorSpeed;\n}\n\nfloat b2DistanceJoint_GetMotorSpeed( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\treturn joint->distanceJoint.motorSpeed;\n}\n\nfloat b2DistanceJoint_GetMotorForce( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\treturn world->inv_h * base->distanceJoint.motorImpulse;\n}\n\nvoid b2DistanceJoint_SetMaxMotorForce( b2JointId jointId, float force )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\tjoint->distanceJoint.maxMotorForce = force;\n}\n\nfloat b2DistanceJoint_GetMaxMotorForce( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint );\n\treturn joint->distanceJoint.maxMotorForce;\n}\n\nb2Vec2 b2GetDistanceJointForce( b2World* world, b2JointSim* base )\n{\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\n\tb2Transform transformA = b2GetBodyTransform( world, base->bodyIdA );\n\tb2Transform transformB = b2GetBodyTransform( world, base->bodyIdB );\n\n\tb2Vec2 pA = b2TransformPoint( transformA, base->localFrameA.p );\n\tb2Vec2 pB = b2TransformPoint( transformB, base->localFrameB.p );\n\tb2Vec2 d = b2Sub( pB, pA );\n\tb2Vec2 axis = b2Normalize( d );\n\tfloat force = ( joint->impulse + joint->lowerImpulse - joint->upperImpulse + joint->motorImpulse ) * world->inv_h;\n\treturn b2MulSV( force, axis );\n}\n\n// 1-D constrained system\n// m (v2 - v1) = lambda\n// v2 + (beta/h) * x1 + gamma * lambda = 0, gamma has units of inverse mass.\n// x2 = x1 + h * v2\n\n// 1-D mass-damper-spring system\n// m (v2 - v1) + h * d * v2 + h * k *\n\n// C = norm(p2 - p1) - L\n// u = (p2 - p1) / norm(p2 - p1)\n// Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1))\n// J = [-u -cross(r1, u) u cross(r2, u)]\n// K = J * invM * JT\n//   = invMass1 + invI1 * cross(r1, u)^2 + invMass2 + invI2 * cross(r2, u)^2\n\nvoid b2PrepareDistanceJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_distanceJoint );\n\n\t// chase body id to the solver set where the body lives\n\tint idA = base->bodyIdA;\n\tint idB = base->bodyIdB;\n\n\tb2World* world = context->world;\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, idA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, idB );\n\n\tB2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet );\n\n\tb2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex );\n\tb2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex );\n\n\tint localIndexA = bodyA->localIndex;\n\tint localIndexB = bodyB->localIndex;\n\n\tb2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA );\n\tb2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB );\n\n\tfloat mA = bodySimA->invMass;\n\tfloat iA = bodySimA->invInertia;\n\tfloat mB = bodySimB->invMass;\n\tfloat iB = bodySimB->invInertia;\n\n\tbase->invMassA = mA;\n\tbase->invMassB = mB;\n\tbase->invIA = iA;\n\tbase->invIB = iB;\n\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\n\tjoint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX;\n\tjoint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX;\n\n\t// initial anchors in world space\n\tjoint->anchorA = b2RotateVector( bodySimA->transform.q, b2Sub( base->localFrameA.p, bodySimA->localCenter ) );\n\tjoint->anchorB = b2RotateVector( bodySimB->transform.q, b2Sub( base->localFrameB.p, bodySimB->localCenter ) );\n\tjoint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center );\n\n\tb2Vec2 rA = joint->anchorA;\n\tb2Vec2 rB = joint->anchorB;\n\tb2Vec2 separation = b2Add( b2Sub( rB, rA ), joint->deltaCenter );\n\tb2Vec2 axis = b2Normalize( separation );\n\n\t// compute effective mass\n\tfloat crA = b2Cross( rA, axis );\n\tfloat crB = b2Cross( rB, axis );\n\tfloat k = mA + mB + iA * crA * crA + iB * crB * crB;\n\tjoint->axialMass = k > 0.0f ? 1.0f / k : 0.0f;\n\n\tjoint->distanceSoftness = b2MakeSoft( joint->hertz, joint->dampingRatio, context->h );\n\n\tif ( context->enableWarmStarting == false )\n\t{\n\t\tjoint->impulse = 0.0f;\n\t\tjoint->lowerImpulse = 0.0f;\n\t\tjoint->upperImpulse = 0.0f;\n\t\tjoint->motorImpulse = 0.0f;\n\t}\n}\n\nvoid b2WarmStartDistanceJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_distanceJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB );\n\n\tb2Vec2 ds = b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), b2Sub( rB, rA ) );\n\tb2Vec2 separation = b2Add( joint->deltaCenter, ds );\n\tb2Vec2 axis = b2Normalize( separation );\n\n\tfloat axialImpulse = joint->impulse + joint->lowerImpulse - joint->upperImpulse + joint->motorImpulse;\n\tb2Vec2 P = b2MulSV( axialImpulse, axis );\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, P );\n\t\tstateA->angularVelocity -= iA * b2Cross( rA, P );\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, P );\n\t\tstateB->angularVelocity += iB * b2Cross( rB, P );\n\t}\n}\n\nvoid b2SolveDistanceJoint( b2JointSim* base, b2StepContext* context, bool useBias )\n{\n\tB2_ASSERT( base->type == b2_distanceJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 vA = stateA->linearVelocity;\n\tfloat wA = stateA->angularVelocity;\n\tb2Vec2 vB = stateB->linearVelocity;\n\tfloat wB = stateB->angularVelocity;\n\n\t// current anchors\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB );\n\n\t// current separation\n\tb2Vec2 ds = b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), b2Sub( rB, rA ) );\n\tb2Vec2 separation = b2Add( joint->deltaCenter, ds );\n\n\tfloat length = b2Length( separation );\n\tb2Vec2 axis = b2Normalize( separation );\n\n\t// joint is soft if\n\t// - spring is enabled\n\t// - and (joint limit is disabled or limits are not equal)\n\tif ( joint->enableSpring && ( joint->minLength < joint->maxLength || joint->enableLimit == false ) )\n\t{\n\t\t// spring\n\t\tif ( joint->hertz > 0.0f )\n\t\t{\n\t\t\t// Cdot = dot(u, v + cross(w, r))\n\t\t\tb2Vec2 vr = b2Add( b2Sub( vB, vA ), b2Sub( b2CrossSV( wB, rB ), b2CrossSV( wA, rA ) ) );\n\t\t\tfloat Cdot = b2Dot( axis, vr );\n\t\t\tfloat C = length - joint->length;\n\t\t\tfloat bias = joint->distanceSoftness.biasRate * C;\n\n\t\t\tfloat m = joint->distanceSoftness.massScale * joint->axialMass;\n\t\t\tfloat oldImpulse = joint->impulse;\n\t\t\tfloat impulse = -m * ( Cdot + bias ) - joint->distanceSoftness.impulseScale * oldImpulse;\n\n\t\t\tfloat h = context->h;\n\t\t\tjoint->impulse = b2ClampFloat( joint->impulse + impulse, joint->lowerSpringForce * h, joint->upperSpringForce * h );\n\t\t\timpulse = joint->impulse - oldImpulse;\n\n\t\t\tb2Vec2 P = b2MulSV( impulse, axis );\n\t\t\tvA = b2MulSub( vA, mA, P );\n\t\t\twA -= iA * b2Cross( rA, P );\n\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t\twB += iB * b2Cross( rB, P );\n\t\t}\n\n\t\tif ( joint->enableMotor )\n\t\t{\n\t\t\tb2Vec2 vr = b2Add( b2Sub( vB, vA ), b2Sub( b2CrossSV( wB, rB ), b2CrossSV( wA, rA ) ) );\n\t\t\tfloat Cdot = b2Dot( axis, vr );\n\t\t\tfloat impulse = joint->axialMass * ( joint->motorSpeed - Cdot );\n\t\t\tfloat oldImpulse = joint->motorImpulse;\n\t\t\tfloat maxImpulse = context->h * joint->maxMotorForce;\n\t\t\tjoint->motorImpulse = b2ClampFloat( joint->motorImpulse + impulse, -maxImpulse, maxImpulse );\n\t\t\timpulse = joint->motorImpulse - oldImpulse;\n\n\t\t\tb2Vec2 P = b2MulSV( impulse, axis );\n\t\t\tvA = b2MulSub( vA, mA, P );\n\t\t\twA -= iA * b2Cross( rA, P );\n\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t\twB += iB * b2Cross( rB, P );\n\t\t}\n\n\t\tif ( joint->enableLimit )\n\t\t{\n\t\t\t// lower limit\n\t\t\t{\n\t\t\t\tb2Vec2 vr = b2Add( b2Sub( vB, vA ), b2Sub( b2CrossSV( wB, rB ), b2CrossSV( wA, rA ) ) );\n\t\t\t\tfloat Cdot = b2Dot( axis, vr );\n\n\t\t\t\tfloat C = length - joint->minLength;\n\n\t\t\t\tfloat bias = 0.0f;\n\t\t\t\tfloat massCoeff = 1.0f;\n\t\t\t\tfloat impulseCoeff = 0.0f;\n\t\t\t\tif ( C > 0.0f )\n\t\t\t\t{\n\t\t\t\t\t// speculative\n\t\t\t\t\tbias = C * context->inv_h;\n\t\t\t\t}\n\t\t\t\telse if ( useBias )\n\t\t\t\t{\n\t\t\t\t\tbias = base->constraintSoftness.biasRate * C;\n\t\t\t\t\tmassCoeff = base->constraintSoftness.massScale;\n\t\t\t\t\timpulseCoeff = base->constraintSoftness.impulseScale;\n\t\t\t\t}\n\n\t\t\t\tfloat impulse = -massCoeff * joint->axialMass * ( Cdot + bias ) - impulseCoeff * joint->lowerImpulse;\n\t\t\t\tfloat newImpulse = b2MaxFloat( 0.0f, joint->lowerImpulse + impulse );\n\t\t\t\timpulse = newImpulse - joint->lowerImpulse;\n\t\t\t\tjoint->lowerImpulse = newImpulse;\n\n\t\t\t\tb2Vec2 P = b2MulSV( impulse, axis );\n\t\t\t\tvA = b2MulSub( vA, mA, P );\n\t\t\t\twA -= iA * b2Cross( rA, P );\n\t\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t\t\twB += iB * b2Cross( rB, P );\n\t\t\t}\n\n\t\t\t// upper\n\t\t\t{\n\t\t\t\tb2Vec2 vr = b2Add( b2Sub( vA, vB ), b2Sub( b2CrossSV( wA, rA ), b2CrossSV( wB, rB ) ) );\n\t\t\t\tfloat Cdot = b2Dot( axis, vr );\n\n\t\t\t\tfloat C = joint->maxLength - length;\n\n\t\t\t\tfloat bias = 0.0f;\n\t\t\t\tfloat massScale = 1.0f;\n\t\t\t\tfloat impulseScale = 0.0f;\n\t\t\t\tif ( C > 0.0f )\n\t\t\t\t{\n\t\t\t\t\t// speculative\n\t\t\t\t\tbias = C * context->inv_h;\n\t\t\t\t}\n\t\t\t\telse if ( useBias )\n\t\t\t\t{\n\t\t\t\t\tbias = base->constraintSoftness.biasRate * C;\n\t\t\t\t\tmassScale = base->constraintSoftness.massScale;\n\t\t\t\t\timpulseScale = base->constraintSoftness.impulseScale;\n\t\t\t\t}\n\n\t\t\t\tfloat impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->upperImpulse;\n\t\t\t\tfloat newImpulse = b2MaxFloat( 0.0f, joint->upperImpulse + impulse );\n\t\t\t\timpulse = newImpulse - joint->upperImpulse;\n\t\t\t\tjoint->upperImpulse = newImpulse;\n\n\t\t\t\tb2Vec2 P = b2MulSV( -impulse, axis );\n\t\t\t\tvA = b2MulSub( vA, mA, P );\n\t\t\t\twA -= iA * b2Cross( rA, P );\n\t\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t\t\twB += iB * b2Cross( rB, P );\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// rigid constraint\n\t\tb2Vec2 vr = b2Add( b2Sub( vB, vA ), b2Sub( b2CrossSV( wB, rB ), b2CrossSV( wA, rA ) ) );\n\t\tfloat Cdot = b2Dot( axis, vr );\n\n\t\tfloat C = length - joint->length;\n\n\t\tfloat bias = 0.0f;\n\t\tfloat massScale = 1.0f;\n\t\tfloat impulseScale = 0.0f;\n\t\tif ( useBias )\n\t\t{\n\t\t\tbias = base->constraintSoftness.biasRate * C;\n\t\t\tmassScale = base->constraintSoftness.massScale;\n\t\t\timpulseScale = base->constraintSoftness.impulseScale;\n\t\t}\n\n\t\tfloat impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->impulse;\n\t\tjoint->impulse += impulse;\n\n\t\tb2Vec2 P = b2MulSV( impulse, axis );\n\t\tvA = b2MulSub( vA, mA, P );\n\t\twA -= iA * b2Cross( rA, P );\n\t\tvB = b2MulAdd( vB, mB, P );\n\t\twB += iB * b2Cross( rB, P );\n\t}\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = vA;\n\t\tstateA->angularVelocity = wA;\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = vB;\n\t\tstateB->angularVelocity = wB;\n\t}\n}\n\n#if 0\nvoid b2DistanceJoint::Dump()\n{\n\tint32 indexA = m_bodyA->m_islandIndex;\n\tint32 indexB = m_bodyB->m_islandIndex;\n\n\tb2Dump(\"  b2DistanceJointDef jd;\\n\");\n\tb2Dump(\"  jd.bodyA = sims[%d];\\n\", indexA);\n\tb2Dump(\"  jd.bodyB = sims[%d];\\n\", indexB);\n\tb2Dump(\"  jd.collideConnected = bool(%d);\\n\", m_collideConnected);\n\tb2Dump(\"  jd.localAnchorA.Set(%.9g, %.9g);\\n\", m_localAnchorA.x, m_localAnchorA.y);\n\tb2Dump(\"  jd.localAnchorB.Set(%.9g, %.9g);\\n\", m_localAnchorB.x, m_localAnchorB.y);\n\tb2Dump(\"  jd.length = %.9g;\\n\", m_length);\n\tb2Dump(\"  jd.minLength = %.9g;\\n\", m_minLength);\n\tb2Dump(\"  jd.maxLength = %.9g;\\n\", m_maxLength);\n\tb2Dump(\"  jd.stiffness = %.9g;\\n\", m_stiffness);\n\tb2Dump(\"  jd.damping = %.9g;\\n\", m_damping);\n\tb2Dump(\"  joints[%d] = m_world->CreateJoint(&jd);\\n\", m_index);\n}\n#endif\n\nvoid b2DrawDistanceJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB )\n{\n\tB2_ASSERT( base->type == b2_distanceJoint );\n\n\tb2DistanceJoint* joint = &base->distanceJoint;\n\n\tb2Vec2 pA = b2TransformPoint( transformA, base->localFrameA.p );\n\tb2Vec2 pB = b2TransformPoint( transformB, base->localFrameB.p );\n\n\tb2Vec2 axis = b2Normalize( b2Sub( pB, pA ) );\n\n\tif ( joint->minLength < joint->maxLength && joint->enableLimit )\n\t{\n\t\tb2Vec2 pMin = b2MulAdd( pA, joint->minLength, axis );\n\t\tb2Vec2 pMax = b2MulAdd( pA, joint->maxLength, axis );\n\t\tb2Vec2 offset = b2MulSV( 0.05f * b2_lengthUnitsPerMeter, b2RightPerp( axis ) );\n\n\t\tif ( joint->minLength > B2_LINEAR_SLOP )\n\t\t{\n\t\t\t// draw->DrawPoint(pMin, 4.0f, c2, draw->context);\n\t\t\tdraw->DrawLineFcn( b2Sub( pMin, offset ), b2Add( pMin, offset ), b2_colorLightGreen, draw->context );\n\t\t}\n\n\t\tif ( joint->maxLength < B2_HUGE )\n\t\t{\n\t\t\t// draw->DrawPoint(pMax, 4.0f, c3, draw->context);\n\t\t\tdraw->DrawLineFcn( b2Sub( pMax, offset ), b2Add( pMax, offset ), b2_colorRed, draw->context );\n\t\t}\n\n\t\tif ( joint->minLength > B2_LINEAR_SLOP && joint->maxLength < B2_HUGE )\n\t\t{\n\t\t\tdraw->DrawLineFcn( pMin, pMax, b2_colorGray, draw->context );\n\t\t}\n\t}\n\n\tdraw->DrawLineFcn( pA, pB, b2_colorWhite, draw->context );\n\tdraw->DrawPointFcn( pA, 4.0f, b2_colorWhite, draw->context );\n\tdraw->DrawPointFcn( pB, 4.0f, b2_colorWhite, draw->context );\n\n\tif ( joint->hertz > 0.0f && joint->enableSpring )\n\t{\n\t\tb2Vec2 pRest = b2MulAdd( pA, joint->length, axis );\n\t\tdraw->DrawPointFcn( pRest, 4.0f, b2_colorBlue, draw->context );\n\t}\n}\n"
  },
  {
    "path": "src/dynamic_tree.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"aabb.h\"\n#include \"constants.h\"\n#include \"core.h\"\n\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n#include <string.h>\n\n#define B2_TREE_STACK_SIZE 1024\n\n// todo externalize this to visualize internal nodes and speed up FindPairs\n\n// A node in the dynamic tree.\ntypedef struct b2TreeNode\n{\n\t// The node bounding box\n\tb2AABB aabb; // 16\n\n\t// Category bits for collision filtering\n\tuint64_t categoryBits; // 8\n\n\tunion\n\t{\n\t\t// Children (internal node)\n\t\tstruct\n\t\t{\n\t\t\tint32_t child1, child2;\n\t\t} children;\n\n\t\t/// User data (leaf node)\n\t\tuint64_t userData;\n\t}; // 8\n\n\tunion\n\t{\n\t\t/// The node parent index (allocated node)\n\t\tint32_t parent;\n\n\t\t/// The node freelist next index (free node)\n\t\tint32_t next;\n\t}; // 4\n\n\tuint16_t height; // 2\n\tuint16_t flags;\t // 2\n} b2TreeNode;\n\nstatic b2TreeNode b2_defaultTreeNode = {\n\t.aabb = { { 0.0f, 0.0f }, { 0.0f, 0.0f } },\n\t.categoryBits = B2_DEFAULT_CATEGORY_BITS,\n\t.children =\n\t\t{\n\t\t\t.child1 = B2_NULL_INDEX,\n\t\t\t.child2 = B2_NULL_INDEX,\n\t\t},\n\t.parent = B2_NULL_INDEX,\n\t.height = 0,\n\t.flags = b2_allocatedNode,\n};\n\nstatic bool b2IsLeaf( const b2TreeNode* node )\n{\n\treturn node->flags & b2_leafNode;\n}\n\nstatic bool b2IsAllocated( const b2TreeNode* node )\n{\n\treturn node->flags & b2_allocatedNode;\n}\n\nstatic uint16_t b2MaxUInt16( uint16_t a, uint16_t b )\n{\n\treturn a > b ? a : b;\n}\n\nb2DynamicTree b2DynamicTree_Create( void )\n{\n\tb2DynamicTree tree;\n\n\t// memset needed for deterministic serialization\n\tmemset( &tree, 0, sizeof( b2DynamicTree ) );\n\n\ttree.root = B2_NULL_INDEX;\n\n\ttree.nodeCapacity = 16;\n\ttree.nodeCount = 0;\n\ttree.nodes = (b2TreeNode*)b2Alloc( tree.nodeCapacity * sizeof( b2TreeNode ) );\n\tmemset( tree.nodes, 0, tree.nodeCapacity * sizeof( b2TreeNode ) );\n\n\t// Build a linked list for the free list.\n\tfor ( int i = 0; i < tree.nodeCapacity - 1; ++i )\n\t{\n\t\ttree.nodes[i].next = i + 1;\n\t}\n\n\ttree.nodes[tree.nodeCapacity - 1].next = B2_NULL_INDEX;\n\ttree.freeList = 0;\n\n\ttree.proxyCount = 0;\n\n\ttree.leafIndices = NULL;\n\ttree.leafBoxes = NULL;\n\ttree.leafCenters = NULL;\n\ttree.binIndices = NULL;\n\ttree.rebuildCapacity = 0;\n\n\treturn tree;\n}\n\nvoid b2DynamicTree_Destroy( b2DynamicTree* tree )\n{\n\tb2Free( tree->nodes, tree->nodeCapacity * sizeof( b2TreeNode ) );\n\tb2Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int32_t ) );\n\tb2Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b2AABB ) );\n\tb2Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b2Vec2 ) );\n\tb2Free( tree->binIndices, tree->rebuildCapacity * sizeof( int32_t ) );\n\n\tmemset( tree, 0, sizeof( b2DynamicTree ) );\n}\n\n// Allocate a node from the pool. Grow the pool if necessary.\nstatic int b2AllocateNode( b2DynamicTree* tree )\n{\n\t// Expand the node pool as needed.\n\tif ( tree->freeList == B2_NULL_INDEX )\n\t{\n\t\tB2_ASSERT( tree->nodeCount == tree->nodeCapacity );\n\n\t\t// The free list is empty. Rebuild a bigger pool.\n\t\tb2TreeNode* oldNodes = tree->nodes;\n\t\tint oldCapacity = tree->nodeCapacity;\n\t\ttree->nodeCapacity += oldCapacity >> 1;\n\t\ttree->nodes = (b2TreeNode*)b2Alloc( tree->nodeCapacity * sizeof( b2TreeNode ) );\n\t\tB2_ASSERT( oldNodes != NULL );\n\t\tmemcpy( tree->nodes, oldNodes, tree->nodeCount * sizeof( b2TreeNode ) );\n\t\tmemset( tree->nodes + tree->nodeCount, 0, ( tree->nodeCapacity - tree->nodeCount ) * sizeof( b2TreeNode ) );\n\t\tb2Free( oldNodes, oldCapacity * sizeof( b2TreeNode ) );\n\n\t\t// Build a linked list for the free list. The parent pointer becomes the \"next\" pointer.\n\t\t// todo avoid building freelist?\n\t\tfor ( int i = tree->nodeCount; i < tree->nodeCapacity - 1; ++i )\n\t\t{\n\t\t\ttree->nodes[i].next = i + 1;\n\t\t}\n\n\t\ttree->nodes[tree->nodeCapacity - 1].next = B2_NULL_INDEX;\n\t\ttree->freeList = tree->nodeCount;\n\t}\n\n\t// Peel a node off the free list.\n\tint nodeIndex = tree->freeList;\n\tb2TreeNode* node = tree->nodes + nodeIndex;\n\ttree->freeList = node->next;\n\t*node = b2_defaultTreeNode;\n\t++tree->nodeCount;\n\treturn nodeIndex;\n}\n\n// Return a node to the pool.\nstatic void b2FreeNode( b2DynamicTree* tree, int nodeId )\n{\n\tB2_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity );\n\tB2_ASSERT( 0 < tree->nodeCount );\n\ttree->nodes[nodeId].next = tree->freeList;\n\ttree->nodes[nodeId].flags = 0;\n\ttree->freeList = nodeId;\n\t--tree->nodeCount;\n}\n\n// Greedy algorithm for sibling selection using the SAH\n// We have three nodes A-(B,C) and want to add a leaf D, there are three choices.\n// 1: make a new parent for A and D : E-(A-(B,C), D)\n// 2: associate D with B\n//   a: B is a leaf : A-(E-(B,D), C)\n//   b: B is an internal node: A-(B{D},C)\n// 3: associate D with C\n//   a: C is a leaf : A-(B, E-(C,D))\n//   b: C is an internal node: A-(B, C{D})\n// All of these have a clear cost except when B or C is an internal node. Hence we need to be greedy.\n\n// The cost for cases 1, 2a, and 3a can be computed using the sibling cost formula.\n// cost of sibling H = area(union(H, D)) + increased area of ancestors\n\n// Suppose B (or C) is an internal node, then the lowest cost would be one of two cases:\n// case1: D becomes a sibling of B\n// case2: D becomes a descendant of B along with a new internal node of area(D).\nstatic int b2FindBestSibling( const b2DynamicTree* tree, b2AABB boxD )\n{\n\tb2Vec2 centerD = b2AABB_Center( boxD );\n\tfloat areaD = b2Perimeter( boxD );\n\n\tconst b2TreeNode* nodes = tree->nodes;\n\tint rootIndex = tree->root;\n\n\tb2AABB rootBox = nodes[rootIndex].aabb;\n\n\t// Area of current node\n\tfloat areaBase = b2Perimeter( rootBox );\n\n\t// Area of inflated node\n\tfloat directCost = b2Perimeter( b2AABB_Union( rootBox, boxD ) );\n\tfloat inheritedCost = 0.0f;\n\n\tint bestSibling = rootIndex;\n\tfloat bestCost = directCost;\n\n\t// Descend the tree from root, following a single greedy path.\n\tint index = rootIndex;\n\twhile ( nodes[index].height > 0 )\n\t{\n\t\tint child1 = nodes[index].children.child1;\n\t\tint child2 = nodes[index].children.child2;\n\n\t\t// Cost of creating a new parent for this node and the new leaf\n\t\tfloat cost = directCost + inheritedCost;\n\n\t\t// Sometimes there are multiple identical costs within tolerance.\n\t\t// This breaks the ties using the centroid distance.\n\t\tif ( cost < bestCost )\n\t\t{\n\t\t\tbestSibling = index;\n\t\t\tbestCost = cost;\n\t\t}\n\n\t\t// Inheritance cost seen by children\n\t\tinheritedCost += directCost - areaBase;\n\n\t\tbool leaf1 = nodes[child1].height == 0;\n\t\tbool leaf2 = nodes[child2].height == 0;\n\n\t\t// Cost of descending into child 1\n\t\tfloat lowerCost1 = FLT_MAX;\n\t\tb2AABB box1 = nodes[child1].aabb;\n\t\tfloat directCost1 = b2Perimeter( b2AABB_Union( box1, boxD ) );\n\t\tfloat area1 = 0.0f;\n\t\tif ( leaf1 )\n\t\t{\n\t\t\t// Child 1 is a leaf\n\t\t\t// Cost of creating new node and increasing area of node P\n\t\t\tfloat cost1 = directCost1 + inheritedCost;\n\n\t\t\t// Need this here due to while condition above\n\t\t\tif ( cost1 < bestCost )\n\t\t\t{\n\t\t\t\tbestSibling = child1;\n\t\t\t\tbestCost = cost1;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Child 1 is an internal node\n\t\t\tarea1 = b2Perimeter( box1 );\n\n\t\t\t// Lower bound cost of inserting under child 1. The minimum accounts for two possibilities:\n\t\t\t// 1. Child1 could be the sibling with cost1 = inheritedCost + directCost1\n\t\t\t// 2. A descendant of child1 could be the sibling with the lower bound cost of\n\t\t\t//       cost1 = inheritedCost + (directCost1 - area1) + areaD\n\t\t\t// This minimum here leads to the minimum of these two costs.\n\t\t\tlowerCost1 = inheritedCost + directCost1 + b2MinFloat( areaD - area1, 0.0f );\n\t\t}\n\n\t\t// Cost of descending into child 2\n\t\tfloat lowerCost2 = FLT_MAX;\n\t\tb2AABB box2 = nodes[child2].aabb;\n\t\tfloat directCost2 = b2Perimeter( b2AABB_Union( box2, boxD ) );\n\t\tfloat area2 = 0.0f;\n\t\tif ( leaf2 )\n\t\t{\n\t\t\tfloat cost2 = directCost2 + inheritedCost;\n\n\t\t\tif ( cost2 < bestCost )\n\t\t\t{\n\t\t\t\tbestSibling = child2;\n\t\t\t\tbestCost = cost2;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tarea2 = b2Perimeter( box2 );\n\t\t\tlowerCost2 = inheritedCost + directCost2 + b2MinFloat( areaD - area2, 0.0f );\n\t\t}\n\n\t\tif ( leaf1 && leaf2 )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t// Can the cost possibly be decreased?\n\t\tif ( bestCost <= lowerCost1 && bestCost <= lowerCost2 )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tif ( lowerCost1 == lowerCost2 && leaf1 == false )\n\t\t{\n\t\t\tB2_ASSERT( lowerCost1 < FLT_MAX );\n\t\t\tB2_ASSERT( lowerCost2 < FLT_MAX );\n\n\t\t\t// No clear choice based on lower bound surface area. This can happen when both\n\t\t\t// children fully contain D. Fall back to node distance.\n\t\t\tb2Vec2 d1 = b2Sub( b2AABB_Center( box1 ), centerD );\n\t\t\tb2Vec2 d2 = b2Sub( b2AABB_Center( box2 ), centerD );\n\t\t\tlowerCost1 = b2LengthSquared( d1 );\n\t\t\tlowerCost2 = b2LengthSquared( d2 );\n\t\t}\n\n\t\t// Descend\n\t\tif ( lowerCost1 < lowerCost2 && leaf1 == false )\n\t\t{\n\t\t\tindex = child1;\n\t\t\tareaBase = area1;\n\t\t\tdirectCost = directCost1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tindex = child2;\n\t\t\tareaBase = area2;\n\t\t\tdirectCost = directCost2;\n\t\t}\n\n\t\tB2_ASSERT( nodes[index].height > 0 );\n\t}\n\n\treturn bestSibling;\n}\n\nenum b2RotateType\n{\n\tb2_rotateNone,\n\tb2_rotateBF,\n\tb2_rotateBG,\n\tb2_rotateCD,\n\tb2_rotateCE\n};\n\n// Perform a left or right rotation if node A is imbalanced.\n// Returns the new root index.\nstatic void b2RotateNodes( b2DynamicTree* tree, int iA )\n{\n\tB2_ASSERT( iA != B2_NULL_INDEX );\n\n\tb2TreeNode* nodes = tree->nodes;\n\n\tb2TreeNode* A = nodes + iA;\n\tif ( A->height < 2 )\n\t{\n\t\treturn;\n\t}\n\n\tint iB = A->children.child1;\n\tint iC = A->children.child2;\n\tB2_ASSERT( 0 <= iB && iB < tree->nodeCapacity );\n\tB2_ASSERT( 0 <= iC && iC < tree->nodeCapacity );\n\n\tb2TreeNode* B = nodes + iB;\n\tb2TreeNode* C = nodes + iC;\n\n\tif ( B->height == 0 )\n\t{\n\t\t// B is a leaf and C is internal\n\t\tB2_ASSERT( C->height > 0 );\n\n\t\tint iF = C->children.child1;\n\t\tint iG = C->children.child2;\n\t\tb2TreeNode* F = nodes + iF;\n\t\tb2TreeNode* G = nodes + iG;\n\t\tB2_ASSERT( 0 <= iF && iF < tree->nodeCapacity );\n\t\tB2_ASSERT( 0 <= iG && iG < tree->nodeCapacity );\n\n\t\t// Base cost\n\t\tfloat costBase = b2Perimeter( C->aabb );\n\n\t\t// Cost of swapping B and F\n\t\tb2AABB aabbBG = b2AABB_Union( B->aabb, G->aabb );\n\t\tfloat costBF = b2Perimeter( aabbBG );\n\n\t\t// Cost of swapping B and G\n\t\tb2AABB aabbBF = b2AABB_Union( B->aabb, F->aabb );\n\t\tfloat costBG = b2Perimeter( aabbBF );\n\n\t\tif ( costBase < costBF && costBase < costBG )\n\t\t{\n\t\t\t// Rotation does not improve cost\n\t\t\treturn;\n\t\t}\n\n\t\tif ( costBF < costBG )\n\t\t{\n\t\t\t// Swap B and F\n\t\t\tA->children.child1 = iF;\n\t\t\tC->children.child1 = iB;\n\n\t\t\tB->parent = iC;\n\t\t\tF->parent = iA;\n\n\t\t\tC->aabb = aabbBG;\n\n\t\t\tC->height = 1 + b2MaxUInt16( B->height, G->height );\n\t\t\tA->height = 1 + b2MaxUInt16( C->height, F->height );\n\t\t\tC->categoryBits = B->categoryBits | G->categoryBits;\n\t\t\tA->categoryBits = C->categoryBits | F->categoryBits;\n\t\t\tC->flags |= ( B->flags | G->flags ) & b2_enlargedNode;\n\t\t\tA->flags |= ( C->flags | F->flags ) & b2_enlargedNode;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Swap B and G\n\t\t\tA->children.child1 = iG;\n\t\t\tC->children.child2 = iB;\n\n\t\t\tB->parent = iC;\n\t\t\tG->parent = iA;\n\n\t\t\tC->aabb = aabbBF;\n\n\t\t\tC->height = 1 + b2MaxUInt16( B->height, F->height );\n\t\t\tA->height = 1 + b2MaxUInt16( C->height, G->height );\n\t\t\tC->categoryBits = B->categoryBits | F->categoryBits;\n\t\t\tA->categoryBits = C->categoryBits | G->categoryBits;\n\t\t\tC->flags |= ( B->flags | F->flags ) & b2_enlargedNode;\n\t\t\tA->flags |= ( C->flags | G->flags ) & b2_enlargedNode;\n\t\t}\n\t}\n\telse if ( C->height == 0 )\n\t{\n\t\t// C is a leaf and B is internal\n\t\tB2_ASSERT( B->height > 0 );\n\n\t\tint iD = B->children.child1;\n\t\tint iE = B->children.child2;\n\t\tb2TreeNode* D = nodes + iD;\n\t\tb2TreeNode* E = nodes + iE;\n\t\tB2_ASSERT( 0 <= iD && iD < tree->nodeCapacity );\n\t\tB2_ASSERT( 0 <= iE && iE < tree->nodeCapacity );\n\n\t\t// Base cost\n\t\tfloat costBase = b2Perimeter( B->aabb );\n\n\t\t// Cost of swapping C and D\n\t\tb2AABB aabbCE = b2AABB_Union( C->aabb, E->aabb );\n\t\tfloat costCD = b2Perimeter( aabbCE );\n\n\t\t// Cost of swapping C and E\n\t\tb2AABB aabbCD = b2AABB_Union( C->aabb, D->aabb );\n\t\tfloat costCE = b2Perimeter( aabbCD );\n\n\t\tif ( costBase < costCD && costBase < costCE )\n\t\t{\n\t\t\t// Rotation does not improve cost\n\t\t\treturn;\n\t\t}\n\n\t\tif ( costCD < costCE )\n\t\t{\n\t\t\t// Swap C and D\n\t\t\tA->children.child2 = iD;\n\t\t\tB->children.child1 = iC;\n\n\t\t\tC->parent = iB;\n\t\t\tD->parent = iA;\n\n\t\t\tB->aabb = aabbCE;\n\n\t\t\tB->height = 1 + b2MaxUInt16( C->height, E->height );\n\t\t\tA->height = 1 + b2MaxUInt16( B->height, D->height );\n\t\t\tB->categoryBits = C->categoryBits | E->categoryBits;\n\t\t\tA->categoryBits = B->categoryBits | D->categoryBits;\n\t\t\tB->flags |= ( C->flags | E->flags ) & b2_enlargedNode;\n\t\t\tA->flags |= ( B->flags | D->flags ) & b2_enlargedNode;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Swap C and E\n\t\t\tA->children.child2 = iE;\n\t\t\tB->children.child2 = iC;\n\n\t\t\tC->parent = iB;\n\t\t\tE->parent = iA;\n\n\t\t\tB->aabb = aabbCD;\n\t\t\tB->height = 1 + b2MaxUInt16( C->height, D->height );\n\t\t\tA->height = 1 + b2MaxUInt16( B->height, E->height );\n\t\t\tB->categoryBits = C->categoryBits | D->categoryBits;\n\t\t\tA->categoryBits = B->categoryBits | E->categoryBits;\n\t\t\tB->flags |= ( C->flags | D->flags ) & b2_enlargedNode;\n\t\t\tA->flags |= ( B->flags | E->flags ) & b2_enlargedNode;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint iD = B->children.child1;\n\t\tint iE = B->children.child2;\n\t\tint iF = C->children.child1;\n\t\tint iG = C->children.child2;\n\n\t\tB2_ASSERT( 0 <= iD && iD < tree->nodeCapacity );\n\t\tB2_ASSERT( 0 <= iE && iE < tree->nodeCapacity );\n\t\tB2_ASSERT( 0 <= iF && iF < tree->nodeCapacity );\n\t\tB2_ASSERT( 0 <= iG && iG < tree->nodeCapacity );\n\n\t\tb2TreeNode* D = nodes + iD;\n\t\tb2TreeNode* E = nodes + iE;\n\t\tb2TreeNode* F = nodes + iF;\n\t\tb2TreeNode* G = nodes + iG;\n\n\t\t// Base cost\n\t\tfloat areaB = b2Perimeter( B->aabb );\n\t\tfloat areaC = b2Perimeter( C->aabb );\n\t\tfloat costBase = areaB + areaC;\n\t\tenum b2RotateType bestRotation = b2_rotateNone;\n\t\tfloat bestCost = costBase;\n\n\t\t// Cost of swapping B and F\n\t\tb2AABB aabbBG = b2AABB_Union( B->aabb, G->aabb );\n\t\tfloat costBF = areaB + b2Perimeter( aabbBG );\n\t\tif ( costBF < bestCost )\n\t\t{\n\t\t\tbestRotation = b2_rotateBF;\n\t\t\tbestCost = costBF;\n\t\t}\n\n\t\t// Cost of swapping B and G\n\t\tb2AABB aabbBF = b2AABB_Union( B->aabb, F->aabb );\n\t\tfloat costBG = areaB + b2Perimeter( aabbBF );\n\t\tif ( costBG < bestCost )\n\t\t{\n\t\t\tbestRotation = b2_rotateBG;\n\t\t\tbestCost = costBG;\n\t\t}\n\n\t\t// Cost of swapping C and D\n\t\tb2AABB aabbCE = b2AABB_Union( C->aabb, E->aabb );\n\t\tfloat costCD = areaC + b2Perimeter( aabbCE );\n\t\tif ( costCD < bestCost )\n\t\t{\n\t\t\tbestRotation = b2_rotateCD;\n\t\t\tbestCost = costCD;\n\t\t}\n\n\t\t// Cost of swapping C and E\n\t\tb2AABB aabbCD = b2AABB_Union( C->aabb, D->aabb );\n\t\tfloat costCE = areaC + b2Perimeter( aabbCD );\n\t\tif ( costCE < bestCost )\n\t\t{\n\t\t\tbestRotation = b2_rotateCE;\n\t\t\t// bestCost = costCE;\n\t\t}\n\n\t\tswitch ( bestRotation )\n\t\t{\n\t\t\tcase b2_rotateNone:\n\t\t\t\tbreak;\n\n\t\t\tcase b2_rotateBF:\n\t\t\t\tA->children.child1 = iF;\n\t\t\t\tC->children.child1 = iB;\n\n\t\t\t\tB->parent = iC;\n\t\t\t\tF->parent = iA;\n\n\t\t\t\tC->aabb = aabbBG;\n\t\t\t\tC->height = 1 + b2MaxUInt16( B->height, G->height );\n\t\t\t\tA->height = 1 + b2MaxUInt16( C->height, F->height );\n\t\t\t\tC->categoryBits = B->categoryBits | G->categoryBits;\n\t\t\t\tA->categoryBits = C->categoryBits | F->categoryBits;\n\t\t\t\tC->flags |= ( B->flags | G->flags ) & b2_enlargedNode;\n\t\t\t\tA->flags |= ( C->flags | F->flags ) & b2_enlargedNode;\n\t\t\t\tbreak;\n\n\t\t\tcase b2_rotateBG:\n\t\t\t\tA->children.child1 = iG;\n\t\t\t\tC->children.child2 = iB;\n\n\t\t\t\tB->parent = iC;\n\t\t\t\tG->parent = iA;\n\n\t\t\t\tC->aabb = aabbBF;\n\t\t\t\tC->height = 1 + b2MaxUInt16( B->height, F->height );\n\t\t\t\tA->height = 1 + b2MaxUInt16( C->height, G->height );\n\t\t\t\tC->categoryBits = B->categoryBits | F->categoryBits;\n\t\t\t\tA->categoryBits = C->categoryBits | G->categoryBits;\n\t\t\t\tC->flags |= ( B->flags | F->flags ) & b2_enlargedNode;\n\t\t\t\tA->flags |= ( C->flags | G->flags ) & b2_enlargedNode;\n\t\t\t\tbreak;\n\n\t\t\tcase b2_rotateCD:\n\t\t\t\tA->children.child2 = iD;\n\t\t\t\tB->children.child1 = iC;\n\n\t\t\t\tC->parent = iB;\n\t\t\t\tD->parent = iA;\n\n\t\t\t\tB->aabb = aabbCE;\n\t\t\t\tB->height = 1 + b2MaxUInt16( C->height, E->height );\n\t\t\t\tA->height = 1 + b2MaxUInt16( B->height, D->height );\n\t\t\t\tB->categoryBits = C->categoryBits | E->categoryBits;\n\t\t\t\tA->categoryBits = B->categoryBits | D->categoryBits;\n\t\t\t\tB->flags |= ( C->flags | E->flags ) & b2_enlargedNode;\n\t\t\t\tA->flags |= ( B->flags | D->flags ) & b2_enlargedNode;\n\t\t\t\tbreak;\n\n\t\t\tcase b2_rotateCE:\n\t\t\t\tA->children.child2 = iE;\n\t\t\t\tB->children.child2 = iC;\n\n\t\t\t\tC->parent = iB;\n\t\t\t\tE->parent = iA;\n\n\t\t\t\tB->aabb = aabbCD;\n\t\t\t\tB->height = 1 + b2MaxUInt16( C->height, D->height );\n\t\t\t\tA->height = 1 + b2MaxUInt16( B->height, E->height );\n\t\t\t\tB->categoryBits = C->categoryBits | D->categoryBits;\n\t\t\t\tA->categoryBits = B->categoryBits | E->categoryBits;\n\t\t\t\tB->flags |= ( C->flags | D->flags ) & b2_enlargedNode;\n\t\t\t\tA->flags |= ( B->flags | E->flags ) & b2_enlargedNode;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tB2_ASSERT( false );\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic void b2InsertLeaf( b2DynamicTree* tree, int leaf, bool shouldRotate )\n{\n\tif ( tree->root == B2_NULL_INDEX )\n\t{\n\t\ttree->root = leaf;\n\t\ttree->nodes[tree->root].parent = B2_NULL_INDEX;\n\t\treturn;\n\t}\n\n\t// Stage 1: find the best sibling for this node\n\tb2AABB leafAABB = tree->nodes[leaf].aabb;\n\tint sibling = b2FindBestSibling( tree, leafAABB );\n\n\t// Stage 2: create a new parent for the leaf and sibling\n\tint oldParent = tree->nodes[sibling].parent;\n\tint newParent = b2AllocateNode( tree );\n\n\t// Warning: node pointer can change after allocation\n\tb2TreeNode* nodes = tree->nodes;\n\tnodes[newParent].parent = oldParent;\n\tnodes[newParent].userData = UINT64_MAX;\n\tnodes[newParent].aabb = b2AABB_Union( leafAABB, nodes[sibling].aabb );\n\tnodes[newParent].categoryBits = nodes[leaf].categoryBits | nodes[sibling].categoryBits;\n\tnodes[newParent].height = nodes[sibling].height + 1;\n\tnodes[newParent].children.child1 = sibling;\n\tnodes[newParent].children.child2 = leaf;\n\tnodes[sibling].parent = newParent;\n\tnodes[leaf].parent = newParent;\n\n\t// Fix grandparent links\n\tif ( oldParent != B2_NULL_INDEX )\n\t{\n\t\t// The sibling was not the root\n\t\tif ( nodes[oldParent].children.child1 == sibling )\n\t\t{\n\t\t\tnodes[oldParent].children.child1 = newParent;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tB2_ASSERT( nodes[oldParent].children.child2 == sibling );\n\t\t\tnodes[oldParent].children.child2 = newParent;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// The sibling was the root\n\t\ttree->root = newParent;\n\t}\n\n\t// Stage 3: walk back up the tree fixing heights and AABBs\n\tint index = nodes[leaf].parent;\n\twhile ( index != B2_NULL_INDEX )\n\t{\n\t\tint child1 = nodes[index].children.child1;\n\t\tint child2 = nodes[index].children.child2;\n\n\t\tB2_ASSERT( child1 != B2_NULL_INDEX );\n\t\tB2_ASSERT( child2 != B2_NULL_INDEX );\n\n\t\tnodes[index].aabb = b2AABB_Union( nodes[child1].aabb, nodes[child2].aabb );\n\t\tnodes[index].categoryBits = nodes[child1].categoryBits | nodes[child2].categoryBits;\n\t\tnodes[index].height = 1 + b2MaxUInt16( nodes[child1].height, nodes[child2].height );\n\t\tnodes[index].flags |= ( nodes[child1].flags | nodes[child2].flags ) & b2_enlargedNode;\n\n\t\tif ( shouldRotate )\n\t\t{\n\t\t\tb2RotateNodes( tree, index );\n\t\t}\n\n\t\tindex = nodes[index].parent;\n\t}\n}\n\nstatic void b2RemoveLeaf( b2DynamicTree* tree, int leaf )\n{\n\tif ( leaf == tree->root )\n\t{\n\t\ttree->root = B2_NULL_INDEX;\n\t\treturn;\n\t}\n\n\tb2TreeNode* nodes = tree->nodes;\n\n\tint parent = nodes[leaf].parent;\n\tint grandParent = nodes[parent].parent;\n\tint sibling;\n\tif ( nodes[parent].children.child1 == leaf )\n\t{\n\t\tsibling = nodes[parent].children.child2;\n\t}\n\telse\n\t{\n\t\tsibling = nodes[parent].children.child1;\n\t}\n\n\tif ( grandParent != B2_NULL_INDEX )\n\t{\n\t\t// Destroy parent and connect sibling to grandParent.\n\t\tif ( nodes[grandParent].children.child1 == parent )\n\t\t{\n\t\t\tnodes[grandParent].children.child1 = sibling;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnodes[grandParent].children.child2 = sibling;\n\t\t}\n\t\tnodes[sibling].parent = grandParent;\n\t\tb2FreeNode( tree, parent );\n\n\t\t// Adjust ancestor bounds.\n\t\tint index = grandParent;\n\t\twhile ( index != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2TreeNode* node = nodes + index;\n\t\t\tb2TreeNode* child1 = nodes + node->children.child1;\n\t\t\tb2TreeNode* child2 = nodes + node->children.child2;\n\n\t\t\t// Fast union using SSE\n\t\t\t//__m128 aabb1 = _mm_load_ps(&child1->aabb.lowerBound.x);\n\t\t\t//__m128 aabb2 = _mm_load_ps(&child2->aabb.lowerBound.x);\n\t\t\t//__m128 lower = _mm_min_ps(aabb1, aabb2);\n\t\t\t//__m128 upper = _mm_max_ps(aabb1, aabb2);\n\t\t\t//__m128 aabb = _mm_shuffle_ps(lower, upper, _MM_SHUFFLE(3, 2, 1, 0));\n\t\t\t//_mm_store_ps(&node->aabb.lowerBound.x, aabb);\n\n\t\t\tnode->aabb = b2AABB_Union( child1->aabb, child2->aabb );\n\t\t\tnode->categoryBits = child1->categoryBits | child2->categoryBits;\n\t\t\tnode->height = 1 + b2MaxUInt16( child1->height, child2->height );\n\n\t\t\tindex = node->parent;\n\t\t}\n\t}\n\telse\n\t{\n\t\ttree->root = sibling;\n\t\ttree->nodes[sibling].parent = B2_NULL_INDEX;\n\t\tb2FreeNode( tree, parent );\n\t}\n}\n\n// Create a proxy in the tree as a leaf node. We return the index of the node instead of a pointer so that we can grow\n// the node pool.\nint b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, uint64_t userData )\n{\n\tB2_ASSERT( -B2_HUGE < aabb.lowerBound.x && aabb.lowerBound.x < B2_HUGE );\n\tB2_ASSERT( -B2_HUGE < aabb.lowerBound.y && aabb.lowerBound.y < B2_HUGE );\n\tB2_ASSERT( -B2_HUGE < aabb.upperBound.x && aabb.upperBound.x < B2_HUGE );\n\tB2_ASSERT( -B2_HUGE < aabb.upperBound.y && aabb.upperBound.y < B2_HUGE );\n\n\tint proxyId = b2AllocateNode( tree );\n\tb2TreeNode* node = tree->nodes + proxyId;\n\n\tnode->aabb = aabb;\n\tnode->userData = userData;\n\tnode->categoryBits = categoryBits;\n\tnode->height = 0;\n\tnode->flags = b2_allocatedNode | b2_leafNode;\n\n\tbool shouldRotate = true;\n\tb2InsertLeaf( tree, proxyId, shouldRotate );\n\n\ttree->proxyCount += 1;\n\n\treturn proxyId;\n}\n\nvoid b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int proxyId )\n{\n\tB2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );\n\tB2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) );\n\n\tb2RemoveLeaf( tree, proxyId );\n\tb2FreeNode( tree, proxyId );\n\n\tB2_ASSERT( tree->proxyCount > 0 );\n\ttree->proxyCount -= 1;\n}\n\nint b2DynamicTree_GetProxyCount( const b2DynamicTree* tree )\n{\n\treturn tree->proxyCount;\n}\n\nvoid b2DynamicTree_MoveProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb )\n{\n\tB2_ASSERT( b2IsValidAABB( aabb ) );\n\tB2_ASSERT( aabb.upperBound.x - aabb.lowerBound.x < B2_HUGE );\n\tB2_ASSERT( aabb.upperBound.y - aabb.lowerBound.y < B2_HUGE );\n\tB2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );\n\tB2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) );\n\n\tb2RemoveLeaf( tree, proxyId );\n\n\ttree->nodes[proxyId].aabb = aabb;\n\n\tbool shouldRotate = false;\n\tb2InsertLeaf( tree, proxyId, shouldRotate );\n}\n\nvoid b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb )\n{\n\tb2TreeNode* nodes = tree->nodes;\n\n\tB2_ASSERT( b2IsValidAABB( aabb ) );\n\tB2_ASSERT( aabb.upperBound.x - aabb.lowerBound.x < B2_HUGE );\n\tB2_ASSERT( aabb.upperBound.y - aabb.lowerBound.y < B2_HUGE );\n\tB2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );\n\tB2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) );\n\n\t// Caller must ensure this\n\tB2_ASSERT( b2AABB_Contains( nodes[proxyId].aabb, aabb ) == false );\n\n\tnodes[proxyId].aabb = aabb;\n\n\tint parentIndex = nodes[proxyId].parent;\n\twhile ( parentIndex != B2_NULL_INDEX )\n\t{\n\t\tbool changed = b2EnlargeAABB( &nodes[parentIndex].aabb, aabb );\n\t\tnodes[parentIndex].flags |= b2_enlargedNode;\n\t\tparentIndex = nodes[parentIndex].parent;\n\n\t\tif ( changed == false )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\twhile ( parentIndex != B2_NULL_INDEX )\n\t{\n\t\tif ( nodes[parentIndex].flags & b2_enlargedNode )\n\t\t{\n\t\t\t// early out because this ancestor was previously ascended and marked as enlarged\n\t\t\tbreak;\n\t\t}\n\n\t\tnodes[parentIndex].flags |= b2_enlargedNode;\n\t\tparentIndex = nodes[parentIndex].parent;\n\t}\n}\n\nvoid b2DynamicTree_SetCategoryBits( b2DynamicTree* tree, int proxyId, uint64_t categoryBits )\n{\n\tb2TreeNode* nodes = tree->nodes;\n\n\tB2_ASSERT( nodes[proxyId].children.child1 == B2_NULL_INDEX );\n\tB2_ASSERT( nodes[proxyId].children.child2 == B2_NULL_INDEX );\n\tB2_ASSERT( ( nodes[proxyId].flags & b2_leafNode ) == b2_leafNode );\n\n\tnodes[proxyId].categoryBits = categoryBits;\n\n\t// Fix up category bits in ancestor internal nodes\n\tint nodeIndex = nodes[proxyId].parent;\n\twhile ( nodeIndex != B2_NULL_INDEX )\n\t{\n\t\tb2TreeNode* node = nodes + nodeIndex;\n\t\tint child1 = node->children.child1;\n\t\tB2_ASSERT( child1 != B2_NULL_INDEX );\n\t\tint child2 = node->children.child2;\n\t\tB2_ASSERT( child2 != B2_NULL_INDEX );\n\t\tnode->categoryBits = nodes[child1].categoryBits | nodes[child2].categoryBits;\n\n\t\tnodeIndex = node->parent;\n\t}\n}\n\nuint64_t b2DynamicTree_GetCategoryBits( b2DynamicTree* tree, int proxyId )\n{\n\tB2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );\n\treturn tree->nodes[proxyId].categoryBits;\n}\n\nint b2DynamicTree_GetHeight( const b2DynamicTree* tree )\n{\n\tif ( tree->root == B2_NULL_INDEX )\n\t{\n\t\treturn 0;\n\t}\n\n\treturn tree->nodes[tree->root].height;\n}\n\nfloat b2DynamicTree_GetAreaRatio( const b2DynamicTree* tree )\n{\n\tif ( tree->root == B2_NULL_INDEX )\n\t{\n\t\treturn 0.0f;\n\t}\n\n\tconst b2TreeNode* root = tree->nodes + tree->root;\n\tfloat rootArea = b2Perimeter( root->aabb );\n\n\tfloat totalArea = 0.0f;\n\tfor ( int i = 0; i < tree->nodeCapacity; ++i )\n\t{\n\t\tconst b2TreeNode* node = tree->nodes + i;\n\t\tif ( b2IsAllocated( node ) == false || b2IsLeaf( node ) || i == tree->root )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\ttotalArea += b2Perimeter( node->aabb );\n\t}\n\n\treturn totalArea / rootArea;\n}\n\nb2AABB b2DynamicTree_GetRootBounds( const b2DynamicTree* tree )\n{\n\tif ( tree->root != B2_NULL_INDEX )\n\t{\n\t\treturn tree->nodes[tree->root].aabb;\n\t}\n\n\tb2AABB empty = { b2Vec2_zero, b2Vec2_zero };\n\treturn empty;\n}\n\n#if B2_VALIDATE\n// Compute the height of a sub-tree.\nstatic int b2ComputeHeight( const b2DynamicTree* tree, int nodeId )\n{\n\tB2_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity );\n\tb2TreeNode* node = tree->nodes + nodeId;\n\n\tif ( b2IsLeaf( node ) )\n\t{\n\t\treturn 0;\n\t}\n\n\tint height1 = b2ComputeHeight( tree, node->children.child1 );\n\tint height2 = b2ComputeHeight( tree, node->children.child2 );\n\treturn 1 + b2MaxInt( height1, height2 );\n}\n\nstatic void b2ValidateStructure( const b2DynamicTree* tree, int index )\n{\n\tif ( index == B2_NULL_INDEX )\n\t{\n\t\treturn;\n\t}\n\n\tif ( index == tree->root )\n\t{\n\t\tB2_ASSERT( tree->nodes[index].parent == B2_NULL_INDEX );\n\t}\n\n\tconst b2TreeNode* node = tree->nodes + index;\n\n\tB2_ASSERT( node->flags == 0 || ( node->flags & b2_allocatedNode ) != 0 );\n\n\tif ( b2IsLeaf( node ) )\n\t{\n\t\tB2_ASSERT( node->height == 0 );\n\t\treturn;\n\t}\n\n\tint child1 = node->children.child1;\n\tint child2 = node->children.child2;\n\n\tB2_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity );\n\tB2_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity );\n\n\tB2_ASSERT( tree->nodes[child1].parent == index );\n\tB2_ASSERT( tree->nodes[child2].parent == index );\n\n\tif ( ( tree->nodes[child1].flags | tree->nodes[child2].flags ) & b2_enlargedNode )\n\t{\n\t\tB2_ASSERT( node->flags & b2_enlargedNode );\n\t}\n\n\tb2ValidateStructure( tree, child1 );\n\tb2ValidateStructure( tree, child2 );\n}\n\nstatic void b2ValidateMetrics( const b2DynamicTree* tree, int index )\n{\n\tif ( index == B2_NULL_INDEX )\n\t{\n\t\treturn;\n\t}\n\n\tconst b2TreeNode* node = tree->nodes + index;\n\n\tif ( b2IsLeaf( node ) )\n\t{\n\t\tB2_ASSERT( node->height == 0 );\n\t\treturn;\n\t}\n\n\tint child1 = node->children.child1;\n\tint child2 = node->children.child2;\n\n\tB2_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity );\n\tB2_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity );\n\n\tint height1 = tree->nodes[child1].height;\n\tint height2 = tree->nodes[child2].height;\n\tint height = 1 + b2MaxInt( height1, height2 );\n\tB2_ASSERT( node->height == height );\n\n\t// b2AABB aabb = b2AABB_Union(tree->nodes[child1].aabb, tree->nodes[child2].aabb);\n\n\tB2_ASSERT( b2AABB_Contains( node->aabb, tree->nodes[child1].aabb ) );\n\tB2_ASSERT( b2AABB_Contains( node->aabb, tree->nodes[child2].aabb ) );\n\n\t// B2_ASSERT(aabb.lowerBound.x == node->aabb.lowerBound.x);\n\t// B2_ASSERT(aabb.lowerBound.y == node->aabb.lowerBound.y);\n\t// B2_ASSERT(aabb.upperBound.x == node->aabb.upperBound.x);\n\t// B2_ASSERT(aabb.upperBound.y == node->aabb.upperBound.y);\n\n\tuint64_t categoryBits = tree->nodes[child1].categoryBits | tree->nodes[child2].categoryBits;\n\tB2_ASSERT( node->categoryBits == categoryBits );\n\n\tb2ValidateMetrics( tree, child1 );\n\tb2ValidateMetrics( tree, child2 );\n}\n#endif\n\nvoid b2DynamicTree_Validate( const b2DynamicTree* tree )\n{\n#if B2_VALIDATE\n\tif ( tree->root == B2_NULL_INDEX )\n\t{\n\t\treturn;\n\t}\n\n\tb2ValidateStructure( tree, tree->root );\n\tb2ValidateMetrics( tree, tree->root );\n\n\tint freeCount = 0;\n\tint freeIndex = tree->freeList;\n\twhile ( freeIndex != B2_NULL_INDEX )\n\t{\n\t\tB2_ASSERT( 0 <= freeIndex && freeIndex < tree->nodeCapacity );\n\t\tfreeIndex = tree->nodes[freeIndex].next;\n\t\t++freeCount;\n\t}\n\n\tint height = b2DynamicTree_GetHeight( tree );\n\tint computedHeight = b2ComputeHeight( tree, tree->root );\n\tB2_ASSERT( height == computedHeight );\n\n\tB2_ASSERT( tree->nodeCount + freeCount == tree->nodeCapacity );\n#else\n\tB2_UNUSED( tree );\n#endif\n}\n\nvoid b2DynamicTree_ValidateNoEnlarged( const b2DynamicTree* tree )\n{\n#if B2_VALIDATE == 1\n\tint capacity = tree->nodeCapacity;\n\tconst b2TreeNode* nodes = tree->nodes;\n\tfor ( int i = 0; i < capacity; ++i )\n\t{\n\t\tconst b2TreeNode* node = nodes + i;\n\t\tif ( node->flags & b2_allocatedNode )\n\t\t{\n\t\t\tB2_ASSERT( ( node->flags & b2_enlargedNode ) == 0 );\n\t\t}\n\t}\n#else\n\tB2_UNUSED( tree );\n#endif\n}\n\nint b2DynamicTree_GetByteCount( const b2DynamicTree* tree )\n{\n\tsize_t size = sizeof( b2DynamicTree ) + sizeof( b2TreeNode ) * tree->nodeCapacity +\n\t\t\t\t  tree->rebuildCapacity * ( sizeof( int ) + sizeof( b2AABB ) + sizeof( b2Vec2 ) + sizeof( int ) );\n\n\treturn (int)size;\n}\n\nuint64_t b2DynamicTree_GetUserData( const b2DynamicTree* tree, int proxyId )\n{\n\tB2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );\n\treturn tree->nodes[proxyId].userData;\n}\n\nb2AABB b2DynamicTree_GetAABB( const b2DynamicTree* tree, int proxyId )\n{\n\tB2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );\n\treturn tree->nodes[proxyId].aabb;\n}\n\nb2TreeStats b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits, b2TreeQueryCallbackFcn* callback,\n\t\t\t\t\t\t\t\t void* context )\n{\n\tb2TreeStats result = { 0 };\n\n\tif ( tree->nodeCount == 0 )\n\t{\n\t\treturn result;\n\t}\n\n\tint stack[B2_TREE_STACK_SIZE];\n\tint stackCount = 0;\n\tstack[stackCount++] = tree->root;\n\n\twhile ( stackCount > 0 )\n\t{\n\t\tint nodeId = stack[--stackCount];\n\n\t\tconst b2TreeNode* node = tree->nodes + nodeId;\n\t\tresult.nodeVisits += 1;\n\n\t\tif ( b2AABB_Overlaps( node->aabb, aabb ) && ( node->categoryBits & maskBits ) != 0 )\n\t\t{\n\t\t\tif ( b2IsLeaf( node ) )\n\t\t\t{\n\t\t\t\t// callback to user code with proxy id\n\t\t\t\tbool proceed = callback( nodeId, node->userData, context );\n\t\t\t\tresult.leafVisits += 1;\n\n\t\t\t\tif ( proceed == false )\n\t\t\t\t{\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ( stackCount < B2_TREE_STACK_SIZE - 1 )\n\t\t\t\t{\n\t\t\t\t\tstack[stackCount++] = node->children.child1;\n\t\t\t\t\tstack[stackCount++] = node->children.child2;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tB2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\nb2TreeStats b2DynamicTree_QueryAll( const b2DynamicTree* tree, b2AABB aabb, b2TreeQueryCallbackFcn* callback, void* context )\n{\n\tb2TreeStats result = { 0 };\n\n\tif ( tree->nodeCount == 0 )\n\t{\n\t\treturn result;\n\t}\n\n\tint stack[B2_TREE_STACK_SIZE];\n\tint stackCount = 0;\n\tstack[stackCount++] = tree->root;\n\n\twhile ( stackCount > 0 )\n\t{\n\t\tint nodeId = stack[--stackCount];\n\n\t\tconst b2TreeNode* node = tree->nodes + nodeId;\n\t\tresult.nodeVisits += 1;\n\n\t\tif ( b2AABB_Overlaps( node->aabb, aabb ) )\n\t\t{\n\t\t\tif ( b2IsLeaf( node ) )\n\t\t\t{\n\t\t\t\t// callback to user code with proxy id\n\t\t\t\tbool proceed = callback( nodeId, node->userData, context );\n\t\t\t\tresult.leafVisits += 1;\n\n\t\t\t\tif ( proceed == false )\n\t\t\t\t{\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ( stackCount < B2_TREE_STACK_SIZE - 1 )\n\t\t\t\t{\n\t\t\t\t\tstack[stackCount++] = node->children.child1;\n\t\t\t\t\tstack[stackCount++] = node->children.child2;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tB2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\nb2TreeStats b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits,\n\t\t\t\t\t\t\t\t   b2TreeRayCastCallbackFcn* callback, void* context )\n{\n\tb2TreeStats result = { 0 };\n\n\tif ( tree->nodeCount == 0 )\n\t{\n\t\treturn result;\n\t}\n\n\tb2Vec2 p1 = input->origin;\n\tb2Vec2 d = input->translation;\n\n\tb2Vec2 r = b2Normalize( d );\n\n\t// v is perpendicular to the segment.\n\tb2Vec2 v = b2CrossSV( 1.0f, r );\n\tb2Vec2 abs_v = b2Abs( v );\n\n\t// Separating axis for segment (Gino, p80).\n\t// |dot(v, p1 - c)| > dot(|v|, h)\n\n\tfloat maxFraction = input->maxFraction;\n\n\tb2Vec2 p2 = b2MulAdd( p1, maxFraction, d );\n\n\t// Build a bounding box for the segment.\n\tb2AABB segmentAABB = { b2Min( p1, p2 ), b2Max( p1, p2 ) };\n\n\tint stack[B2_TREE_STACK_SIZE];\n\tint stackCount = 0;\n\tstack[stackCount++] = tree->root;\n\n\tconst b2TreeNode* nodes = tree->nodes;\n\n\tb2RayCastInput subInput = *input;\n\n\twhile ( stackCount > 0 )\n\t{\n\t\tint nodeId = stack[--stackCount];\n\t\tif ( nodeId == B2_NULL_INDEX )\n\t\t{\n\t\t\t// todo is this possible?\n\t\t\tB2_ASSERT( false );\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst b2TreeNode* node = nodes + nodeId;\n\t\tresult.nodeVisits += 1;\n\n\t\tb2AABB nodeAABB = node->aabb;\n\n\t\tif ( ( node->categoryBits & maskBits ) == 0 || b2AABB_Overlaps( nodeAABB, segmentAABB ) == false )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Separating axis for segment (Gino, p80).\n\t\t// |dot(v, p1 - c)| > dot(|v|, h)\n\t\t// radius extension is added to the node in this case\n\t\tb2Vec2 c = b2AABB_Center( nodeAABB );\n\t\tb2Vec2 h = b2AABB_Extents( nodeAABB );\n\t\tfloat term1 = b2AbsFloat( b2Dot( v, b2Sub( p1, c ) ) );\n\t\tfloat term2 = b2Dot( abs_v, h );\n\t\tif ( term2 < term1 )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ( b2IsLeaf( node ) )\n\t\t{\n\t\t\tsubInput.maxFraction = maxFraction;\n\n\t\t\tfloat value = callback( &subInput, nodeId, node->userData, context );\n\t\t\tresult.leafVisits += 1;\n\n\t\t\t// The user may return -1 to indicate this shape should be skipped\n\n\t\t\tif ( value == 0.0f )\n\t\t\t{\n\t\t\t\t// The client has terminated the ray cast.\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tif ( 0.0f < value && value <= maxFraction )\n\t\t\t{\n\t\t\t\t// Update segment bounding box.\n\t\t\t\tmaxFraction = value;\n\t\t\t\tp2 = b2MulAdd( p1, maxFraction, d );\n\t\t\t\tsegmentAABB.lowerBound = b2Min( p1, p2 );\n\t\t\t\tsegmentAABB.upperBound = b2Max( p1, p2 );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ( stackCount < B2_TREE_STACK_SIZE - 1 )\n\t\t\t{\n\t\t\t\tb2Vec2 c1 = b2AABB_Center( nodes[node->children.child1].aabb );\n\t\t\t\tb2Vec2 c2 = b2AABB_Center( nodes[node->children.child2].aabb );\n\t\t\t\tif ( b2DistanceSquared( c1, p1 ) < b2DistanceSquared( c2, p1 ) )\n\t\t\t\t{\n\t\t\t\t\tstack[stackCount++] = node->children.child2;\n\t\t\t\t\tstack[stackCount++] = node->children.child1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstack[stackCount++] = node->children.child1;\n\t\t\t\t\tstack[stackCount++] = node->children.child2;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tB2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\nb2TreeStats b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits,\n\t\t\t\t\t\t\t\t\t b2TreeShapeCastCallbackFcn* callback, void* context )\n{\n\tb2TreeStats stats = { 0 };\n\n\tif ( tree->nodeCount == 0 || input->proxy.count == 0 )\n\t{\n\t\treturn stats;\n\t}\n\n\tb2AABB originAABB = { input->proxy.points[0], input->proxy.points[0] };\n\tfor ( int i = 1; i < input->proxy.count; ++i )\n\t{\n\t\toriginAABB.lowerBound = b2Min( originAABB.lowerBound, input->proxy.points[i] );\n\t\toriginAABB.upperBound = b2Max( originAABB.upperBound, input->proxy.points[i] );\n\t}\n\n\tb2Vec2 radius = { input->proxy.radius, input->proxy.radius };\n\n\toriginAABB.lowerBound = b2Sub( originAABB.lowerBound, radius );\n\toriginAABB.upperBound = b2Add( originAABB.upperBound, radius );\n\n\tb2Vec2 p1 = b2AABB_Center( originAABB );\n\tb2Vec2 extension = b2AABB_Extents( originAABB );\n\n\t// v is perpendicular to the segment.\n\tb2Vec2 r = input->translation;\n\tb2Vec2 v = b2CrossSV( 1.0f, r );\n\tb2Vec2 abs_v = b2Abs( v );\n\n\t// Separating axis for segment (Gino, p80).\n\t// |dot(v, p1 - c)| > dot(|v|, h)\n\n\tfloat maxFraction = input->maxFraction;\n\n\t// Build total box for the shape cast\n\tb2Vec2 t = b2MulSV( maxFraction, input->translation );\n\tb2AABB totalAABB = {\n\t\tb2Min( originAABB.lowerBound, b2Add( originAABB.lowerBound, t ) ),\n\t\tb2Max( originAABB.upperBound, b2Add( originAABB.upperBound, t ) ),\n\t};\n\n\tb2ShapeCastInput subInput = *input;\n\tconst b2TreeNode* nodes = tree->nodes;\n\n\tint stack[B2_TREE_STACK_SIZE];\n\tint stackCount = 0;\n\tstack[stackCount++] = tree->root;\n\n\twhile ( stackCount > 0 )\n\t{\n\t\tint nodeId = stack[--stackCount];\n\t\tif ( nodeId == B2_NULL_INDEX )\n\t\t{\n\t\t\t// todo is this possible?\n\t\t\tB2_ASSERT( false );\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst b2TreeNode* node = nodes + nodeId;\n\t\tstats.nodeVisits += 1;\n\n\t\tif ( ( node->categoryBits & maskBits ) == 0 || b2AABB_Overlaps( node->aabb, totalAABB ) == false )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Separating axis for segment (Gino, p80).\n\t\t// |dot(v, p1 - c)| > dot(|v|, h)\n\t\t// radius extension is added to the node in this case\n\t\tb2Vec2 c = b2AABB_Center( node->aabb );\n\t\tb2Vec2 h = b2Add( b2AABB_Extents( node->aabb ), extension );\n\t\tfloat term1 = b2AbsFloat( b2Dot( v, b2Sub( p1, c ) ) );\n\t\tfloat term2 = b2Dot( abs_v, h );\n\t\tif ( term2 < term1 )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ( b2IsLeaf( node ) )\n\t\t{\n\t\t\tsubInput.maxFraction = maxFraction;\n\n\t\t\tfloat value = callback( &subInput, nodeId, node->userData, context );\n\t\t\tstats.leafVisits += 1;\n\n\t\t\tif ( value == 0.0f )\n\t\t\t{\n\t\t\t\t// The client has terminated the ray cast.\n\t\t\t\treturn stats;\n\t\t\t}\n\n\t\t\tif ( 0.0f < value && value < maxFraction )\n\t\t\t{\n\t\t\t\t// Update segment bounding box.\n\t\t\t\tmaxFraction = value;\n\t\t\t\tt = b2MulSV( maxFraction, input->translation );\n\t\t\t\ttotalAABB.lowerBound = b2Min( originAABB.lowerBound, b2Add( originAABB.lowerBound, t ) );\n\t\t\t\ttotalAABB.upperBound = b2Max( originAABB.upperBound, b2Add( originAABB.upperBound, t ) );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ( stackCount < B2_TREE_STACK_SIZE - 1 )\n\t\t\t{\n\t\t\t\tb2Vec2 c1 = b2AABB_Center( nodes[node->children.child1].aabb );\n\t\t\t\tb2Vec2 c2 = b2AABB_Center( nodes[node->children.child2].aabb );\n\t\t\t\tif ( b2DistanceSquared( c1, p1 ) < b2DistanceSquared( c2, p1 ) )\n\t\t\t\t{\n\t\t\t\t\tstack[stackCount++] = node->children.child2;\n\t\t\t\t\tstack[stackCount++] = node->children.child1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstack[stackCount++] = node->children.child1;\n\t\t\t\t\tstack[stackCount++] = node->children.child2;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tB2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );\n\t\t\t}\n\t\t}\n\t}\n\n\treturn stats;\n}\n\n// Median split == 0, Surface area heuristic == 1\n#define B2_TREE_HEURISTIC 0\n\n#if B2_TREE_HEURISTIC == 0\n\n// Median split heuristic\nstatic int b2PartitionMid( int* indices, b2Vec2* centers, int count )\n{\n\t// Handle trivial case\n\tif ( count <= 2 )\n\t{\n\t\treturn count / 2;\n\t}\n\n\tb2Vec2 lowerBound = centers[0];\n\tb2Vec2 upperBound = centers[0];\n\n\tfor ( int i = 1; i < count; ++i )\n\t{\n\t\tlowerBound = b2Min( lowerBound, centers[i] );\n\t\tupperBound = b2Max( upperBound, centers[i] );\n\t}\n\n\tb2Vec2 d = b2Sub( upperBound, lowerBound );\n\tb2Vec2 c = { 0.5f * ( lowerBound.x + upperBound.x ), 0.5f * ( lowerBound.y + upperBound.y ) };\n\n\t// Partition longest axis using the Hoare partition scheme\n\t// https://en.wikipedia.org/wiki/Quicksort\n\t// https://nicholasvadivelu.com/2021/01/11/array-partition/\n\tint i1 = 0, i2 = count;\n\tif ( d.x > d.y )\n\t{\n\t\tfloat pivot = c.x;\n\n\t\twhile ( i1 < i2 )\n\t\t{\n\t\t\twhile ( i1 < i2 && centers[i1].x < pivot )\n\t\t\t{\n\t\t\t\ti1 += 1;\n\t\t\t};\n\n\t\t\twhile ( i1 < i2 && centers[i2 - 1].x >= pivot )\n\t\t\t{\n\t\t\t\ti2 -= 1;\n\t\t\t};\n\n\t\t\tif ( i1 < i2 )\n\t\t\t{\n\t\t\t\t// Swap indices\n\t\t\t\t{\n\t\t\t\t\tint temp = indices[i1];\n\t\t\t\t\tindices[i1] = indices[i2 - 1];\n\t\t\t\t\tindices[i2 - 1] = temp;\n\t\t\t\t}\n\n\t\t\t\t// Swap centers\n\t\t\t\t{\n\t\t\t\t\tb2Vec2 temp = centers[i1];\n\t\t\t\t\tcenters[i1] = centers[i2 - 1];\n\t\t\t\t\tcenters[i2 - 1] = temp;\n\t\t\t\t}\n\n\t\t\t\ti1 += 1;\n\t\t\t\ti2 -= 1;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfloat pivot = c.y;\n\n\t\twhile ( i1 < i2 )\n\t\t{\n\t\t\twhile ( i1 < i2 && centers[i1].y < pivot )\n\t\t\t{\n\t\t\t\ti1 += 1;\n\t\t\t};\n\n\t\t\twhile ( i1 < i2 && centers[i2 - 1].y >= pivot )\n\t\t\t{\n\t\t\t\ti2 -= 1;\n\t\t\t};\n\n\t\t\tif ( i1 < i2 )\n\t\t\t{\n\t\t\t\t// Swap indices\n\t\t\t\t{\n\t\t\t\t\tint temp = indices[i1];\n\t\t\t\t\tindices[i1] = indices[i2 - 1];\n\t\t\t\t\tindices[i2 - 1] = temp;\n\t\t\t\t}\n\n\t\t\t\t// Swap centers\n\t\t\t\t{\n\t\t\t\t\tb2Vec2 temp = centers[i1];\n\t\t\t\t\tcenters[i1] = centers[i2 - 1];\n\t\t\t\t\tcenters[i2 - 1] = temp;\n\t\t\t\t}\n\n\t\t\t\ti1 += 1;\n\t\t\t\ti2 -= 1;\n\t\t\t}\n\t\t}\n\t}\n\tB2_ASSERT( i1 == i2 );\n\n\tif ( i1 > 0 && i1 < count )\n\t{\n\t\treturn i1;\n\t}\n\n\treturn count / 2;\n}\n\n#else\n\n#define B2_BIN_COUNT 8\n\ntypedef struct b2TreeBin\n{\n\tb2AABB aabb;\n\tint count;\n} b2TreeBin;\n\ntypedef struct b2TreePlane\n{\n\tb2AABB leftAABB;\n\tb2AABB rightAABB;\n\tint leftCount;\n\tint rightCount;\n} b2TreePlane;\n\n// \"On Fast Construction of SAH-based Bounding Volume Hierarchies\" by Ingo Wald\n// Returns the left child count\nstatic int b2PartitionSAH( int* indices, int* binIndices, b2AABB* boxes, int count )\n{\n\tB2_ASSERT( count > 0 );\n\n\tb2TreeBin bins[B2_BIN_COUNT];\n\tb2TreePlane planes[B2_BIN_COUNT - 1];\n\n\tb2Vec2 center = b2AABB_Center( boxes[0] );\n\tb2AABB centroidAABB;\n\tcentroidAABB.lowerBound = center;\n\tcentroidAABB.upperBound = center;\n\n\tfor ( int i = 1; i < count; ++i )\n\t{\n\t\tcenter = b2AABB_Center( boxes[i] );\n\t\tcentroidAABB.lowerBound = b2Min( centroidAABB.lowerBound, center );\n\t\tcentroidAABB.upperBound = b2Max( centroidAABB.upperBound, center );\n\t}\n\n\tb2Vec2 d = b2Sub( centroidAABB.upperBound, centroidAABB.lowerBound );\n\n\t// Find longest axis\n\tint axisIndex;\n\tfloat invD;\n\tif ( d.x > d.y )\n\t{\n\t\taxisIndex = 0;\n\t\tinvD = d.x;\n\t}\n\telse\n\t{\n\t\taxisIndex = 1;\n\t\tinvD = d.y;\n\t}\n\n\tinvD = invD > 0.0f ? 1.0f / invD : 0.0f;\n\n\t// Initialize bin bounds and count\n\tfor ( int i = 0; i < B2_BIN_COUNT; ++i )\n\t{\n\t\tbins[i].aabb.lowerBound = (b2Vec2){ FLT_MAX, FLT_MAX };\n\t\tbins[i].aabb.upperBound = (b2Vec2){ -FLT_MAX, -FLT_MAX };\n\t\tbins[i].count = 0;\n\t}\n\n\t// Assign boxes to bins and compute bin boxes\n\t// TODO_ERIN optimize\n\tfloat binCount = B2_BIN_COUNT;\n\tfloat lowerBoundArray[2] = { centroidAABB.lowerBound.x, centroidAABB.lowerBound.y };\n\tfloat minC = lowerBoundArray[axisIndex];\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tb2Vec2 c = b2AABB_Center( boxes[i] );\n\t\tfloat cArray[2] = { c.x, c.y };\n\t\tint binIndex = (int)( binCount * ( cArray[axisIndex] - minC ) * invD );\n\t\tbinIndex = b2ClampInt( binIndex, 0, B2_BIN_COUNT - 1 );\n\t\tbinIndices[i] = binIndex;\n\t\tbins[binIndex].count += 1;\n\t\tbins[binIndex].aabb = b2AABB_Union( bins[binIndex].aabb, boxes[i] );\n\t}\n\n\tint planeCount = B2_BIN_COUNT - 1;\n\n\t// Prepare all the left planes, candidates for left child\n\tplanes[0].leftCount = bins[0].count;\n\tplanes[0].leftAABB = bins[0].aabb;\n\tfor ( int i = 1; i < planeCount; ++i )\n\t{\n\t\tplanes[i].leftCount = planes[i - 1].leftCount + bins[i].count;\n\t\tplanes[i].leftAABB = b2AABB_Union( planes[i - 1].leftAABB, bins[i].aabb );\n\t}\n\n\t// Prepare all the right planes, candidates for right child\n\tplanes[planeCount - 1].rightCount = bins[planeCount].count;\n\tplanes[planeCount - 1].rightAABB = bins[planeCount].aabb;\n\tfor ( int i = planeCount - 2; i >= 0; --i )\n\t{\n\t\tplanes[i].rightCount = planes[i + 1].rightCount + bins[i + 1].count;\n\t\tplanes[i].rightAABB = b2AABB_Union( planes[i + 1].rightAABB, bins[i + 1].aabb );\n\t}\n\n\t// Find best split to minimize SAH\n\tfloat minCost = FLT_MAX;\n\tint bestPlane = 0;\n\tfor ( int i = 0; i < planeCount; ++i )\n\t{\n\t\tfloat leftArea = b2Perimeter( planes[i].leftAABB );\n\t\tfloat rightArea = b2Perimeter( planes[i].rightAABB );\n\t\tint leftCount = planes[i].leftCount;\n\t\tint rightCount = planes[i].rightCount;\n\n\t\tfloat cost = leftCount * leftArea + rightCount * rightArea;\n\t\tif ( cost < minCost )\n\t\t{\n\t\t\tbestPlane = i;\n\t\t\tminCost = cost;\n\t\t}\n\t}\n\n\t// Partition node indices and boxes using the Hoare partition scheme\n\t// https://en.wikipedia.org/wiki/Quicksort\n\t// https://nicholasvadivelu.com/2021/01/11/array-partition/\n\tint i1 = 0, i2 = count;\n\twhile ( i1 < i2 )\n\t{\n\t\twhile ( i1 < i2 && binIndices[i1] < bestPlane )\n\t\t{\n\t\t\ti1 += 1;\n\t\t};\n\n\t\twhile ( i1 < i2 && binIndices[i2 - 1] >= bestPlane )\n\t\t{\n\t\t\ti2 -= 1;\n\t\t};\n\n\t\tif ( i1 < i2 )\n\t\t{\n\t\t\t// Swap indices\n\t\t\t{\n\t\t\t\tint temp = indices[i1];\n\t\t\t\tindices[i1] = indices[i2 - 1];\n\t\t\t\tindices[i2 - 1] = temp;\n\t\t\t}\n\n\t\t\t// Swap boxes\n\t\t\t{\n\t\t\t\tb2AABB temp = boxes[i1];\n\t\t\t\tboxes[i1] = boxes[i2 - 1];\n\t\t\t\tboxes[i2 - 1] = temp;\n\t\t\t}\n\n\t\t\ti1 += 1;\n\t\t\ti2 -= 1;\n\t\t}\n\t}\n\tB2_ASSERT( i1 == i2 );\n\n\tif ( i1 > 0 && i1 < count )\n\t{\n\t\treturn i1;\n\t}\n\telse\n\t{\n\t\treturn count / 2;\n\t}\n}\n\n#endif\n\n// Temporary data used to track the rebuild of a tree node\nstruct b2RebuildItem\n{\n\tint nodeIndex;\n\tint childCount;\n\n\t// Leaf indices\n\tint startIndex;\n\tint splitIndex;\n\tint endIndex;\n};\n\n// Returns root node index\nstatic int b2BuildTree( b2DynamicTree* tree, int leafCount )\n{\n\tb2TreeNode* nodes = tree->nodes;\n\tint* leafIndices = tree->leafIndices;\n\n\tif ( leafCount == 1 )\n\t{\n\t\tnodes[leafIndices[0]].parent = B2_NULL_INDEX;\n\t\treturn leafIndices[0];\n\t}\n\n#if B2_TREE_HEURISTIC == 0\n\tb2Vec2* leafCenters = tree->leafCenters;\n#else\n\tb2AABB* leafBoxes = tree->leafBoxes;\n\tint* binIndices = tree->binIndices;\n#endif\n\n\t// todo large stack item\n\tstruct b2RebuildItem stack[B2_TREE_STACK_SIZE];\n\tint top = 0;\n\n\tstack[0].nodeIndex = b2AllocateNode( tree );\n\tstack[0].childCount = -1;\n\tstack[0].startIndex = 0;\n\tstack[0].endIndex = leafCount;\n#if B2_TREE_HEURISTIC == 0\n\tstack[0].splitIndex = b2PartitionMid( leafIndices, leafCenters, leafCount );\n#else\n\tstack[0].splitIndex = b2PartitionSAH( leafIndices, binIndices, leafBoxes, leafCount );\n#endif\n\n\twhile ( true )\n\t{\n\t\tstruct b2RebuildItem* item = stack + top;\n\n\t\titem->childCount += 1;\n\n\t\tif ( item->childCount == 2 )\n\t\t{\n\t\t\t// This internal node has both children established\n\n\t\t\tif ( top == 0 )\n\t\t\t{\n\t\t\t\t// all done\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tstruct b2RebuildItem* parentItem = stack + ( top - 1 );\n\t\t\tb2TreeNode* parentNode = nodes + parentItem->nodeIndex;\n\n\t\t\tif ( parentItem->childCount == 0 )\n\t\t\t{\n\t\t\t\tB2_ASSERT( parentNode->children.child1 == B2_NULL_INDEX );\n\t\t\t\tparentNode->children.child1 = item->nodeIndex;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tB2_ASSERT( parentItem->childCount == 1 );\n\t\t\t\tB2_ASSERT( parentNode->children.child2 == B2_NULL_INDEX );\n\t\t\t\tparentNode->children.child2 = item->nodeIndex;\n\t\t\t}\n\n\t\t\tb2TreeNode* node = nodes + item->nodeIndex;\n\n\t\t\tB2_ASSERT( node->parent == B2_NULL_INDEX );\n\t\t\tnode->parent = parentItem->nodeIndex;\n\n\t\t\tB2_ASSERT( node->children.child1 != B2_NULL_INDEX );\n\t\t\tB2_ASSERT( node->children.child2 != B2_NULL_INDEX );\n\t\t\tb2TreeNode* child1 = nodes + node->children.child1;\n\t\t\tb2TreeNode* child2 = nodes + node->children.child2;\n\n\t\t\tnode->aabb = b2AABB_Union( child1->aabb, child2->aabb );\n\t\t\tnode->height = 1 + b2MaxUInt16( child1->height, child2->height );\n\t\t\tnode->categoryBits = child1->categoryBits | child2->categoryBits;\n\n\t\t\t// Pop stack\n\t\t\ttop -= 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint startIndex, endIndex;\n\t\t\tif ( item->childCount == 0 )\n\t\t\t{\n\t\t\t\tstartIndex = item->startIndex;\n\t\t\t\tendIndex = item->splitIndex;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tB2_ASSERT( item->childCount == 1 );\n\t\t\t\tstartIndex = item->splitIndex;\n\t\t\t\tendIndex = item->endIndex;\n\t\t\t}\n\n\t\t\tint count = endIndex - startIndex;\n\n\t\t\tif ( count == 1 )\n\t\t\t{\n\t\t\t\tint childIndex = leafIndices[startIndex];\n\t\t\t\tb2TreeNode* node = nodes + item->nodeIndex;\n\n\t\t\t\tif ( item->childCount == 0 )\n\t\t\t\t{\n\t\t\t\t\tB2_ASSERT( node->children.child1 == B2_NULL_INDEX );\n\t\t\t\t\tnode->children.child1 = childIndex;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tB2_ASSERT( item->childCount == 1 );\n\t\t\t\t\tB2_ASSERT( node->children.child2 == B2_NULL_INDEX );\n\t\t\t\t\tnode->children.child2 = childIndex;\n\t\t\t\t}\n\n\t\t\t\tb2TreeNode* childNode = nodes + childIndex;\n\t\t\t\tB2_ASSERT( childNode->parent == B2_NULL_INDEX );\n\t\t\t\tchildNode->parent = item->nodeIndex;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tB2_ASSERT( count > 0 );\n\t\t\t\tB2_ASSERT( top < B2_TREE_STACK_SIZE );\n\n\t\t\t\ttop += 1;\n\t\t\t\tstruct b2RebuildItem* newItem = stack + top;\n\t\t\t\tnewItem->nodeIndex = b2AllocateNode( tree );\n\t\t\t\tnewItem->childCount = -1;\n\t\t\t\tnewItem->startIndex = startIndex;\n\t\t\t\tnewItem->endIndex = endIndex;\n#if B2_TREE_HEURISTIC == 0\n\t\t\t\tnewItem->splitIndex = b2PartitionMid( leafIndices + startIndex, leafCenters + startIndex, count );\n#else\n\t\t\t\tnewItem->splitIndex =\n\t\t\t\t\tb2PartitionSAH( leafIndices + startIndex, binIndices + startIndex, leafBoxes + startIndex, count );\n#endif\n\t\t\t\tnewItem->splitIndex += startIndex;\n\t\t\t}\n\t\t}\n\t}\n\n\tb2TreeNode* rootNode = nodes + stack[0].nodeIndex;\n\tB2_ASSERT( rootNode->parent == B2_NULL_INDEX );\n\tB2_ASSERT( rootNode->children.child1 != B2_NULL_INDEX );\n\tB2_ASSERT( rootNode->children.child2 != B2_NULL_INDEX );\n\n\tb2TreeNode* child1 = nodes + rootNode->children.child1;\n\tb2TreeNode* child2 = nodes + rootNode->children.child2;\n\n\trootNode->aabb = b2AABB_Union( child1->aabb, child2->aabb );\n\trootNode->height = 1 + b2MaxUInt16( child1->height, child2->height );\n\trootNode->categoryBits = child1->categoryBits | child2->categoryBits;\n\n\treturn stack[0].nodeIndex;\n}\n\n// Not safe to access tree during this operation because it may grow\nint b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild )\n{\n\tint proxyCount = tree->proxyCount;\n\tif ( proxyCount == 0 )\n\t{\n\t\treturn 0;\n\t}\n\n\t// Ensure capacity for rebuild space\n\tif ( proxyCount > tree->rebuildCapacity )\n\t{\n\t\tint newCapacity = proxyCount + proxyCount / 2;\n\n\t\tb2Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int ) );\n\t\ttree->leafIndices = b2Alloc( newCapacity * sizeof( int ) );\n\n#if B2_TREE_HEURISTIC == 0\n\t\tb2Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b2Vec2 ) );\n\t\ttree->leafCenters = b2Alloc( newCapacity * sizeof( b2Vec2 ) );\n#else\n\t\tb2Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b2AABB ) );\n\t\ttree->leafBoxes = b2Alloc( newCapacity * sizeof( b2AABB ) );\n\t\tb2Free( tree->binIndices, tree->rebuildCapacity * sizeof( int ) );\n\t\ttree->binIndices = b2Alloc( newCapacity * sizeof( int ) );\n#endif\n\t\ttree->rebuildCapacity = newCapacity;\n\t}\n\n\tint leafCount = 0;\n\tint stack[B2_TREE_STACK_SIZE];\n\tint stackCount = 0;\n\n\tint nodeIndex = tree->root;\n\tb2TreeNode* nodes = tree->nodes;\n\tb2TreeNode* node = nodes + nodeIndex;\n\n\t// These are the nodes that get sorted to rebuild the tree.\n\t// I'm using indices because the node pool may grow during the build.\n\tint* leafIndices = tree->leafIndices;\n\n#if B2_TREE_HEURISTIC == 0\n\tb2Vec2* leafCenters = tree->leafCenters;\n#else\n\tb2AABB* leafBoxes = tree->leafBoxes;\n#endif\n\n\t// Gather all proxy nodes that have grown and all internal nodes that haven't grown. Both are\n\t// considered leaves in the tree rebuild.\n\t// Free all internal nodes that have grown.\n\t// todo use a node growth metric instead of simply enlarged to reduce rebuild size and frequency\n\t// this should be weighed against B2_AABB_MARGIN\n\twhile ( true )\n\t{\n\t\tif ( node->height == 0 || ( ( node->flags & b2_enlargedNode ) == 0 && fullBuild == false ) )\n\t\t{\n\t\t\tleafIndices[leafCount] = nodeIndex;\n#if B2_TREE_HEURISTIC == 0\n\t\t\tleafCenters[leafCount] = b2AABB_Center( node->aabb );\n#else\n\t\t\tleafBoxes[leafCount] = node->aabb;\n#endif\n\t\t\tleafCount += 1;\n\n\t\t\t// Detach\n\t\t\tnode->parent = B2_NULL_INDEX;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint doomedNodeIndex = nodeIndex;\n\n\t\t\t// Handle children\n\t\t\tnodeIndex = node->children.child1;\n\n\t\t\tif ( stackCount < B2_TREE_STACK_SIZE )\n\t\t\t{\n\t\t\t\tstack[stackCount++] = node->children.child2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tB2_ASSERT( stackCount < B2_TREE_STACK_SIZE );\n\t\t\t}\n\n\t\t\tnode = nodes + nodeIndex;\n\n\t\t\t// Remove doomed node\n\t\t\tb2FreeNode( tree, doomedNodeIndex );\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ( stackCount == 0 )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tnodeIndex = stack[--stackCount];\n\t\tnode = nodes + nodeIndex;\n\t}\n\n#if B2_VALIDATE == 1\n\tint capacity = tree->nodeCapacity;\n\tfor ( int i = 0; i < capacity; ++i )\n\t{\n\t\tif ( nodes[i].flags & b2_allocatedNode )\n\t\t{\n\t\t\tB2_ASSERT( ( nodes[i].flags & b2_enlargedNode ) == 0 );\n\t\t}\n\t}\n#endif\n\n\tB2_ASSERT( leafCount <= proxyCount );\n\n\ttree->root = b2BuildTree( tree, leafCount );\n\n\tb2DynamicTree_Validate( tree );\n\n\treturn leafCount;\n}\n"
  },
  {
    "path": "src/geometry.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"constants.h\"\n#include \"shape.h\"\n\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n#include <stddef.h>\n\n_Static_assert( B2_MAX_POLYGON_VERTICES > 2, \"must be 3 or more\" );\n\nbool b2IsValidRay( const b2RayCastInput* input )\n{\n\tbool isValid = b2IsValidVec2( input->origin ) && b2IsValidVec2( input->translation ) &&\n\t\t\t\t   b2IsValidFloat( input->maxFraction ) && 0.0f <= input->maxFraction && input->maxFraction < B2_HUGE;\n\treturn isValid;\n}\n\nstatic b2Vec2 b2ComputePolygonCentroid( const b2Vec2* vertices, int count )\n{\n\tb2Vec2 center = { 0.0f, 0.0f };\n\tfloat area = 0.0f;\n\n\t// Get a reference point for forming triangles.\n\t// Use the first vertex to reduce round-off errors.\n\tb2Vec2 origin = vertices[0];\n\n\tconst float inv3 = 1.0f / 3.0f;\n\n\tfor ( int i = 1; i < count - 1; ++i )\n\t{\n\t\t// Triangle edges\n\t\tb2Vec2 e1 = b2Sub( vertices[i], origin );\n\t\tb2Vec2 e2 = b2Sub( vertices[i + 1], origin );\n\t\tfloat a = 0.5f * b2Cross( e1, e2 );\n\n\t\t// Area weighted centroid\n\t\tcenter = b2MulAdd( center, a * inv3, b2Add( e1, e2 ) );\n\t\tarea += a;\n\t}\n\n\tB2_ASSERT( area > FLT_EPSILON );\n\tfloat invArea = 1.0f / area;\n\tcenter.x *= invArea;\n\tcenter.y *= invArea;\n\n\t// Restore offset\n\tcenter = b2Add( origin, center );\n\n\treturn center;\n}\n\nb2Polygon b2MakePolygon( const b2Hull* hull, float radius )\n{\n\tB2_ASSERT( b2ValidateHull( hull ) );\n\n\tif ( hull->count < 3 )\n\t{\n\t\t// Handle a bad hull when assertions are disabled\n\t\treturn b2MakeSquare( 0.5f );\n\t}\n\n\tb2Polygon shape = { 0 };\n\tshape.count = hull->count;\n\tshape.radius = radius;\n\n\t// Copy vertices\n\tfor ( int i = 0; i < shape.count; ++i )\n\t{\n\t\tshape.vertices[i] = hull->points[i];\n\t}\n\n\t// Compute normals. Ensure the edges have non-zero length.\n\tfor ( int i = 0; i < shape.count; ++i )\n\t{\n\t\tint i1 = i;\n\t\tint i2 = i + 1 < shape.count ? i + 1 : 0;\n\t\tb2Vec2 edge = b2Sub( shape.vertices[i2], shape.vertices[i1] );\n\t\tB2_ASSERT( b2Dot( edge, edge ) > FLT_EPSILON * FLT_EPSILON );\n\t\tshape.normals[i] = b2Normalize( b2CrossVS( edge, 1.0f ) );\n\t}\n\n\tshape.centroid = b2ComputePolygonCentroid( shape.vertices, shape.count );\n\n\treturn shape;\n}\n\nb2Polygon b2MakeOffsetPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation )\n{\n\treturn b2MakeOffsetRoundedPolygon( hull, position, rotation, 0.0f );\n}\n\nb2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation, float radius )\n{\n\tB2_ASSERT( b2ValidateHull( hull ) );\n\n\tif ( hull->count < 3 )\n\t{\n\t\t// Handle a bad hull when assertions are disabled\n\t\treturn b2MakeSquare( 0.5f );\n\t}\n\n\tb2Transform transform = { position, rotation };\n\n\tb2Polygon shape = { 0 };\n\tshape.count = hull->count;\n\tshape.radius = radius;\n\n\t// Copy vertices\n\tfor ( int i = 0; i < shape.count; ++i )\n\t{\n\t\tshape.vertices[i] = b2TransformPoint( transform, hull->points[i] );\n\t}\n\n\t// Compute normals. Ensure the edges have non-zero length.\n\tfor ( int i = 0; i < shape.count; ++i )\n\t{\n\t\tint i1 = i;\n\t\tint i2 = i + 1 < shape.count ? i + 1 : 0;\n\t\tb2Vec2 edge = b2Sub( shape.vertices[i2], shape.vertices[i1] );\n\t\tB2_ASSERT( b2Dot( edge, edge ) > FLT_EPSILON * FLT_EPSILON );\n\t\tshape.normals[i] = b2Normalize( b2CrossVS( edge, 1.0f ) );\n\t}\n\n\tshape.centroid = b2ComputePolygonCentroid( shape.vertices, shape.count );\n\n\treturn shape;\n}\n\nb2Polygon b2MakeSquare( float halfWidth )\n{\n\treturn b2MakeBox( halfWidth, halfWidth );\n}\n\nb2Polygon b2MakeBox( float halfWidth, float halfHeight )\n{\n\tB2_ASSERT( b2IsValidFloat( halfWidth ) && halfWidth > 0.0f );\n\tB2_ASSERT( b2IsValidFloat( halfHeight ) && halfHeight > 0.0f );\n\n\tb2Polygon shape = { 0 };\n\tshape.count = 4;\n\tshape.vertices[0] = (b2Vec2){ -halfWidth, -halfHeight };\n\tshape.vertices[1] = (b2Vec2){ halfWidth, -halfHeight };\n\tshape.vertices[2] = (b2Vec2){ halfWidth, halfHeight };\n\tshape.vertices[3] = (b2Vec2){ -halfWidth, halfHeight };\n\tshape.normals[0] = (b2Vec2){ 0.0f, -1.0f };\n\tshape.normals[1] = (b2Vec2){ 1.0f, 0.0f };\n\tshape.normals[2] = (b2Vec2){ 0.0f, 1.0f };\n\tshape.normals[3] = (b2Vec2){ -1.0f, 0.0f };\n\tshape.radius = 0.0f;\n\tshape.centroid = b2Vec2_zero;\n\treturn shape;\n}\n\nb2Polygon b2MakeRoundedBox( float halfWidth, float halfHeight, float radius )\n{\n\tB2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f );\n\tb2Polygon shape = b2MakeBox( halfWidth, halfHeight );\n\tshape.radius = radius;\n\treturn shape;\n}\n\nb2Polygon b2MakeOffsetBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation )\n{\n\tb2Transform xf = { center, rotation };\n\n\tb2Polygon shape = { 0 };\n\tshape.count = 4;\n\tshape.vertices[0] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, -halfHeight } );\n\tshape.vertices[1] = b2TransformPoint( xf, (b2Vec2){ halfWidth, -halfHeight } );\n\tshape.vertices[2] = b2TransformPoint( xf, (b2Vec2){ halfWidth, halfHeight } );\n\tshape.vertices[3] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, halfHeight } );\n\tshape.normals[0] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, -1.0f } );\n\tshape.normals[1] = b2RotateVector( xf.q, (b2Vec2){ 1.0f, 0.0f } );\n\tshape.normals[2] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, 1.0f } );\n\tshape.normals[3] = b2RotateVector( xf.q, (b2Vec2){ -1.0f, 0.0f } );\n\tshape.radius = 0.0f;\n\tshape.centroid = xf.p;\n\treturn shape;\n}\n\nb2Polygon b2MakeOffsetRoundedBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation, float radius )\n{\n\tB2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f );\n\tb2Transform xf = { center, rotation };\n\n\tb2Polygon shape = { 0 };\n\tshape.count = 4;\n\tshape.vertices[0] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, -halfHeight } );\n\tshape.vertices[1] = b2TransformPoint( xf, (b2Vec2){ halfWidth, -halfHeight } );\n\tshape.vertices[2] = b2TransformPoint( xf, (b2Vec2){ halfWidth, halfHeight } );\n\tshape.vertices[3] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, halfHeight } );\n\tshape.normals[0] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, -1.0f } );\n\tshape.normals[1] = b2RotateVector( xf.q, (b2Vec2){ 1.0f, 0.0f } );\n\tshape.normals[2] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, 1.0f } );\n\tshape.normals[3] = b2RotateVector( xf.q, (b2Vec2){ -1.0f, 0.0f } );\n\tshape.radius = radius;\n\tshape.centroid = xf.p;\n\treturn shape;\n}\n\nb2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon )\n{\n\tb2Polygon p = *polygon;\n\n\tfor ( int i = 0; i < p.count; ++i )\n\t{\n\t\tp.vertices[i] = b2TransformPoint( transform, p.vertices[i] );\n\t\tp.normals[i] = b2RotateVector( transform.q, p.normals[i] );\n\t}\n\n\tp.centroid = b2TransformPoint( transform, p.centroid );\n\n\treturn p;\n}\n\nb2MassData b2ComputeCircleMass( const b2Circle* shape, float density )\n{\n\tfloat rr = shape->radius * shape->radius;\n\n\tb2MassData massData;\n\tmassData.mass = density * B2_PI * rr;\n\tmassData.center = shape->center;\n\n\t// inertia about the center of mass\n\tmassData.rotationalInertia = massData.mass * 0.5f * rr ;\n\n\treturn massData;\n}\n\nb2MassData b2ComputeCapsuleMass( const b2Capsule* shape, float density )\n{\n\tfloat radius = shape->radius;\n\tfloat rr = radius * radius;\n\tb2Vec2 p1 = shape->center1;\n\tb2Vec2 p2 = shape->center2;\n\tfloat length = b2Length( b2Sub( p2, p1 ) );\n\tfloat ll = length * length;\n\n\tfloat circleMass = density * ( B2_PI * radius * radius );\n\tfloat boxMass = density * ( 2.0f * radius * length );\n\n\tb2MassData massData;\n\tmassData.mass = circleMass + boxMass;\n\tmassData.center.x = 0.5f * ( p1.x + p2.x );\n\tmassData.center.y = 0.5f * ( p1.y + p2.y );\n\n\t// two offset half circles, both halves add up to full circle and each half is offset by half length\n\t// semi-circle centroid = 4 r / 3 pi\n\t// Need to apply parallel-axis theorem twice:\n\t// 1. shift semi-circle centroid to origin\n\t// 2. shift semi-circle to box end\n\t// m * ((h + lc)^2 - lc^2) = m * (h^2 + 2 * h * lc)\n\t// See: https://en.wikipedia.org/wiki/Parallel_axis_theorem\n\t// I verified this formula by computing the convex hull of a 128 vertex capsule\n\n\t// half circle centroid\n\tfloat lc = 4.0f * radius / ( 3.0f * B2_PI );\n\n\t// half length of rectangular portion of capsule\n\tfloat h = 0.5f * length;\n\n\tfloat circleInertia = circleMass * ( 0.5f * rr + h * h + 2.0f * h * lc );\n\tfloat boxInertia = boxMass * ( 4.0f * rr + ll ) / 12.0f;\n\tmassData.rotationalInertia = circleInertia + boxInertia;\n\n\treturn massData;\n}\n\nb2MassData b2ComputePolygonMass( const b2Polygon* shape, float density )\n{\n\t// Polygon mass, centroid, and inertia.\n\t// Let rho be the polygon density in mass per unit area.\n\t// Then:\n\t// mass = rho * int(dA)\n\t// centroid.x = (1/mass) * rho * int(x * dA)\n\t// centroid.y = (1/mass) * rho * int(y * dA)\n\t// I = rho * int((x*x + y*y) * dA)\n\t//\n\t// We can compute these integrals by summing all the integrals\n\t// for each triangle of the polygon. To evaluate the integral\n\t// for a single triangle, we make a change of variables to\n\t// the (u,v) coordinates of the triangle:\n\t// x = x0 + e1x * u + e2x * v\n\t// y = y0 + e1y * u + e2y * v\n\t// where 0 <= u && 0 <= v && u + v <= 1.\n\t//\n\t// We integrate u from [0,1-v] and then v from [0,1].\n\t// We also need to use the Jacobian of the transformation:\n\t// D = cross(e1, e2)\n\t//\n\t// Simplification: triangle centroid = (1/3) * (p1 + p2 + p3)\n\t//\n\t// The rest of the derivation is handled by computer algebra.\n\n\tB2_ASSERT( shape->count > 0 );\n\n\tif ( shape->count == 1 )\n\t{\n\t\tb2Circle circle;\n\t\tcircle.center = shape->vertices[0];\n\t\tcircle.radius = shape->radius;\n\t\treturn b2ComputeCircleMass( &circle, density );\n\t}\n\n\tif ( shape->count == 2 )\n\t{\n\t\tb2Capsule capsule;\n\t\tcapsule.center1 = shape->vertices[0];\n\t\tcapsule.center2 = shape->vertices[1];\n\t\tcapsule.radius = shape->radius;\n\t\treturn b2ComputeCapsuleMass( &capsule, density );\n\t}\n\n\tb2Vec2 vertices[B2_MAX_POLYGON_VERTICES] = { 0 };\n\tint count = shape->count;\n\tfloat radius = shape->radius;\n\n\tif ( radius > 0.0f )\n\t{\n\t\t// Approximate mass of rounded polygons by pushing out the vertices.\n\t\tfloat sqrt2 = 1.412f;\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tint j = i == 0 ? count - 1 : i - 1;\n\t\t\tb2Vec2 n1 = shape->normals[j];\n\t\t\tb2Vec2 n2 = shape->normals[i];\n\n\t\t\tb2Vec2 mid = b2Normalize( b2Add( n1, n2 ) );\n\t\t\tvertices[i] = b2MulAdd( shape->vertices[i], sqrt2 * radius, mid );\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tvertices[i] = shape->vertices[i];\n\t\t}\n\t}\n\n\tb2Vec2 center = { 0.0f, 0.0f };\n\tfloat area = 0.0f;\n\tfloat rotationalInertia = 0.0f;\n\n\t// Get a reference point for forming triangles.\n\t// Use the first vertex to reduce round-off errors.\n\tb2Vec2 r = vertices[0];\n\n\tconst float inv3 = 1.0f / 3.0f;\n\n\tfor ( int i = 1; i < count - 1; ++i )\n\t{\n\t\t// Triangle edges\n\t\tb2Vec2 e1 = b2Sub( vertices[i], r );\n\t\tb2Vec2 e2 = b2Sub( vertices[i + 1], r );\n\n\t\tfloat D = b2Cross( e1, e2 );\n\n\t\tfloat triangleArea = 0.5f * D;\n\t\tarea += triangleArea;\n\n\t\t// Area weighted centroid, r at origin\n\t\tcenter = b2MulAdd( center, triangleArea * inv3, b2Add( e1, e2 ) );\n\n\t\tfloat ex1 = e1.x, ey1 = e1.y;\n\t\tfloat ex2 = e2.x, ey2 = e2.y;\n\n\t\tfloat intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2;\n\t\tfloat inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2;\n\n\t\trotationalInertia += ( 0.25f * inv3 * D ) * ( intx2 + inty2 );\n\t}\n\n\tb2MassData massData;\n\n\t// Total mass\n\tmassData.mass = density * area;\n\n\t// Center of mass, shift back from origin at r\n\tB2_ASSERT( area > FLT_EPSILON );\n\tfloat invArea = 1.0f / area;\n\tcenter.x *= invArea;\n\tcenter.y *= invArea;\n\tmassData.center = b2Add( r, center );\n\n\t// Inertia tensor relative to the local origin (point s).\n\tmassData.rotationalInertia = density * rotationalInertia;\n\n\t// Shift inertia to center of mass\n\tmassData.rotationalInertia -= massData.mass * b2Dot( center, center );\n\n\t// If this goes negative we are hosed\n\tB2_ASSERT( massData.rotationalInertia >= 0.0f );\n\n\treturn massData;\n}\n\nb2AABB b2ComputeCircleAABB( const b2Circle* shape, b2Transform xf )\n{\n\tb2Vec2 p = b2TransformPoint( xf, shape->center );\n\tfloat r = shape->radius;\n\n\tb2AABB aabb = { { p.x - r, p.y - r }, { p.x + r, p.y + r } };\n\treturn aabb;\n}\n\nb2AABB b2ComputeCapsuleAABB( const b2Capsule* shape, b2Transform xf )\n{\n\tb2Vec2 v1 = b2TransformPoint( xf, shape->center1 );\n\tb2Vec2 v2 = b2TransformPoint( xf, shape->center2 );\n\n\tb2Vec2 r = { shape->radius, shape->radius };\n\tb2Vec2 lower = b2Sub( b2Min( v1, v2 ), r );\n\tb2Vec2 upper = b2Add( b2Max( v1, v2 ), r );\n\n\tb2AABB aabb = { lower, upper };\n\treturn aabb;\n}\n\nb2AABB b2ComputePolygonAABB( const b2Polygon* shape, b2Transform xf )\n{\n\tB2_ASSERT( shape->count > 0 );\n\tb2Vec2 lower = b2TransformPoint( xf, shape->vertices[0] );\n\tb2Vec2 upper = lower;\n\n\tfor ( int i = 1; i < shape->count; ++i )\n\t{\n\t\tb2Vec2 v = b2TransformPoint( xf, shape->vertices[i] );\n\t\tlower = b2Min( lower, v );\n\t\tupper = b2Max( upper, v );\n\t}\n\n\tb2Vec2 r = { shape->radius, shape->radius };\n\tlower = b2Sub( lower, r );\n\tupper = b2Add( upper, r );\n\n\tb2AABB aabb = { lower, upper };\n\treturn aabb;\n}\n\nb2AABB b2ComputeSegmentAABB( const b2Segment* shape, b2Transform xf )\n{\n\tb2Vec2 v1 = b2TransformPoint( xf, shape->point1 );\n\tb2Vec2 v2 = b2TransformPoint( xf, shape->point2 );\n\n\tb2Vec2 lower = b2Min( v1, v2 );\n\tb2Vec2 upper = b2Max( v1, v2 );\n\n\tb2AABB aabb = { lower, upper };\n\treturn aabb;\n}\n\nbool b2PointInCircle( const b2Circle* shape, b2Vec2 point )\n{\n\tb2Vec2 center = shape->center;\n\treturn b2DistanceSquared( point, center ) <= shape->radius * shape->radius;\n}\n\nbool b2PointInCapsule( const b2Capsule* shape, b2Vec2 point )\n{\n\tfloat rr = shape->radius * shape->radius;\n\tb2Vec2 p1 = shape->center1;\n\tb2Vec2 p2 = shape->center2;\n\n\tb2Vec2 d = b2Sub( p2, p1 );\n\tfloat dd = b2Dot( d, d );\n\tif ( dd == 0.0f )\n\t{\n\t\t// Capsule is really a circle\n\t\treturn b2DistanceSquared( point, p1 ) <= rr;\n\t}\n\n\t// Get closest point on capsule segment\n\t// c = p1 + t * d\n\t// dot(point - c, d) = 0\n\t// dot(point - p1 - t * d, d) = 0\n\t// t = dot(point - p1, d) / dot(d, d)\n\tfloat t = b2Dot( b2Sub( point, p1 ), d ) / dd;\n\tt = b2ClampFloat( t, 0.0f, 1.0f );\n\tb2Vec2 c = b2MulAdd( p1, t, d );\n\n\t// Is query point within radius around closest point?\n\treturn b2DistanceSquared( point, c ) <= rr;\n}\n\nbool b2PointInPolygon( const b2Polygon* shape, b2Vec2 point )\n{\n\tb2DistanceInput input = { 0 };\n\tinput.proxyA = b2MakeProxy( shape->vertices, shape->count, 0.0f );\n\tinput.proxyB = b2MakeProxy( &point, 1, 0.0f );\n\tinput.transformA = b2Transform_identity;\n\tinput.transformB = b2Transform_identity;\n\tinput.useRadii = false;\n\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput output = b2ShapeDistance( &input, &cache, NULL, 0 );\n\n\treturn output.distance <= shape->radius;\n}\n\n// Precision Improvements for Ray / Sphere Intersection - Ray Tracing Gems 2019\n// http://www.codercorner.com/blog/?p=321\nb2CastOutput b2RayCastCircle( const b2Circle* shape, const b2RayCastInput* input )\n{\n\tB2_ASSERT( b2IsValidRay( input ) );\n\n\tb2Vec2 p = shape->center;\n\n\tb2CastOutput output = { 0 };\n\n\t// Shift ray so circle center is the origin\n\tb2Vec2 s = b2Sub( input->origin, p );\n\n\tfloat r = shape->radius;\n\tfloat rr = r * r;\n\n\tfloat length;\n\tb2Vec2 d = b2GetLengthAndNormalize( &length, input->translation );\n\tif ( length == 0.0f )\n\t{\n\t\t// zero length ray\n\n\t\tif ( b2LengthSquared( s ) < rr )\n\t\t{\n\t\t\t// initial overlap\n\t\t\toutput.point = input->origin;\n\t\t\toutput.hit = true;\n\t\t}\n\n\t\treturn output;\n\t}\n\n\t// Find closest point on ray to origin\n\n\t// solve: dot(s + t * d, d) = 0\n\tfloat t = -b2Dot( s, d );\n\n\t// c is the closest point on the line to the origin\n\tb2Vec2 c = b2MulAdd( s, t, d );\n\n\tfloat cc = b2Dot( c, c );\n\n\tif ( cc > rr )\n\t{\n\t\t// closest point is outside the circle\n\t\treturn output;\n\t}\n\n\t// Pythagoras\n\tfloat h = sqrtf( rr - cc );\n\n\tfloat fraction = t - h;\n\n\tif ( fraction < 0.0f || input->maxFraction * length < fraction )\n\t{\n\t\t// intersection is point outside the range of the ray segment\n\n\t\tif ( b2LengthSquared( s ) < rr )\n\t\t{\n\t\t\t// initial overlap\n\t\t\toutput.point = input->origin;\n\t\t\toutput.hit = true;\n\t\t}\n\n\t\treturn output;\n\t}\n\n\t// hit point relative to center\n\tb2Vec2 hitPoint = b2MulAdd( s, fraction, d );\n\n\toutput.fraction = fraction / length;\n\toutput.normal = b2Normalize( hitPoint );\n\toutput.point = b2MulAdd( p, shape->radius, output.normal );\n\toutput.hit = true;\n\n\treturn output;\n}\n\nb2CastOutput b2RayCastCapsule( const b2Capsule* shape, const b2RayCastInput* input )\n{\n\tB2_ASSERT( b2IsValidRay( input ) );\n\n\tb2CastOutput output = { 0 };\n\n\tb2Vec2 v1 = shape->center1;\n\tb2Vec2 v2 = shape->center2;\n\n\tb2Vec2 e = b2Sub( v2, v1 );\n\n\tfloat capsuleLength;\n\tb2Vec2 a = b2GetLengthAndNormalize( &capsuleLength, e );\n\n\tif ( capsuleLength < FLT_EPSILON )\n\t{\n\t\t// Capsule is really a circle\n\t\tb2Circle circle = { v1, shape->radius };\n\t\treturn b2RayCastCircle( &circle, input );\n\t}\n\n\tb2Vec2 p1 = input->origin;\n\tb2Vec2 d = input->translation;\n\n\t// Ray from capsule start to ray start\n\tb2Vec2 q = b2Sub( p1, v1 );\n\tfloat qa = b2Dot( q, a );\n\n\t// Vector to ray start that is perpendicular to capsule axis\n\tb2Vec2 qp = b2MulAdd( q, -qa, a );\n\n\tfloat radius = shape->radius;\n\n\t// Does the ray start within the infinite length capsule?\n\tif ( b2Dot( qp, qp ) < radius * radius )\n\t{\n\t\tif ( qa < 0.0f )\n\t\t{\n\t\t\t// start point behind capsule segment\n\t\t\tb2Circle circle = { v1, shape->radius };\n\t\t\treturn b2RayCastCircle( &circle, input );\n\t\t}\n\n\t\tif ( qa > capsuleLength )\n\t\t{\n\t\t\t// start point ahead of capsule segment\n\t\t\tb2Circle circle = { v2, shape->radius };\n\t\t\treturn b2RayCastCircle( &circle, input );\n\t\t}\n\n\t\t// ray starts inside capsule -> no hit\n\t\toutput.point = input->origin;\n\t\toutput.hit = true;\n\t\treturn output;\n\t}\n\n\t// Perpendicular to capsule axis, pointing right\n\tb2Vec2 n = { a.y, -a.x };\n\n\tfloat rayLength;\n\tb2Vec2 u = b2GetLengthAndNormalize( &rayLength, d );\n\n\t// Intersect ray with infinite length capsule\n\t// v1 + radius * n + s1 * a = p1 + s2 * u\n\t// v1 - radius * n + s1 * a = p1 + s2 * u\n\n\t// s1 * a - s2 * u = b\n\t// b = q - radius * ap\n\t// or\n\t// b = q + radius * ap\n\n\t// Cramer's rule [a -u]\n\tfloat den = -a.x * u.y + u.x * a.y;\n\tif ( -FLT_EPSILON < den && den < FLT_EPSILON )\n\t{\n\t\t// Ray is parallel to capsule and outside infinite length capsule\n\t\treturn output;\n\t}\n\n\tb2Vec2 b1 = b2MulSub( q, radius, n );\n\tb2Vec2 b2 = b2MulAdd( q, radius, n );\n\n\tfloat invDen = 1.0f / den;\n\n\t// Cramer's rule [a b1]\n\tfloat s21 = ( a.x * b1.y - b1.x * a.y ) * invDen;\n\n\t// Cramer's rule [a b2]\n\tfloat s22 = ( a.x * b2.y - b2.x * a.y ) * invDen;\n\n\tfloat s2;\n\tb2Vec2 b;\n\tif ( s21 < s22 )\n\t{\n\t\ts2 = s21;\n\t\tb = b1;\n\t}\n\telse\n\t{\n\t\ts2 = s22;\n\t\tb = b2;\n\t\tn = b2Neg( n );\n\t}\n\n\tif ( s2 < 0.0f || input->maxFraction * rayLength < s2 )\n\t{\n\t\treturn output;\n\t}\n\n\t// Cramer's rule [b -u]\n\tfloat s1 = ( -b.x * u.y + u.x * b.y ) * invDen;\n\n\tif ( s1 < 0.0f )\n\t{\n\t\t// ray passes behind capsule segment\n\t\tb2Circle circle = { v1, shape->radius };\n\t\treturn b2RayCastCircle( &circle, input );\n\t}\n\telse if ( capsuleLength < s1 )\n\t{\n\t\t// ray passes ahead of capsule segment\n\t\tb2Circle circle = { v2, shape->radius };\n\t\treturn b2RayCastCircle( &circle, input );\n\t}\n\telse\n\t{\n\t\t// ray hits capsule side\n\t\toutput.fraction = s2 / rayLength;\n\t\toutput.point = b2Add( b2Lerp( v1, v2, s1 / capsuleLength ), b2MulSV( shape->radius, n ) );\n\t\toutput.normal = n;\n\t\toutput.hit = true;\n\t\treturn output;\n\t}\n}\n\n// Ray vs line segment\nb2CastOutput b2RayCastSegment( const b2Segment* shape, const b2RayCastInput* input, bool oneSided )\n{\n\tif ( oneSided )\n\t{\n\t\t// Skip left-side collision\n\t\tfloat offset = b2Cross( b2Sub( input->origin, shape->point1 ), b2Sub( shape->point2, shape->point1 ) );\n\t\tif ( offset < 0.0f )\n\t\t{\n\t\t\tb2CastOutput output = { 0 };\n\t\t\treturn output;\n\t\t}\n\t}\n\n\t// Put the ray into the edge's frame of reference.\n\tb2Vec2 p1 = input->origin;\n\tb2Vec2 d = input->translation;\n\n\tb2Vec2 v1 = shape->point1;\n\tb2Vec2 v2 = shape->point2;\n\tb2Vec2 e = b2Sub( v2, v1 );\n\n\tb2CastOutput output = { 0 };\n\n\tfloat length;\n\tb2Vec2 eUnit = b2GetLengthAndNormalize( &length, e );\n\tif ( length == 0.0f )\n\t{\n\t\treturn output;\n\t}\n\n\t// Normal points to the right, looking from v1 towards v2\n\tb2Vec2 normal = b2RightPerp( eUnit );\n\n\t// Intersect ray with infinite segment using normal\n\t// Similar to intersecting a ray with an infinite plane\n\t// p = p1 + t * d\n\t// dot(normal, p - v1) = 0\n\t// dot(normal, p1 - v1) + t * dot(normal, d) = 0\n\tfloat numerator = b2Dot( normal, b2Sub( v1, p1 ) );\n\tfloat denominator = b2Dot( normal, d );\n\n\tif ( denominator == 0.0f )\n\t{\n\t\t// parallel\n\t\treturn output;\n\t}\n\n\tfloat t = numerator / denominator;\n\tif ( t < 0.0f || input->maxFraction < t )\n\t{\n\t\t// out of ray range\n\t\treturn output;\n\t}\n\n\t// Intersection point on infinite segment\n\tb2Vec2 p = b2MulAdd( p1, t, d );\n\n\t// Compute position of p along segment\n\t// p = v1 + s * e\n\t// s = dot(p - v1, e) / dot(e, e)\n\n\tfloat s = b2Dot( b2Sub( p, v1 ), eUnit );\n\tif ( s < 0.0f || length < s )\n\t{\n\t\t// out of segment range\n\t\treturn output;\n\t}\n\n\tif ( numerator > 0.0f )\n\t{\n\t\tnormal = b2Neg( normal );\n\t}\n\n\toutput.fraction = t;\n\toutput.point = p;\n\toutput.normal = normal;\n\toutput.hit = true;\n\n\treturn output;\n}\n\nb2CastOutput b2RayCastPolygon( const b2Polygon* shape, const b2RayCastInput* input )\n{\n\tB2_ASSERT( b2IsValidRay( input ) );\n\n\tif ( shape->radius == 0.0f )\n\t{\n\t\t// Shift all math to first vertex since the polygon may be far\n\t\t// from the origin.\n\t\tb2Vec2 base = shape->vertices[0];\n\n\t\tb2Vec2 p1 = b2Sub( input->origin, base );\n\t\tb2Vec2 d = input->translation;\n\n\t\tfloat lower = 0.0f, upper = input->maxFraction;\n\n\t\tint index = -1;\n\n\t\tb2CastOutput output = { 0 };\n\n\t\tfor ( int edgeIndex = 0; edgeIndex < shape->count; ++edgeIndex )\n\t\t{\n\t\t\t// p = p1 + a * d\n\t\t\t// dot(normal, p - v) = 0\n\t\t\t// dot(normal, p1 - v) + a * dot(normal, d) = 0\n\t\t\tb2Vec2 vertex = b2Sub( shape->vertices[edgeIndex], base );\n\t\t\tfloat numerator = b2Dot( shape->normals[edgeIndex], b2Sub( vertex, p1 ) );\n\t\t\tfloat denominator = b2Dot( shape->normals[edgeIndex], d );\n\n\t\t\tif ( denominator == 0.0f )\n\t\t\t{\n\t\t\t\t// Parallel and runs outside edge\n\t\t\t\tif ( numerator < 0.0f )\n\t\t\t\t{\n\t\t\t\t\treturn output;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Note: we want this predicate without division:\n\t\t\t\t// lower < numerator / denominator, where denominator < 0\n\t\t\t\t// Since denominator < 0, we have to flip the inequality:\n\t\t\t\t// lower < numerator / denominator <==> denominator * lower > numerator.\n\t\t\t\tif ( denominator < 0.0f && numerator < lower * denominator )\n\t\t\t\t{\n\t\t\t\t\t// Increase lower.\n\t\t\t\t\t// The segment enters this half-space.\n\t\t\t\t\tlower = numerator / denominator;\n\t\t\t\t\tindex = edgeIndex;\n\t\t\t\t}\n\t\t\t\telse if ( denominator > 0.0f && numerator < upper * denominator )\n\t\t\t\t{\n\t\t\t\t\t// Decrease upper.\n\t\t\t\t\t// The segment exits this half-space.\n\t\t\t\t\tupper = numerator / denominator;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( upper < lower )\n\t\t\t{\n\t\t\t\t// Ray misses\n\t\t\t\treturn output;\n\t\t\t}\n\t\t}\n\n\t\tB2_ASSERT( 0.0f <= lower && lower <= input->maxFraction );\n\n\t\tif ( index >= 0 )\n\t\t{\n\t\t\toutput.fraction = lower;\n\t\t\toutput.normal = shape->normals[index];\n\t\t\toutput.point = b2MulAdd( input->origin, lower, d );\n\t\t\toutput.hit = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// initial overlap\n\t\t\toutput.point = input->origin;\n\t\t\toutput.hit = true;\n\t\t}\n\n\t\treturn output;\n\t}\n\n\tb2ShapeCastPairInput castInput;\n\tcastInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius );\n\tcastInput.proxyB = b2MakeProxy( &input->origin, 1, 0.0f );\n\tcastInput.transformA = b2Transform_identity;\n\tcastInput.transformB = b2Transform_identity;\n\tcastInput.translationB = input->translation;\n\tcastInput.maxFraction = input->maxFraction;\n\tcastInput.canEncroach = false;\n\treturn b2ShapeCast( &castInput );\n}\n\nb2CastOutput b2ShapeCastCircle( const b2Circle* shape, const b2ShapeCastInput* input )\n{\n\tb2ShapeCastPairInput pairInput;\n\tpairInput.proxyA = b2MakeProxy( &shape->center, 1, shape->radius );\n\tpairInput.proxyB = input->proxy;\n\tpairInput.transformA = b2Transform_identity;\n\tpairInput.transformB = b2Transform_identity;\n\tpairInput.translationB = input->translation;\n\tpairInput.maxFraction = input->maxFraction;\n\tpairInput.canEncroach = input->canEncroach;\n\n\tb2CastOutput output = b2ShapeCast( &pairInput );\n\treturn output;\n}\n\nb2CastOutput b2ShapeCastCapsule( const b2Capsule* shape, const b2ShapeCastInput* input )\n{\n\tb2ShapeCastPairInput pairInput;\n\tpairInput.proxyA = b2MakeProxy( &shape->center1, 2, shape->radius );\n\tpairInput.proxyB = input->proxy;\n\tpairInput.transformA = b2Transform_identity;\n\tpairInput.transformB = b2Transform_identity;\n\tpairInput.translationB = input->translation;\n\tpairInput.maxFraction = input->maxFraction;\n\tpairInput.canEncroach = input->canEncroach;\n\n\tb2CastOutput output = b2ShapeCast( &pairInput );\n\treturn output;\n}\n\nb2CastOutput b2ShapeCastSegment( const b2Segment* shape, const b2ShapeCastInput* input )\n{\n\tb2ShapeCastPairInput pairInput;\n\tpairInput.proxyA = b2MakeProxy( &shape->point1, 2, 0.0f );\n\tpairInput.proxyB = input->proxy;\n\tpairInput.transformA = b2Transform_identity;\n\tpairInput.transformB = b2Transform_identity;\n\tpairInput.translationB = input->translation;\n\tpairInput.maxFraction = input->maxFraction;\n\tpairInput.canEncroach = input->canEncroach;\n\n\tb2CastOutput output = b2ShapeCast( &pairInput );\n\treturn output;\n}\n\nb2CastOutput b2ShapeCastPolygon( const b2Polygon* shape, const b2ShapeCastInput* input )\n{\n\tb2ShapeCastPairInput pairInput;\n\tpairInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius );\n\tpairInput.proxyB = input->proxy;\n\tpairInput.transformA = b2Transform_identity;\n\tpairInput.transformB = b2Transform_identity;\n\tpairInput.translationB = input->translation;\n\tpairInput.maxFraction = input->maxFraction;\n\tpairInput.canEncroach = input->canEncroach;\n\n\tb2CastOutput output = b2ShapeCast( &pairInput );\n\treturn output;\n}\n\nb2PlaneResult b2CollideMoverAndCircle( const b2Capsule* mover, const b2Circle* shape )\n{\n\tb2DistanceInput distanceInput;\n\tdistanceInput.proxyA = b2MakeProxy( &shape->center, 1, 0.0f );\n\tdistanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );\n\tdistanceInput.transformA = b2Transform_identity;\n\tdistanceInput.transformB = b2Transform_identity;\n\tdistanceInput.useRadii = false;\n\n\tfloat totalRadius = mover->radius + shape->radius;\n\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );\n\n\tif ( distanceOutput.distance <= totalRadius )\n\t{\n\t\tb2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };\n\t\treturn (b2PlaneResult){\n\t\t\t.plane = plane,\n\t\t\t.point = distanceOutput.pointA,\n\t\t\t.hit = true,\n\t\t};\n\t}\n\n\treturn (b2PlaneResult){ 0 };\n}\n\nb2PlaneResult b2CollideMoverAndCapsule( const b2Capsule* mover, const b2Capsule* shape )\n{\n\tb2DistanceInput distanceInput;\n\tdistanceInput.proxyA = b2MakeProxy( &shape->center1, 2, 0.0f );\n\tdistanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );\n\tdistanceInput.transformA = b2Transform_identity;\n\tdistanceInput.transformB = b2Transform_identity;\n\tdistanceInput.useRadii = false;\n\n\tfloat totalRadius = mover->radius + shape->radius;\n\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );\n\n\tif ( distanceOutput.distance <= totalRadius )\n\t{\n\t\tb2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };\n\t\treturn (b2PlaneResult){\n\t\t\t.plane = plane,\n\t\t\t.point = distanceOutput.pointA,\n\t\t\t.hit = true,\n\t\t};\n\t}\n\n\treturn (b2PlaneResult){ 0 };\n}\n\nb2PlaneResult b2CollideMoverAndPolygon( const b2Capsule* mover, const b2Polygon* shape )\n{\n\tb2DistanceInput distanceInput;\n\tdistanceInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius );\n\tdistanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );\n\tdistanceInput.transformA = b2Transform_identity;\n\tdistanceInput.transformB = b2Transform_identity;\n\tdistanceInput.useRadii = false;\n\n\tfloat totalRadius = mover->radius + shape->radius;\n\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );\n\n\tif ( distanceOutput.distance <= totalRadius )\n\t{\n\t\tb2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };\n\t\treturn (b2PlaneResult){\n\t\t\t.plane = plane,\n\t\t\t.point = distanceOutput.pointA,\n\t\t\t.hit = true,\n\t\t};\n\t}\n\n\treturn (b2PlaneResult){ 0 };\n}\n\nb2PlaneResult b2CollideMoverAndSegment( const b2Capsule* mover, const b2Segment* shape )\n{\n\tb2DistanceInput distanceInput;\n\tdistanceInput.proxyA = b2MakeProxy( &shape->point1, 2, 0.0f );\n\tdistanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );\n\tdistanceInput.transformA = b2Transform_identity;\n\tdistanceInput.transformB = b2Transform_identity;\n\tdistanceInput.useRadii = false;\n\n\tfloat totalRadius = mover->radius;\n\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );\n\n\tif ( distanceOutput.distance <= totalRadius )\n\t{\n\t\tb2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };\n\t\treturn (b2PlaneResult){\n\t\t\t.plane = plane,\n\t\t\t.point = distanceOutput.pointA,\n\t\t\t.hit = true,\n\t\t};\n\t}\n\n\treturn (b2PlaneResult){ 0 };\n}\n"
  },
  {
    "path": "src/hull.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"constants.h\"\n#include \"core.h\"\n\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n\n// quickhull recursion\nstatic b2Hull b2RecurseHull( b2Vec2 p1, b2Vec2 p2, b2Vec2* ps, int count )\n{\n\tb2Hull hull;\n\thull.count = 0;\n\n\tif ( count == 0 )\n\t{\n\t\treturn hull;\n\t}\n\n\t// create an edge vector pointing from p1 to p2\n\tb2Vec2 e = b2Normalize( b2Sub( p2, p1 ) );\n\n\t// discard points left of e and find point furthest to the right of e\n\tb2Vec2 rightPoints[B2_MAX_POLYGON_VERTICES];\n\tint rightCount = 0;\n\n\tint bestIndex = 0;\n\tfloat bestDistance = b2Cross( b2Sub( ps[bestIndex], p1 ), e );\n\tif ( bestDistance > 0.0f )\n\t{\n\t\trightPoints[rightCount++] = ps[bestIndex];\n\t}\n\n\tfor ( int i = 1; i < count; ++i )\n\t{\n\t\tfloat distance = b2Cross( b2Sub( ps[i], p1 ), e );\n\t\tif ( distance > bestDistance )\n\t\t{\n\t\t\tbestIndex = i;\n\t\t\tbestDistance = distance;\n\t\t}\n\n\t\tif ( distance > 0.0f )\n\t\t{\n\t\t\trightPoints[rightCount++] = ps[i];\n\t\t}\n\t}\n\n\tif ( bestDistance < 2.0f * B2_LINEAR_SLOP )\n\t{\n\t\treturn hull;\n\t}\n\n\tb2Vec2 bestPoint = ps[bestIndex];\n\n\t// compute hull to the right of p1-bestPoint\n\tb2Hull hull1 = b2RecurseHull( p1, bestPoint, rightPoints, rightCount );\n\n\t// compute hull to the right of bestPoint-p2\n\tb2Hull hull2 = b2RecurseHull( bestPoint, p2, rightPoints, rightCount );\n\n\t// stitch together hulls\n\tfor ( int i = 0; i < hull1.count; ++i )\n\t{\n\t\thull.points[hull.count++] = hull1.points[i];\n\t}\n\n\thull.points[hull.count++] = bestPoint;\n\n\tfor ( int i = 0; i < hull2.count; ++i )\n\t{\n\t\thull.points[hull.count++] = hull2.points[i];\n\t}\n\n\tB2_ASSERT( hull.count < B2_MAX_POLYGON_VERTICES );\n\n\treturn hull;\n}\n\n// quickhull algorithm\n// - merges vertices based on B2_LINEAR_SLOP\n// - removes collinear points using B2_LINEAR_SLOP\n// - returns an empty hull if it fails\nb2Hull b2ComputeHull( const b2Vec2* points, int count )\n{\n\tb2Hull hull;\n\thull.count = 0;\n\n\tif ( count < 3 || count > B2_MAX_POLYGON_VERTICES )\n\t{\n\t\t// check your data\n\t\treturn hull;\n\t}\n\n\tcount = b2MinInt( count, B2_MAX_POLYGON_VERTICES );\n\n\tb2AABB aabb = { { FLT_MAX, FLT_MAX }, { -FLT_MAX, -FLT_MAX } };\n\n\t// Perform aggressive point welding. First point always remains.\n\t// Also compute the bounding box for later.\n\tb2Vec2 ps[B2_MAX_POLYGON_VERTICES];\n\tint n = 0;\n\tconst float linearSlop = B2_LINEAR_SLOP;\n\tconst float tolSqr = 16.0f * linearSlop * linearSlop;\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\taabb.lowerBound = b2Min( aabb.lowerBound, points[i] );\n\t\taabb.upperBound = b2Max( aabb.upperBound, points[i] );\n\n\t\tb2Vec2 vi = points[i];\n\n\t\tbool unique = true;\n\t\tfor ( int j = 0; j < i; ++j )\n\t\t{\n\t\t\tb2Vec2 vj = points[j];\n\n\t\t\tfloat distSqr = b2DistanceSquared( vi, vj );\n\t\t\tif ( distSqr < tolSqr )\n\t\t\t{\n\t\t\t\tunique = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif ( unique )\n\t\t{\n\t\t\tps[n++] = vi;\n\t\t}\n\t}\n\n\tif ( n < 3 )\n\t{\n\t\t// all points very close together, check your data and check your scale\n\t\treturn hull;\n\t}\n\n\t// Find an extreme point as the first point on the hull\n\tb2Vec2 c = b2AABB_Center( aabb );\n\tint f1 = 0;\n\tfloat dsq1 = b2DistanceSquared( c, ps[f1] );\n\tfor ( int i = 1; i < n; ++i )\n\t{\n\t\tfloat dsq = b2DistanceSquared( c, ps[i] );\n\t\tif ( dsq > dsq1 )\n\t\t{\n\t\t\tf1 = i;\n\t\t\tdsq1 = dsq;\n\t\t}\n\t}\n\n\t// remove p1 from working set\n\tb2Vec2 p1 = ps[f1];\n\tps[f1] = ps[n - 1];\n\tn = n - 1;\n\n\tint f2 = 0;\n\tfloat dsq2 = b2DistanceSquared( p1, ps[f2] );\n\tfor ( int i = 1; i < n; ++i )\n\t{\n\t\tfloat dsq = b2DistanceSquared( p1, ps[i] );\n\t\tif ( dsq > dsq2 )\n\t\t{\n\t\t\tf2 = i;\n\t\t\tdsq2 = dsq;\n\t\t}\n\t}\n\n\t// remove p2 from working set\n\tb2Vec2 p2 = ps[f2];\n\tps[f2] = ps[n - 1];\n\tn = n - 1;\n\n\t// split the points into points that are left and right of the line p1-p2.\n\tb2Vec2 rightPoints[B2_MAX_POLYGON_VERTICES - 2];\n\tint rightCount = 0;\n\n\tb2Vec2 leftPoints[B2_MAX_POLYGON_VERTICES - 2];\n\tint leftCount = 0;\n\n\tb2Vec2 e = b2Normalize( b2Sub( p2, p1 ) );\n\n\tfor ( int i = 0; i < n; ++i )\n\t{\n\t\tfloat d = b2Cross( b2Sub( ps[i], p1 ), e );\n\n\t\t// slop used here to skip points that are very close to the line p1-p2\n\t\tif ( d >= 2.0f * linearSlop )\n\t\t{\n\t\t\trightPoints[rightCount++] = ps[i];\n\t\t}\n\t\telse if ( d <= -2.0f * linearSlop )\n\t\t{\n\t\t\tleftPoints[leftCount++] = ps[i];\n\t\t}\n\t}\n\n\t// compute hulls on right and left\n\tb2Hull hull1 = b2RecurseHull( p1, p2, rightPoints, rightCount );\n\tb2Hull hull2 = b2RecurseHull( p2, p1, leftPoints, leftCount );\n\n\tif ( hull1.count == 0 && hull2.count == 0 )\n\t{\n\t\t// all points collinear\n\t\treturn hull;\n\t}\n\n\t// stitch hulls together, preserving CCW winding order\n\thull.points[hull.count++] = p1;\n\n\tfor ( int i = 0; i < hull1.count; ++i )\n\t{\n\t\thull.points[hull.count++] = hull1.points[i];\n\t}\n\n\thull.points[hull.count++] = p2;\n\n\tfor ( int i = 0; i < hull2.count; ++i )\n\t{\n\t\thull.points[hull.count++] = hull2.points[i];\n\t}\n\n\tB2_ASSERT( hull.count <= B2_MAX_POLYGON_VERTICES );\n\n\t// merge collinear\n\tbool searching = true;\n\twhile ( searching && hull.count > 2 )\n\t{\n\t\tsearching = false;\n\n\t\tfor ( int i = 0; i < hull.count; ++i )\n\t\t{\n\t\t\tint i1 = i;\n\t\t\tint i2 = ( i + 1 ) % hull.count;\n\t\t\tint i3 = ( i + 2 ) % hull.count;\n\n\t\t\tb2Vec2 s1 = hull.points[i1];\n\t\t\tb2Vec2 s2 = hull.points[i2];\n\t\t\tb2Vec2 s3 = hull.points[i3];\n\n\t\t\t// unit edge vector for s1-s3\n\t\t\tb2Vec2 r = b2Normalize( b2Sub( s3, s1 ) );\n\n\t\t\tfloat distance = b2Cross( b2Sub( s2, s1 ), r );\n\t\t\tif ( distance <= 2.0f * linearSlop )\n\t\t\t{\n\t\t\t\t// remove midpoint from hull\n\t\t\t\tfor ( int j = i2; j < hull.count - 1; ++j )\n\t\t\t\t{\n\t\t\t\t\thull.points[j] = hull.points[j + 1];\n\t\t\t\t}\n\t\t\t\thull.count -= 1;\n\n\t\t\t\t// continue searching for collinear points\n\t\t\t\tsearching = true;\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( hull.count < 3 )\n\t{\n\t\t// all points collinear, shouldn't be reached since this was validated above\n\t\thull.count = 0;\n\t}\n\n\treturn hull;\n}\n\nbool b2ValidateHull( const b2Hull* hull )\n{\n\tif ( hull->count < 3 || B2_MAX_POLYGON_VERTICES < hull->count )\n\t{\n\t\treturn false;\n\t}\n\n\t// test that every point is behind every edge\n\tfor ( int i = 0; i < hull->count; ++i )\n\t{\n\t\t// create an edge vector\n\t\tint i1 = i;\n\t\tint i2 = i < hull->count - 1 ? i1 + 1 : 0;\n\t\tb2Vec2 p = hull->points[i1];\n\t\tb2Vec2 e = b2Normalize( b2Sub( hull->points[i2], p ) );\n\n\t\tfor ( int j = 0; j < hull->count; ++j )\n\t\t{\n\t\t\t// skip points that subtend the current edge\n\t\t\tif ( j == i1 || j == i2 )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfloat distance = b2Cross( b2Sub( hull->points[j], p ), e );\n\t\t\tif ( distance >= 0.0f )\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t// test for collinear points\n\tconst float linearSlop = B2_LINEAR_SLOP;\n\tfor ( int i = 0; i < hull->count; ++i )\n\t{\n\t\tint i1 = i;\n\t\tint i2 = ( i + 1 ) % hull->count;\n\t\tint i3 = ( i + 2 ) % hull->count;\n\n\t\tb2Vec2 p1 = hull->points[i1];\n\t\tb2Vec2 p2 = hull->points[i2];\n\t\tb2Vec2 p3 = hull->points[i3];\n\n\t\tb2Vec2 e = b2Normalize( b2Sub( p3, p1 ) );\n\n\t\tfloat distance = b2Cross( b2Sub( p2, p1 ), e );\n\t\tif ( distance <= linearSlop )\n\t\t{\n\t\t\t// p1-p2-p3 are collinear\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/id_pool.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"id_pool.h\"\n\nb2IdPool b2CreateIdPool( void )\n{\n\tb2IdPool pool = { 0 };\n\tpool.freeArray = b2IntArray_Create( 32 );\n\treturn pool;\n}\n\nvoid b2DestroyIdPool( b2IdPool* pool )\n{\n\tb2IntArray_Destroy( &pool->freeArray );\n\t*pool = ( b2IdPool ){ 0 };\n}\n\nint b2AllocId( b2IdPool* pool )\n{\n\tint count = pool->freeArray.count;\n\tif ( count > 0 )\n\t{\n\t\tint id = b2IntArray_Pop( &pool->freeArray );\n\t\treturn id;\n\t}\n\n\tint id = pool->nextIndex;\n\tpool->nextIndex += 1;\n\treturn id;\n}\n\nvoid b2FreeId( b2IdPool* pool, int id )\n{\n\tB2_ASSERT( pool->nextIndex > 0 );\n\tB2_ASSERT( 0 <= id && id < pool->nextIndex );\n\tb2IntArray_Push( &pool->freeArray, id );\n}\n\n#if B2_VALIDATE\n\nvoid b2ValidateFreeId( b2IdPool* pool, int id )\n{\n\tint freeCount = pool->freeArray.count;\n\tfor ( int i = 0; i < freeCount; ++i )\n\t{\n\t\tif ( pool->freeArray.data[i] == id )\n\t\t{\n\t\t\treturn;\n\t\t}\n\t}\n\n\tB2_ASSERT( 0 );\n}\n\nvoid b2ValidateUsedId( b2IdPool* pool, int id )\n{\n\tint freeCount = pool->freeArray.count;\n\tfor ( int i = 0; i < freeCount; ++i )\n\t{\n\t\tif ( pool->freeArray.data[i] == id )\n\t\t{\n\t\t\tB2_ASSERT( 0 );\n\t\t}\n\t}\n}\n\n#else\n\nvoid b2ValidateFreeId( b2IdPool* pool, int id )\n{\n\tB2_UNUSED( pool, id );\n}\n\nvoid b2ValidateUsedId( b2IdPool* pool, int id )\n{\n\tB2_UNUSED( pool, id );\n}\n#endif\n"
  },
  {
    "path": "src/id_pool.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n\ntypedef struct b2IdPool\n{\n\tb2IntArray freeArray;\n\tint nextIndex;\n} b2IdPool;\n\nb2IdPool b2CreateIdPool( void );\nvoid b2DestroyIdPool( b2IdPool* pool );\n\nint b2AllocId( b2IdPool* pool );\nvoid b2FreeId( b2IdPool* pool, int id );\nvoid b2ValidateFreeId( b2IdPool* pool, int id );\nvoid b2ValidateUsedId( b2IdPool* pool, int id );\n\nstatic inline int b2GetIdCount( b2IdPool* pool )\n{\n\treturn pool->nextIndex - pool->freeArray.count;\n}\n\nstatic inline int b2GetIdCapacity( b2IdPool* pool )\n{\n\treturn pool->nextIndex;\n}\n\nstatic inline int b2GetIdBytes( b2IdPool* pool )\n{\n\treturn b2IntArray_ByteCount(&pool->freeArray);\n}\n"
  },
  {
    "path": "src/island.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"island.h\"\n\n#include \"body.h\"\n#include \"contact.h\"\n#include \"core.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"solver_set.h\"\n\n#include <stddef.h>\n\nB2_ARRAY_SOURCE( b2Island, b2Island )\nB2_ARRAY_SOURCE( b2IslandSim, b2IslandSim )\n\nb2Island* b2CreateIsland( b2World* world, int setIndex )\n{\n\tB2_ASSERT( setIndex == b2_awakeSet || setIndex >= b2_firstSleepingSet );\n\n\tint islandId = b2AllocId( &world->islandIdPool );\n\n\tif ( islandId == world->islands.count )\n\t{\n\t\tb2Island emptyIsland = { 0 };\n\t\tb2IslandArray_Push( &world->islands, emptyIsland );\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( world->islands.data[islandId].setIndex == B2_NULL_INDEX );\n\t}\n\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );\n\n\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\tisland->setIndex = setIndex;\n\tisland->localIndex = set->islandSims.count;\n\tisland->islandId = islandId;\n\tisland->headBody = B2_NULL_INDEX;\n\tisland->tailBody = B2_NULL_INDEX;\n\tisland->bodyCount = 0;\n\tisland->headContact = B2_NULL_INDEX;\n\tisland->tailContact = B2_NULL_INDEX;\n\tisland->contactCount = 0;\n\tisland->headJoint = B2_NULL_INDEX;\n\tisland->tailJoint = B2_NULL_INDEX;\n\tisland->jointCount = 0;\n\tisland->constraintRemoveCount = 0;\n\n\tb2IslandSim* islandSim = b2IslandSimArray_Add( &set->islandSims );\n\tislandSim->islandId = islandId;\n\n\treturn island;\n}\n\nvoid b2DestroyIsland( b2World* world, int islandId )\n{\n\tif ( world->splitIslandId == islandId )\n\t{\n\t\tworld->splitIslandId = B2_NULL_INDEX;\n\t}\n\n\t// assume island is empty\n\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, island->setIndex );\n\tint movedIndex = b2IslandSimArray_RemoveSwap( &set->islandSims, island->localIndex );\n\tif ( movedIndex != B2_NULL_INDEX )\n\t{\n\t\t// Fix index on moved element\n\t\tb2IslandSim* movedElement = set->islandSims.data + island->localIndex;\n\t\tint movedId = movedElement->islandId;\n\t\tb2Island* movedIsland = b2IslandArray_Get( &world->islands, movedId );\n\t\tB2_ASSERT( movedIsland->localIndex == movedIndex );\n\t\tmovedIsland->localIndex = island->localIndex;\n\t}\n\n\t// Free island and id (preserve island revision)\n\tisland->islandId = B2_NULL_INDEX;\n\tisland->setIndex = B2_NULL_INDEX;\n\tisland->localIndex = B2_NULL_INDEX;\n\tb2FreeId( &world->islandIdPool, islandId );\n}\n\nstatic int b2MergeIslands( b2World* world, int islandIdA, int islandIdB )\n{\n\tif ( islandIdA == islandIdB )\n\t{\n\t\treturn islandIdA;\n\t}\n\n\tif ( islandIdA == B2_NULL_INDEX )\n\t{\n\t\tB2_ASSERT( islandIdB != B2_NULL_INDEX );\n\t\treturn islandIdB;\n\t}\n\n\tif ( islandIdB == B2_NULL_INDEX )\n\t{\n\t\tB2_ASSERT( islandIdA != B2_NULL_INDEX );\n\t\treturn islandIdA;\n\t}\n\n\tb2Island* islandA = b2IslandArray_Get( &world->islands, islandIdA );\n\tb2Island* islandB = b2IslandArray_Get( &world->islands, islandIdB );\n\n\t// Keep the biggest island to reduce cache misses\n\tb2Island* big;\n\tb2Island* small;\n\tif ( islandA->bodyCount >= islandB->bodyCount )\n\t{\n\t\tbig = islandA;\n\t\tsmall = islandB;\n\t}\n\telse\n\t{\n\t\tbig = islandB;\n\t\tsmall = islandA;\n\t}\n\n\tint bigId = big->islandId;\n\n\t// remap island indices (cache misses)\n\tint bodyId = small->headBody;\n\twhile ( bodyId != B2_NULL_INDEX )\n\t{\n\t\tb2Body* body = b2BodyArray_Get( &world->bodies, bodyId );\n\t\tbody->islandId = bigId;\n\t\tbodyId = body->islandNext;\n\t}\n\n\tint contactId = small->headContact;\n\twhile ( contactId != B2_NULL_INDEX )\n\t{\n\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\tcontact->islandId = bigId;\n\t\tcontactId = contact->islandNext;\n\t}\n\n\tint jointId = small->headJoint;\n\twhile ( jointId != B2_NULL_INDEX )\n\t{\n\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\tjoint->islandId = bigId;\n\t\tjointId = joint->islandNext;\n\t}\n\n\t// connect body lists\n\tB2_ASSERT( big->tailBody != B2_NULL_INDEX );\n\tb2Body* tailBody = b2BodyArray_Get( &world->bodies, big->tailBody );\n\tB2_ASSERT( tailBody->islandNext == B2_NULL_INDEX );\n\ttailBody->islandNext = small->headBody;\n\n\tB2_ASSERT( small->headBody != B2_NULL_INDEX );\n\tb2Body* headBody = b2BodyArray_Get( &world->bodies, small->headBody );\n\tB2_ASSERT( headBody->islandPrev == B2_NULL_INDEX );\n\theadBody->islandPrev = big->tailBody;\n\n\tbig->tailBody = small->tailBody;\n\tbig->bodyCount += small->bodyCount;\n\n\t// connect contact lists\n\tif ( big->headContact == B2_NULL_INDEX )\n\t{\n\t\t// Big island has no contacts\n\t\tB2_ASSERT( big->tailContact == B2_NULL_INDEX && big->contactCount == 0 );\n\t\tbig->headContact = small->headContact;\n\t\tbig->tailContact = small->tailContact;\n\t\tbig->contactCount = small->contactCount;\n\t}\n\telse if ( small->headContact != B2_NULL_INDEX )\n\t{\n\t\t// Both islands have contacts\n\t\tB2_ASSERT( small->tailContact != B2_NULL_INDEX && small->contactCount > 0 );\n\t\tB2_ASSERT( big->tailContact != B2_NULL_INDEX && big->contactCount > 0 );\n\n\t\tb2Contact* tailContact = b2ContactArray_Get( &world->contacts, big->tailContact );\n\t\tB2_ASSERT( tailContact->islandNext == B2_NULL_INDEX );\n\t\ttailContact->islandNext = small->headContact;\n\n\t\tb2Contact* headContact = b2ContactArray_Get( &world->contacts, small->headContact );\n\t\tB2_ASSERT( headContact->islandPrev == B2_NULL_INDEX );\n\t\theadContact->islandPrev = big->tailContact;\n\n\t\tbig->tailContact = small->tailContact;\n\t\tbig->contactCount += small->contactCount;\n\t}\n\n\tif ( big->headJoint == B2_NULL_INDEX )\n\t{\n\t\t// Root island has no joints\n\t\tB2_ASSERT( big->tailJoint == B2_NULL_INDEX && big->jointCount == 0 );\n\t\tbig->headJoint = small->headJoint;\n\t\tbig->tailJoint = small->tailJoint;\n\t\tbig->jointCount = small->jointCount;\n\t}\n\telse if ( small->headJoint != B2_NULL_INDEX )\n\t{\n\t\t// Both islands have joints\n\t\tB2_ASSERT( small->tailJoint != B2_NULL_INDEX && small->jointCount > 0 );\n\t\tB2_ASSERT( big->tailJoint != B2_NULL_INDEX && big->jointCount > 0 );\n\n\t\tb2Joint* tailJoint = b2JointArray_Get( &world->joints, big->tailJoint );\n\t\tB2_ASSERT( tailJoint->islandNext == B2_NULL_INDEX );\n\t\ttailJoint->islandNext = small->headJoint;\n\n\t\tb2Joint* headJoint = b2JointArray_Get( &world->joints, small->headJoint );\n\t\tB2_ASSERT( headJoint->islandPrev == B2_NULL_INDEX );\n\t\theadJoint->islandPrev = big->tailJoint;\n\n\t\tbig->tailJoint = small->tailJoint;\n\t\tbig->jointCount += small->jointCount;\n\t}\n\n\t// Track removed constraints\n\tbig->constraintRemoveCount += small->constraintRemoveCount;\n\n\tsmall->bodyCount = 0;\n\tsmall->contactCount = 0;\n\tsmall->jointCount = 0;\n\tsmall->headBody = B2_NULL_INDEX;\n\tsmall->headContact = B2_NULL_INDEX;\n\tsmall->headJoint = B2_NULL_INDEX;\n\tsmall->tailBody = B2_NULL_INDEX;\n\tsmall->tailContact = B2_NULL_INDEX;\n\tsmall->tailJoint = B2_NULL_INDEX;\n\tsmall->constraintRemoveCount = 0;\n\n\tb2DestroyIsland( world, small->islandId );\n\n\tb2ValidateIsland( world, bigId );\n\n\treturn bigId;\n}\n\nstatic void b2AddContactToIsland( b2World* world, int islandId, b2Contact* contact )\n{\n\tB2_ASSERT( contact->islandId == B2_NULL_INDEX );\n\tB2_ASSERT( contact->islandPrev == B2_NULL_INDEX );\n\tB2_ASSERT( contact->islandNext == B2_NULL_INDEX );\n\n\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\n\tif ( island->headContact != B2_NULL_INDEX )\n\t{\n\t\tcontact->islandNext = island->headContact;\n\t\tb2Contact* headContact = b2ContactArray_Get( &world->contacts, island->headContact );\n\t\theadContact->islandPrev = contact->contactId;\n\t}\n\n\tisland->headContact = contact->contactId;\n\tif ( island->tailContact == B2_NULL_INDEX )\n\t{\n\t\tisland->tailContact = island->headContact;\n\t}\n\n\tisland->contactCount += 1;\n\tcontact->islandId = islandId;\n\n\tb2ValidateIsland( world, islandId );\n}\n\n// Link a contact into an island.\nvoid b2LinkContact( b2World* world, b2Contact* contact )\n{\n\tB2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) != 0 );\n\n\tint bodyIdA = contact->edges[0].bodyId;\n\tint bodyIdB = contact->edges[1].bodyId;\n\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );\n\n\tB2_ASSERT( bodyA->setIndex != b2_disabledSet && bodyB->setIndex != b2_disabledSet );\n\tB2_ASSERT( bodyA->setIndex != b2_staticSet || bodyB->setIndex != b2_staticSet );\n\n\t// Wake bodyB if bodyA is awake and bodyB is sleeping\n\tif ( bodyA->setIndex == b2_awakeSet && bodyB->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeSolverSet( world, bodyB->setIndex );\n\t}\n\n\t// Wake bodyA if bodyB is awake and bodyA is sleeping\n\tif ( bodyB->setIndex == b2_awakeSet && bodyA->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeSolverSet( world, bodyA->setIndex );\n\t}\n\n\tint islandIdA = bodyA->islandId;\n\tint islandIdB = bodyB->islandId;\n\n\t// Static bodies have null island indices.\n\tB2_ASSERT( bodyA->setIndex != b2_staticSet || islandIdA == B2_NULL_INDEX );\n\tB2_ASSERT( bodyB->setIndex != b2_staticSet || islandIdB == B2_NULL_INDEX );\n\tB2_ASSERT( islandIdA != B2_NULL_INDEX || islandIdB != B2_NULL_INDEX );\n\n\t// Merge islands. This will destroy one of the islands.\n\tint finalIslandId = b2MergeIslands( world, islandIdA, islandIdB );\n\n\t// Add contact to the island that survived\n\tb2AddContactToIsland( world, finalIslandId, contact );\n}\n\n// This is called when a contact no longer has contact points or when a contact is destroyed.\nvoid b2UnlinkContact( b2World* world, b2Contact* contact )\n{\n\tB2_ASSERT( contact->islandId != B2_NULL_INDEX );\n\n\t// remove from island\n\tint islandId = contact->islandId;\n\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\n\tif ( contact->islandPrev != B2_NULL_INDEX )\n\t{\n\t\tb2Contact* prevContact = b2ContactArray_Get( &world->contacts, contact->islandPrev );\n\t\tB2_ASSERT( prevContact->islandNext == contact->contactId );\n\t\tprevContact->islandNext = contact->islandNext;\n\t}\n\n\tif ( contact->islandNext != B2_NULL_INDEX )\n\t{\n\t\tb2Contact* nextContact = b2ContactArray_Get( &world->contacts, contact->islandNext );\n\t\tB2_ASSERT( nextContact->islandPrev == contact->contactId );\n\t\tnextContact->islandPrev = contact->islandPrev;\n\t}\n\n\tif ( island->headContact == contact->contactId )\n\t{\n\t\tisland->headContact = contact->islandNext;\n\t}\n\n\tif ( island->tailContact == contact->contactId )\n\t{\n\t\tisland->tailContact = contact->islandPrev;\n\t}\n\n\tB2_ASSERT( island->contactCount > 0 );\n\tisland->contactCount -= 1;\n\tisland->constraintRemoveCount += 1;\n\n\tcontact->islandId = B2_NULL_INDEX;\n\tcontact->islandPrev = B2_NULL_INDEX;\n\tcontact->islandNext = B2_NULL_INDEX;\n\n\tb2ValidateIsland( world, islandId );\n}\n\nstatic void b2AddJointToIsland( b2World* world, int islandId, b2Joint* joint )\n{\n\tB2_ASSERT( joint->islandId == B2_NULL_INDEX );\n\tB2_ASSERT( joint->islandPrev == B2_NULL_INDEX );\n\tB2_ASSERT( joint->islandNext == B2_NULL_INDEX );\n\n\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\n\tif ( island->headJoint != B2_NULL_INDEX )\n\t{\n\t\tjoint->islandNext = island->headJoint;\n\t\tb2Joint* headJoint = b2JointArray_Get( &world->joints, island->headJoint );\n\t\theadJoint->islandPrev = joint->jointId;\n\t}\n\n\tisland->headJoint = joint->jointId;\n\tif ( island->tailJoint == B2_NULL_INDEX )\n\t{\n\t\tisland->tailJoint = island->headJoint;\n\t}\n\n\tisland->jointCount += 1;\n\tjoint->islandId = islandId;\n\n\tb2ValidateIsland( world, islandId );\n}\n\nvoid b2LinkJoint( b2World* world, b2Joint* joint )\n{\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );\n\n\tB2_ASSERT( bodyA->type == b2_dynamicBody || bodyB->type == b2_dynamicBody );\n\n\tif ( bodyA->setIndex == b2_awakeSet && bodyB->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeSolverSet( world, bodyB->setIndex );\n\t}\n\telse if ( bodyB->setIndex == b2_awakeSet && bodyA->setIndex >= b2_firstSleepingSet )\n\t{\n\t\tb2WakeSolverSet( world, bodyA->setIndex );\n\t}\n\n\tint islandIdA = bodyA->islandId;\n\tint islandIdB = bodyB->islandId;\n\n\tB2_ASSERT( islandIdA != B2_NULL_INDEX || islandIdB != B2_NULL_INDEX );\n\n\t// Merge islands. This will destroy one of the islands.\n\tint finalIslandId = b2MergeIslands( world, islandIdA, islandIdB );\n\n\t// Add joint the island that survived\n\tb2AddJointToIsland( world, finalIslandId, joint );\n}\n\nvoid b2UnlinkJoint( b2World* world, b2Joint* joint )\n{\n\tif ( joint->islandId == B2_NULL_INDEX )\n\t{\n\t\treturn;\n\t}\n\n\t// remove from island\n\tint islandId = joint->islandId;\n\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\n\tif ( joint->islandPrev != B2_NULL_INDEX )\n\t{\n\t\tb2Joint* prevJoint = b2JointArray_Get( &world->joints, joint->islandPrev );\n\t\tB2_ASSERT( prevJoint->islandNext == joint->jointId );\n\t\tprevJoint->islandNext = joint->islandNext;\n\t}\n\n\tif ( joint->islandNext != B2_NULL_INDEX )\n\t{\n\t\tb2Joint* nextJoint = b2JointArray_Get( &world->joints, joint->islandNext );\n\t\tB2_ASSERT( nextJoint->islandPrev == joint->jointId );\n\t\tnextJoint->islandPrev = joint->islandPrev;\n\t}\n\n\tif ( island->headJoint == joint->jointId )\n\t{\n\t\tisland->headJoint = joint->islandNext;\n\t}\n\n\tif ( island->tailJoint == joint->jointId )\n\t{\n\t\tisland->tailJoint = joint->islandPrev;\n\t}\n\n\tB2_ASSERT( island->jointCount > 0 );\n\tisland->jointCount -= 1;\n\tisland->constraintRemoveCount += 1;\n\n\tjoint->islandId = B2_NULL_INDEX;\n\tjoint->islandPrev = B2_NULL_INDEX;\n\tjoint->islandNext = B2_NULL_INDEX;\n\n\tb2ValidateIsland( world, islandId );\n}\n\n#define B2_CONTACT_REMOVE_THRESHOLD 1\n\n// Possible optimizations:\n// 2. start from the sleepy bodies and stop processing if a sleep body is connected to a non-sleepy body\n// 3. use a sleepy flag on bodies to avoid velocity access\nvoid b2SplitIsland( b2World* world, int baseId )\n{\n\tb2Island* baseIsland = b2IslandArray_Get( &world->islands, baseId );\n\tint setIndex = baseIsland->setIndex;\n\n\tif ( setIndex != b2_awakeSet )\n\t{\n\t\t// can only split awake island\n\t\treturn;\n\t}\n\n\tif ( baseIsland->constraintRemoveCount == 0 )\n\t{\n\t\t// this island doesn't need to be split\n\t\treturn;\n\t}\n\n\tb2ValidateIsland( world, baseId );\n\n\tint bodyCount = baseIsland->bodyCount;\n\n\tb2Body* bodies = world->bodies.data;\n\tb2ArenaAllocator* alloc = &world->arena;\n\n\t// No lock is needed because I ensure the allocator is not used while this task is active.\n\tint* stack = b2AllocateArenaItem( alloc, bodyCount * sizeof( int ), \"island stack\" );\n\tint* bodyIds = b2AllocateArenaItem( alloc, bodyCount * sizeof( int ), \"body ids\" );\n\n\t// Build array containing all body indices from base island. These\n\t// serve as seed bodies for the depth first search (DFS).\n\tint index = 0;\n\tint nextBody = baseIsland->headBody;\n\twhile ( nextBody != B2_NULL_INDEX )\n\t{\n\t\tbodyIds[index++] = nextBody;\n\t\tb2Body* body = bodies + nextBody;\n\n\t\tnextBody = body->islandNext;\n\t}\n\tB2_ASSERT( index == bodyCount );\n\n\t// Each island is found as a depth first search starting from a seed body\n\tfor ( int i = 0; i < bodyCount; ++i )\n\t{\n\t\tint seedIndex = bodyIds[i];\n\t\tb2Body* seed = bodies + seedIndex;\n\t\tB2_ASSERT( seed->setIndex == setIndex );\n\n\t\tif ( seed->islandId != baseId )\n\t\t{\n\t\t\t// The body has already been visited\n\t\t\tcontinue;\n\t\t}\n\n\t\tint stackCount = 0;\n\t\tstack[stackCount++] = seedIndex;\n\n\t\t// Create new island\n\t\t// No lock needed because only a single island can split per time step. No islands are being used during the constraint\n\t\t// solve. However, islands are touched during body finalization.\n\t\tb2Island* island = b2CreateIsland( world, setIndex );\n\n\t\tint islandId = island->islandId;\n\t\tseed->islandId = islandId;\n\n\t\t// Perform a depth first search (DFS) on the constraint graph.\n\t\twhile ( stackCount > 0 )\n\t\t{\n\t\t\t// Grab the next body off the stack and add it to the island.\n\t\t\tint bodyId = stack[--stackCount];\n\t\t\tb2Body* body = bodies + bodyId;\n\t\t\tB2_ASSERT( body->setIndex == b2_awakeSet );\n\t\t\tB2_ASSERT( body->islandId == islandId );\n\n\t\t\t// Add body to island\n\t\t\tif ( island->tailBody != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tbodies[island->tailBody].islandNext = bodyId;\n\t\t\t}\n\t\t\tbody->islandPrev = island->tailBody;\n\t\t\tbody->islandNext = B2_NULL_INDEX;\n\t\t\tisland->tailBody = bodyId;\n\n\t\t\tif ( island->headBody == B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tisland->headBody = bodyId;\n\t\t\t}\n\n\t\t\tisland->bodyCount += 1;\n\n\t\t\t// Search all contacts connected to this body.\n\t\t\tint contactKey = body->headContactKey;\n\t\t\twhile ( contactKey != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tint contactId = contactKey >> 1;\n\t\t\t\tint edgeIndex = contactKey & 1;\n\n\t\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\t\t\tB2_ASSERT( contact->contactId == contactId );\n\n\t\t\t\t// Next key\n\t\t\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\n\t\t\t\t// Has this contact already been added to this island?\n\t\t\t\tif ( contact->islandId == islandId )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Is this contact enabled and touching?\n\t\t\t\tif ( ( contact->flags & b2_contactTouchingFlag ) == 0 )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tint otherEdgeIndex = edgeIndex ^ 1;\n\t\t\t\tint otherBodyId = contact->edges[otherEdgeIndex].bodyId;\n\t\t\t\tb2Body* otherBody = bodies + otherBodyId;\n\n\t\t\t\t// Maybe add other body to stack\n\t\t\t\tif ( otherBody->islandId != islandId && otherBody->setIndex != b2_staticSet )\n\t\t\t\t{\n\t\t\t\t\tB2_ASSERT( stackCount < bodyCount );\n\t\t\t\t\tstack[stackCount++] = otherBodyId;\n\n\t\t\t\t\t// Need to update the body's island id immediately so it is not traversed again\n\t\t\t\t\totherBody->islandId = islandId;\n\t\t\t\t}\n\n\t\t\t\t// Add contact to island\n\t\t\t\tcontact->islandId = islandId;\n\t\t\t\tif ( island->tailContact != B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\tb2Contact* tailContact = b2ContactArray_Get( &world->contacts, island->tailContact );\n\t\t\t\t\ttailContact->islandNext = contactId;\n\t\t\t\t}\n\t\t\t\tcontact->islandPrev = island->tailContact;\n\t\t\t\tcontact->islandNext = B2_NULL_INDEX;\n\t\t\t\tisland->tailContact = contactId;\n\n\t\t\t\tif ( island->headContact == B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\tisland->headContact = contactId;\n\t\t\t\t}\n\n\t\t\t\tisland->contactCount += 1;\n\t\t\t}\n\n\t\t\t// Search all joints connect to this body.\n\t\t\tint jointKey = body->headJointKey;\n\t\t\twhile ( jointKey != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tint jointId = jointKey >> 1;\n\t\t\t\tint edgeIndex = jointKey & 1;\n\n\t\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\t\t\tB2_ASSERT( joint->jointId == jointId );\n\n\t\t\t\t// Next key\n\t\t\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\n\t\t\t\t// Has this joint already been added to this island?\n\t\t\t\tif ( joint->islandId == islandId )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// todo redundant with test below?\n\t\t\t\tif ( joint->setIndex == b2_disabledSet )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tint otherEdgeIndex = edgeIndex ^ 1;\n\t\t\t\tint otherBodyId = joint->edges[otherEdgeIndex].bodyId;\n\t\t\t\tb2Body* otherBody = bodies + otherBodyId;\n\n\t\t\t\t// Don't simulate joints connected to disabled bodies.\n\t\t\t\tif ( otherBody->setIndex == b2_disabledSet )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// At least one body must be dynamic\n\t\t\t\tif ( body->type != b2_dynamicBody && otherBody->type != b2_dynamicBody )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Maybe add other body to stack\n\t\t\t\tif ( otherBody->islandId != islandId && otherBody->setIndex == b2_awakeSet )\n\t\t\t\t{\n\t\t\t\t\tB2_ASSERT( stackCount < bodyCount );\n\t\t\t\t\tstack[stackCount++] = otherBodyId;\n\n\t\t\t\t\t// Need to update the body's island id immediately so it is not traversed again\n\t\t\t\t\totherBody->islandId = islandId;\n\t\t\t\t}\n\n\t\t\t\t// Add joint to island\n\t\t\t\tjoint->islandId = islandId;\n\t\t\t\tif ( island->tailJoint != B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\tb2Joint* tailJoint = b2JointArray_Get( &world->joints, island->tailJoint );\n\t\t\t\t\ttailJoint->islandNext = jointId;\n\t\t\t\t}\n\t\t\t\tjoint->islandPrev = island->tailJoint;\n\t\t\t\tjoint->islandNext = B2_NULL_INDEX;\n\t\t\t\tisland->tailJoint = jointId;\n\n\t\t\t\tif ( island->headJoint == B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\tisland->headJoint = jointId;\n\t\t\t\t}\n\n\t\t\t\tisland->jointCount += 1;\n\t\t\t}\n\t\t}\n\n\t\tb2ValidateIsland( world, islandId );\n\t}\n\n\t// Done with the base split island. This is delayed because the baseId is used as a marker and it\n\t// should not be recycled in while splitting.\n\tb2DestroyIsland( world, baseId );\n\n\tb2FreeArenaItem( alloc, bodyIds );\n\tb2FreeArenaItem( alloc, stack );\n}\n\n// Split an island because some contacts and/or joints have been removed.\n// This is called during the constraint solve while islands are not being touched. This uses DFS and touches a lot of memory,\n// so it can be quite slow.\n// Note: contacts/joints connected to static bodies must belong to an island but don't affect island connectivity\n// Note: static bodies are never in an island\n// Note: this task interacts with some allocators without locks under the assumption that no other tasks\n// are interacting with these data structures.\nvoid b2SplitIslandTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )\n{\n\tb2TracyCZoneNC( split, \"Split Island\", b2_colorOlive, true );\n\n\tB2_UNUSED( startIndex, endIndex, threadIndex );\n\n\tuint64_t ticks = b2GetTicks();\n\tb2World* world = context;\n\n\tB2_ASSERT( world->splitIslandId != B2_NULL_INDEX );\n\n\tb2SplitIsland( world, world->splitIslandId );\n\n\tworld->profile.splitIslands += b2GetMilliseconds( ticks );\n\tb2TracyCZoneEnd( split );\n}\n\n#if B2_VALIDATE\nvoid b2ValidateIsland( b2World* world, int islandId )\n{\n\tif ( islandId == B2_NULL_INDEX )\n\t{\n\t\treturn;\n\t}\n\n\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\tB2_ASSERT( island->islandId == islandId );\n\tB2_ASSERT( island->setIndex != B2_NULL_INDEX );\n\tB2_ASSERT( island->headBody != B2_NULL_INDEX );\n\n\t{\n\t\tB2_ASSERT( island->tailBody != B2_NULL_INDEX );\n\t\tB2_ASSERT( island->bodyCount > 0 );\n\t\tif ( island->bodyCount > 1 )\n\t\t{\n\t\t\tB2_ASSERT( island->tailBody != island->headBody );\n\t\t}\n\t\tB2_ASSERT( island->bodyCount <= b2GetIdCount( &world->bodyIdPool ) );\n\n\t\tint count = 0;\n\t\tint bodyId = island->headBody;\n\t\twhile ( bodyId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Body* body = b2BodyArray_Get( &world->bodies, bodyId );\n\t\t\tB2_ASSERT( body->islandId == islandId );\n\t\t\tB2_ASSERT( body->setIndex == island->setIndex );\n\t\t\tcount += 1;\n\n\t\t\tif ( count == island->bodyCount )\n\t\t\t{\n\t\t\t\tB2_ASSERT( bodyId == island->tailBody );\n\t\t\t}\n\n\t\t\tbodyId = body->islandNext;\n\t\t}\n\t\tB2_ASSERT( count == island->bodyCount );\n\t}\n\n\tif ( island->headContact != B2_NULL_INDEX )\n\t{\n\t\tB2_ASSERT( island->tailContact != B2_NULL_INDEX );\n\t\tB2_ASSERT( island->contactCount > 0 );\n\t\tif ( island->contactCount > 1 )\n\t\t{\n\t\t\tB2_ASSERT( island->tailContact != island->headContact );\n\t\t}\n\t\tB2_ASSERT( island->contactCount <= b2GetIdCount( &world->contactIdPool ) );\n\n\t\tint count = 0;\n\t\tint contactId = island->headContact;\n\t\twhile ( contactId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\t\tB2_ASSERT( contact->setIndex == island->setIndex );\n\t\t\tB2_ASSERT( contact->islandId == islandId );\n\t\t\tcount += 1;\n\n\t\t\tif ( count == island->contactCount )\n\t\t\t{\n\t\t\t\tB2_ASSERT( contactId == island->tailContact );\n\t\t\t}\n\n\t\t\tcontactId = contact->islandNext;\n\t\t}\n\t\tB2_ASSERT( count == island->contactCount );\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( island->tailContact == B2_NULL_INDEX );\n\t\tB2_ASSERT( island->contactCount == 0 );\n\t}\n\n\tif ( island->headJoint != B2_NULL_INDEX )\n\t{\n\t\tB2_ASSERT( island->tailJoint != B2_NULL_INDEX );\n\t\tB2_ASSERT( island->jointCount > 0 );\n\t\tif ( island->jointCount > 1 )\n\t\t{\n\t\t\tB2_ASSERT( island->tailJoint != island->headJoint );\n\t\t}\n\t\tB2_ASSERT( island->jointCount <= b2GetIdCount( &world->jointIdPool ) );\n\n\t\tint count = 0;\n\t\tint jointId = island->headJoint;\n\t\twhile ( jointId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\t\tB2_ASSERT( joint->setIndex == island->setIndex );\n\t\t\tcount += 1;\n\n\t\t\tif ( count == island->jointCount )\n\t\t\t{\n\t\t\t\tB2_ASSERT( jointId == island->tailJoint );\n\t\t\t}\n\n\t\t\tjointId = joint->islandNext;\n\t\t}\n\t\tB2_ASSERT( count == island->jointCount );\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( island->tailJoint == B2_NULL_INDEX );\n\t\tB2_ASSERT( island->jointCount == 0 );\n\t}\n}\n\n#else\n\nvoid b2ValidateIsland( b2World* world, int islandId )\n{\n\tB2_UNUSED( world );\n\tB2_UNUSED( islandId );\n}\n#endif\n"
  },
  {
    "path": "src/island.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n\n#include <stdbool.h>\n#include <stdint.h>\n\ntypedef struct b2Contact b2Contact;\ntypedef struct b2Joint b2Joint;\ntypedef struct b2World b2World;\n\n// Deterministic solver\n//\n// Collide all awake contacts\n// Use bit array to emit start/stop touching events in defined order, per thread. Try using contact index, assuming contacts are\n// created in a deterministic order. bit-wise OR together bit arrays and issue changes:\n// - start touching: merge islands - temporary linked list - mark root island dirty - wake all - largest island is root\n// - stop touching: increment constraintRemoveCount\n\n// Persistent island for awake bodies, joints, and contacts\n// https://en.wikipedia.org/wiki/Component_(graph_theory)\n// https://en.wikipedia.org/wiki/Dynamic_connectivity\n// map from int to solver set and index\ntypedef struct b2Island\n{\n\t// index of solver set stored in b2World\n\t// may be B2_NULL_INDEX\n\tint setIndex;\n\n\t// island index within set\n\t// may be B2_NULL_INDEX\n\tint localIndex;\n\n\tint islandId;\n\n\tint headBody;\n\tint tailBody;\n\tint bodyCount;\n\n\tint headContact;\n\tint tailContact;\n\tint contactCount;\n\n\tint headJoint;\n\tint tailJoint;\n\tint jointCount;\n\n\t// Keeps track of how many contacts have been removed from this island.\n\t// This is used to determine if an island is a candidate for splitting.\n\tint constraintRemoveCount;\n} b2Island;\n\n// This is used to move islands across solver sets\ntypedef struct b2IslandSim\n{\n\tint islandId;\n} b2IslandSim;\n\nb2Island* b2CreateIsland( b2World* world, int setIndex );\nvoid b2DestroyIsland( b2World* world, int islandId );\n\n// Link contacts into the island graph when it starts having contact points\nvoid b2LinkContact( b2World* world, b2Contact* contact );\n\n// Unlink contact from the island graph when it stops having contact points\nvoid b2UnlinkContact( b2World* world, b2Contact* contact );\n\n// Link a joint into the island graph when it is created\nvoid b2LinkJoint( b2World* world, b2Joint* joint );\n\n// Unlink a joint from the island graph when it is destroyed\nvoid b2UnlinkJoint( b2World* world, b2Joint* joint );\n\nvoid b2SplitIsland( b2World* world, int baseId );\nvoid b2SplitIslandTask( int startIndex, int endIndex, uint32_t threadIndex, void* context );\n\nvoid b2ValidateIsland( b2World* world, int islandId );\n\nB2_ARRAY_INLINE( b2Island, b2Island )\nB2_ARRAY_INLINE( b2IslandSim, b2IslandSim )\n"
  },
  {
    "path": "src/joint.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"joint.h\"\n\n#include \"body.h\"\n#include \"contact.h\"\n#include \"core.h\"\n#include \"island.h\"\n#include \"physics_world.h\"\n#include \"shape.h\"\n#include \"solver.h\"\n#include \"solver_set.h\"\n\n// needed for dll export\n#include \"box2d/box2d.h\"\n\n#include <stddef.h>\n#include <stdio.h>\n#include <string.h>\n\nB2_ARRAY_SOURCE( b2Joint, b2Joint )\nB2_ARRAY_SOURCE( b2JointSim, b2JointSim )\nB2_ARRAY_SOURCE( b2JointEvent, b2JointEvent )\n\nstatic b2JointDef b2DefaultJointDef( void )\n{\n\tb2JointDef def = { 0 };\n\tdef.localFrameA.q = b2Rot_identity;\n\tdef.localFrameB.q = b2Rot_identity;\n\tdef.forceThreshold = FLT_MAX;\n\tdef.torqueThreshold = FLT_MAX;\n\tdef.constraintHertz = 60.0f;\n\tdef.constraintDampingRatio = 2.0f;\n\tdef.drawScale = b2_lengthUnitsPerMeter;\n\treturn def;\n}\n\nb2DistanceJointDef b2DefaultDistanceJointDef( void )\n{\n\tb2DistanceJointDef def = { 0 };\n\tdef.base = b2DefaultJointDef();\n\tdef.lowerSpringForce = -FLT_MAX;\n\tdef.upperSpringForce = FLT_MAX;\n\tdef.length = 1.0f;\n\tdef.maxLength = B2_HUGE;\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2MotorJointDef b2DefaultMotorJointDef( void )\n{\n\tb2MotorJointDef def = { 0 };\n\tdef.base = b2DefaultJointDef();\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2FilterJointDef b2DefaultFilterJointDef( void )\n{\n\tb2FilterJointDef def = { 0 };\n\tdef.base = b2DefaultJointDef();\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2PrismaticJointDef b2DefaultPrismaticJointDef( void )\n{\n\tb2PrismaticJointDef def = { 0 };\n\tdef.base = b2DefaultJointDef();\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2RevoluteJointDef b2DefaultRevoluteJointDef( void )\n{\n\tb2RevoluteJointDef def = { 0 };\n\tdef.base = b2DefaultJointDef();\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2WeldJointDef b2DefaultWeldJointDef( void )\n{\n\tb2WeldJointDef def = { 0 };\n\tdef.base = b2DefaultJointDef();\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2WheelJointDef b2DefaultWheelJointDef( void )\n{\n\tb2WheelJointDef def = { 0 };\n\tdef.base = b2DefaultJointDef();\n\tdef.enableSpring = true;\n\tdef.hertz = 1.0f;\n\tdef.dampingRatio = 0.7f;\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2ExplosionDef b2DefaultExplosionDef( void )\n{\n\tb2ExplosionDef def = { 0 };\n\tdef.maskBits = B2_DEFAULT_MASK_BITS;\n\treturn def;\n}\n\nb2Joint* b2GetJointFullId( b2World* world, b2JointId jointId )\n{\n\tint id = jointId.index1 - 1;\n\tb2Joint* joint = b2JointArray_Get( &world->joints, id );\n\tB2_ASSERT( joint->jointId == id && joint->generation == jointId.generation );\n\treturn joint;\n}\n\nb2JointSim* b2GetJointSim( b2World* world, b2Joint* joint )\n{\n\tif ( joint->setIndex == b2_awakeSet )\n\t{\n\t\tB2_ASSERT( 0 <= joint->colorIndex && joint->colorIndex < B2_GRAPH_COLOR_COUNT );\n\t\tb2GraphColor* color = world->constraintGraph.colors + joint->colorIndex;\n\t\treturn b2JointSimArray_Get( &color->jointSims, joint->localIndex );\n\t}\n\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, joint->setIndex );\n\treturn b2JointSimArray_Get( &set->jointSims, joint->localIndex );\n}\n\nb2JointSim* b2GetJointSimCheckType( b2JointId jointId, b2JointType type )\n{\n\tB2_UNUSED( type );\n\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn NULL;\n\t}\n\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tB2_ASSERT( joint->type == type );\n\tb2JointSim* jointSim = b2GetJointSim( world, joint );\n\tB2_ASSERT( jointSim->type == type );\n\treturn jointSim;\n}\n\nstatic void b2DestroyContactsBetweenBodies( b2World* world, b2Body* bodyA, b2Body* bodyB )\n{\n\tint contactKey;\n\tint otherBodyId;\n\n\t// use the smaller of the two contact lists\n\tif ( bodyA->contactCount < bodyB->contactCount )\n\t{\n\t\tcontactKey = bodyA->headContactKey;\n\t\totherBodyId = bodyB->id;\n\t}\n\telse\n\t{\n\t\tcontactKey = bodyB->headContactKey;\n\t\totherBodyId = bodyA->id;\n\t}\n\n\t// no need to wake bodies when a joint removes collision between them\n\tbool wakeBodies = false;\n\n\t// destroy the contacts\n\twhile ( contactKey != B2_NULL_INDEX )\n\t{\n\t\tint contactId = contactKey >> 1;\n\t\tint edgeIndex = contactKey & 1;\n\n\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\n\t\tint otherEdgeIndex = edgeIndex ^ 1;\n\t\tif ( contact->edges[otherEdgeIndex].bodyId == otherBodyId )\n\t\t{\n\t\t\t// Careful, this removes the contact from the current doubly linked list\n\t\t\tb2DestroyContact( world, contact, wakeBodies );\n\t\t}\n\t}\n\n\tb2ValidateSolverSets( world );\n}\n\ntypedef struct b2JointPair\n{\n\tb2Joint* joint;\n\tb2JointSim* jointSim;\n} b2JointPair;\n\nstatic b2JointPair b2CreateJoint( b2World* world, const b2JointDef* def, b2JointType type )\n{\n\tB2_ASSERT( b2IsValidTransform( def->localFrameA ) );\n\tB2_ASSERT( b2IsValidTransform( def->localFrameB ) );\n\tB2_ASSERT( world->worldId == def->bodyIdA.world0 );\n\tB2_ASSERT( world->worldId == def->bodyIdB.world0 );\n\tB2_ASSERT( B2_ID_EQUALS( def->bodyIdA, def->bodyIdB ) == false );\n\n\tb2Body* bodyA = b2GetBodyFullId( world, def->bodyIdA );\n\tb2Body* bodyB = b2GetBodyFullId( world, def->bodyIdB );\n\n\tint bodyIdA = bodyA->id;\n\tint bodyIdB = bodyB->id;\n\tint maxSetIndex = b2MaxInt( bodyA->setIndex, bodyB->setIndex );\n\n\t// Create joint id and joint\n\tint jointId = b2AllocId( &world->jointIdPool );\n\tif ( jointId == world->joints.count )\n\t{\n\t\tb2JointArray_Push( &world->joints, (b2Joint){ 0 } );\n\t}\n\n\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\tjoint->jointId = jointId;\n\tjoint->userData = def->userData;\n\tjoint->generation += 1;\n\tjoint->setIndex = B2_NULL_INDEX;\n\tjoint->colorIndex = B2_NULL_INDEX;\n\tjoint->localIndex = B2_NULL_INDEX;\n\tjoint->islandId = B2_NULL_INDEX;\n\tjoint->islandPrev = B2_NULL_INDEX;\n\tjoint->islandNext = B2_NULL_INDEX;\n\tjoint->drawScale = def->drawScale;\n\tjoint->type = type;\n\tjoint->collideConnected = def->collideConnected;\n\t//joint->isMarked = false;\n\n\t// Doubly linked list on bodyA\n\tjoint->edges[0].bodyId = bodyIdA;\n\tjoint->edges[0].prevKey = B2_NULL_INDEX;\n\tjoint->edges[0].nextKey = bodyA->headJointKey;\n\n\tint keyA = ( jointId << 1 ) | 0;\n\tif ( bodyA->headJointKey != B2_NULL_INDEX )\n\t{\n\t\tb2Joint* jointA = b2JointArray_Get( &world->joints, bodyA->headJointKey >> 1 );\n\t\tb2JointEdge* edgeA = jointA->edges + ( bodyA->headJointKey & 1 );\n\t\tedgeA->prevKey = keyA;\n\t}\n\tbodyA->headJointKey = keyA;\n\tbodyA->jointCount += 1;\n\n\t// Doubly linked list on bodyB\n\tjoint->edges[1].bodyId = bodyIdB;\n\tjoint->edges[1].prevKey = B2_NULL_INDEX;\n\tjoint->edges[1].nextKey = bodyB->headJointKey;\n\n\tint keyB = ( jointId << 1 ) | 1;\n\tif ( bodyB->headJointKey != B2_NULL_INDEX )\n\t{\n\t\tb2Joint* jointB = b2JointArray_Get( &world->joints, bodyB->headJointKey >> 1 );\n\t\tb2JointEdge* edgeB = jointB->edges + ( bodyB->headJointKey & 1 );\n\t\tedgeB->prevKey = keyB;\n\t}\n\tbodyB->headJointKey = keyB;\n\tbodyB->jointCount += 1;\n\n\tb2JointSim* jointSim;\n\n\tif ( bodyA->setIndex == b2_disabledSet || bodyB->setIndex == b2_disabledSet )\n\t{\n\t\t// if either body is disabled, create in disabled set\n\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet );\n\t\tjoint->setIndex = b2_disabledSet;\n\t\tjoint->localIndex = set->jointSims.count;\n\n\t\tjointSim = b2JointSimArray_Add( &set->jointSims );\n\t\tmemset( jointSim, 0, sizeof( b2JointSim ) );\n\n\t\tjointSim->jointId = jointId;\n\t\tjointSim->bodyIdA = bodyIdA;\n\t\tjointSim->bodyIdB = bodyIdB;\n\t}\n\telse if ( bodyA->type != b2_dynamicBody && bodyB->type != b2_dynamicBody )\n\t{\n\t\t// joint is not attached to a dynamic body\n\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_staticSet );\n\t\tjoint->setIndex = b2_staticSet;\n\t\tjoint->localIndex = set->jointSims.count;\n\n\t\tjointSim = b2JointSimArray_Add( &set->jointSims );\n\t\tmemset( jointSim, 0, sizeof( b2JointSim ) );\n\n\t\tjointSim->jointId = jointId;\n\t\tjointSim->bodyIdA = bodyIdA;\n\t\tjointSim->bodyIdB = bodyIdB;\n\t}\n\telse if ( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet )\n\t{\n\t\t// if either body is sleeping, wake it\n\t\tif ( maxSetIndex >= b2_firstSleepingSet )\n\t\t{\n\t\t\tb2WakeSolverSet( world, maxSetIndex );\n\t\t}\n\n\t\tjoint->setIndex = b2_awakeSet;\n\n\t\tjointSim = b2CreateJointInGraph( world, joint );\n\t\tjointSim->jointId = jointId;\n\t\tjointSim->bodyIdA = bodyIdA;\n\t\tjointSim->bodyIdB = bodyIdB;\n\t}\n\telse\n\t{\n\t\t// joint connected between sleeping and/or static bodies\n\t\tB2_ASSERT( bodyA->setIndex >= b2_firstSleepingSet || bodyB->setIndex >= b2_firstSleepingSet );\n\t\tB2_ASSERT( bodyA->setIndex != b2_staticSet || bodyB->setIndex != b2_staticSet );\n\n\t\t// joint should go into the sleeping set (not static set)\n\t\tint setIndex = maxSetIndex;\n\n\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );\n\t\tjoint->setIndex = setIndex;\n\t\tjoint->localIndex = set->jointSims.count;\n\n\t\tjointSim = b2JointSimArray_Add( &set->jointSims );\n\t\tmemset( jointSim, 0, sizeof( b2JointSim ) );\n\n\t\t// These must be set to accommodate the merge below\n\t\tjointSim->jointId = jointId;\n\t\tjointSim->bodyIdA = bodyIdA;\n\t\tjointSim->bodyIdB = bodyIdB;\n\n\t\tif ( bodyA->setIndex != bodyB->setIndex && bodyA->setIndex >= b2_firstSleepingSet &&\n\t\t\t bodyB->setIndex >= b2_firstSleepingSet )\n\t\t{\n\t\t\t// merge sleeping sets\n\t\t\tb2MergeSolverSets( world, bodyA->setIndex, bodyB->setIndex );\n\t\t\tB2_ASSERT( bodyA->setIndex == bodyB->setIndex );\n\n\t\t\t// fix potentially invalid set index\n\t\t\tsetIndex = bodyA->setIndex;\n\n\t\t\tb2SolverSet* mergedSet = b2SolverSetArray_Get( &world->solverSets, setIndex );\n\n\t\t\t// Careful! The joint sim pointer was orphaned by the set merge.\n\t\t\tjointSim = b2JointSimArray_Get( &mergedSet->jointSims, joint->localIndex );\n\t\t}\n\n\t\tB2_ASSERT( joint->setIndex == setIndex );\n\t}\n\n\tjointSim->localFrameA = def->localFrameA;\n\tjointSim->localFrameB = def->localFrameB;\n\tjointSim->type = type;\n\tjointSim->constraintHertz = def->constraintHertz;\n\tjointSim->constraintDampingRatio = def->constraintDampingRatio;\n\tjointSim->constraintSoftness = (b2Softness){\n\t\t.biasRate = 0.0f,\n\t\t.massScale = 1.0f,\n\t\t.impulseScale = 0.0f,\n\t};\n\n\tB2_ASSERT( b2IsValidFloat( def->forceThreshold ) && def->forceThreshold >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( def->torqueThreshold ) && def->torqueThreshold >= 0.0f );\n\n\tjointSim->forceThreshold = def->forceThreshold;\n\tjointSim->torqueThreshold = def->torqueThreshold;\n\n\tB2_ASSERT( jointSim->jointId == jointId );\n\tB2_ASSERT( jointSim->bodyIdA == bodyIdA );\n\tB2_ASSERT( jointSim->bodyIdB == bodyIdB );\n\n\tif ( joint->setIndex > b2_disabledSet )\n\t{\n\t\t// Add edge to island graph\n\t\tb2LinkJoint( world, joint );\n\t}\n\n\t// If the joint prevents collisions, then destroy all contacts between attached bodies\n\tif ( def->collideConnected == false )\n\t{\n\t\tb2DestroyContactsBetweenBodies( world, bodyA, bodyB );\n\t}\n\n\tb2ValidateSolverSets( world );\n\n\treturn (b2JointPair){ joint, jointSim };\n}\n\nb2JointId b2CreateDistanceJoint( b2WorldId worldId, const b2DistanceJointDef* def )\n{\n\tB2_CHECK_DEF( def );\n\tb2World* world = b2GetWorldFromId( worldId );\n\n\tB2_ASSERT( world->locked == false );\n\n\tif ( world->locked )\n\t{\n\t\treturn (b2JointId){ 0 };\n\t}\n\n\tB2_ASSERT( b2IsValidFloat( def->length ) && def->length > 0.0f );\n\tB2_ASSERT( def->lowerSpringForce <= def->upperSpringForce );\n\n\tb2JointPair pair = b2CreateJoint( world, &def->base, b2_distanceJoint );\n\n\tb2JointSim* joint = pair.jointSim;\n\n\tb2DistanceJoint empty = { 0 };\n\tjoint->distanceJoint = empty;\n\tjoint->distanceJoint.length = b2MaxFloat( def->length, B2_LINEAR_SLOP );\n\tjoint->distanceJoint.hertz = def->hertz;\n\tjoint->distanceJoint.dampingRatio = def->dampingRatio;\n\tjoint->distanceJoint.minLength = b2MaxFloat( def->minLength, B2_LINEAR_SLOP );\n\tjoint->distanceJoint.maxLength = b2MaxFloat( def->minLength, def->maxLength );\n\tjoint->distanceJoint.maxMotorForce = def->maxMotorForce;\n\tjoint->distanceJoint.motorSpeed = def->motorSpeed;\n\tjoint->distanceJoint.enableSpring = def->enableSpring;\n\tjoint->distanceJoint.lowerSpringForce = def->lowerSpringForce;\n\tjoint->distanceJoint.upperSpringForce = def->upperSpringForce;\n\tjoint->distanceJoint.enableLimit = def->enableLimit;\n\tjoint->distanceJoint.enableMotor = def->enableMotor;\n\tjoint->distanceJoint.impulse = 0.0f;\n\tjoint->distanceJoint.lowerImpulse = 0.0f;\n\tjoint->distanceJoint.upperImpulse = 0.0f;\n\tjoint->distanceJoint.motorImpulse = 0.0f;\n\n\tb2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->generation };\n\treturn jointId;\n}\n\nb2JointId b2CreateMotorJoint( b2WorldId worldId, const b2MotorJointDef* def )\n{\n\tB2_CHECK_DEF( def );\n\tb2World* world = b2GetWorldFromId( worldId );\n\n\tB2_ASSERT( world->locked == false );\n\n\tif ( world->locked )\n\t{\n\t\treturn (b2JointId){ 0 };\n\t}\n\n\tb2JointPair pair = b2CreateJoint( world, &def->base, b2_motorJoint );\n\tb2JointSim* joint = pair.jointSim;\n\n\tjoint->motorJoint = (b2MotorJoint){ 0 };\n\tjoint->motorJoint.linearVelocity = def->linearVelocity;\n\tjoint->motorJoint.maxVelocityForce = def->maxVelocityForce;\n\tjoint->motorJoint.angularVelocity = def->angularVelocity;\n\tjoint->motorJoint.maxVelocityTorque = def->maxVelocityTorque;\n\tjoint->motorJoint.linearHertz = def->linearHertz;\n\tjoint->motorJoint.linearDampingRatio = def->linearDampingRatio;\n\tjoint->motorJoint.maxSpringForce = def->maxSpringForce;\n\tjoint->motorJoint.angularHertz = def->angularHertz;\n\tjoint->motorJoint.angularDampingRatio = def->angularDampingRatio;\n\tjoint->motorJoint.maxSpringTorque = def->maxSpringTorque;\n\n\tb2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->generation };\n\treturn jointId;\n}\n\nb2JointId b2CreateFilterJoint( b2WorldId worldId, const b2FilterJointDef* def )\n{\n\tB2_CHECK_DEF( def );\n\tb2World* world = b2GetWorldFromId( worldId );\n\n\tB2_ASSERT( world->locked == false );\n\n\tif ( world->locked )\n\t{\n\t\treturn (b2JointId){ 0 };\n\t}\n\n\tb2JointPair pair = b2CreateJoint( world, &def->base, b2_filterJoint );\n\n\tb2JointSim* joint = pair.jointSim;\n\n\tb2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->generation };\n\treturn jointId;\n}\n\nb2JointId b2CreatePrismaticJoint( b2WorldId worldId, const b2PrismaticJointDef* def )\n{\n\tB2_CHECK_DEF( def );\n\tB2_ASSERT( def->lowerTranslation <= def->upperTranslation );\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\n\tB2_ASSERT( world->locked == false );\n\n\tif ( world->locked )\n\t{\n\t\treturn (b2JointId){ 0 };\n\t}\n\n\tb2JointPair pair = b2CreateJoint( world, &def->base, b2_prismaticJoint );\n\n\tb2JointSim* joint = pair.jointSim;\n\n\tjoint->prismaticJoint = (b2PrismaticJoint){ 0 };\n\tjoint->prismaticJoint.hertz = def->hertz;\n\tjoint->prismaticJoint.dampingRatio = def->dampingRatio;\n\tjoint->prismaticJoint.targetTranslation = def->targetTranslation;\n\tjoint->prismaticJoint.lowerTranslation = def->lowerTranslation;\n\tjoint->prismaticJoint.upperTranslation = def->upperTranslation;\n\tjoint->prismaticJoint.maxMotorForce = def->maxMotorForce;\n\tjoint->prismaticJoint.motorSpeed = def->motorSpeed;\n\tjoint->prismaticJoint.enableSpring = def->enableSpring;\n\tjoint->prismaticJoint.enableLimit = def->enableLimit;\n\tjoint->prismaticJoint.enableMotor = def->enableMotor;\n\n\tb2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->generation };\n\treturn jointId;\n}\n\nb2JointId b2CreateRevoluteJoint( b2WorldId worldId, const b2RevoluteJointDef* def )\n{\n\tB2_CHECK_DEF( def );\n\tB2_ASSERT( def->lowerAngle <= def->upperAngle );\n\tB2_ASSERT( def->lowerAngle >= -0.99f * B2_PI );\n\tB2_ASSERT( def->upperAngle <= 0.99f * B2_PI );\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\n\tB2_ASSERT( world->locked == false );\n\n\tif ( world->locked )\n\t{\n\t\treturn (b2JointId){ 0 };\n\t}\n\n\tb2JointPair pair = b2CreateJoint( world, &def->base, b2_revoluteJoint );\n\n\tb2JointSim* joint = pair.jointSim;\n\n\tb2RevoluteJoint empty = { 0 };\n\tjoint->revoluteJoint = empty;\n\n\tjoint->revoluteJoint.targetAngle = b2ClampFloat( def->targetAngle, -B2_PI, B2_PI );\n\tjoint->revoluteJoint.hertz = def->hertz;\n\tjoint->revoluteJoint.dampingRatio = def->dampingRatio;\n\tjoint->revoluteJoint.lowerAngle = def->lowerAngle;\n\tjoint->revoluteJoint.upperAngle = def->upperAngle;\n\tjoint->revoluteJoint.maxMotorTorque = def->maxMotorTorque;\n\tjoint->revoluteJoint.motorSpeed = def->motorSpeed;\n\tjoint->revoluteJoint.enableSpring = def->enableSpring;\n\tjoint->revoluteJoint.enableLimit = def->enableLimit;\n\tjoint->revoluteJoint.enableMotor = def->enableMotor;\n\n\tb2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->generation };\n\treturn jointId;\n}\n\nb2JointId b2CreateWeldJoint( b2WorldId worldId, const b2WeldJointDef* def )\n{\n\tB2_CHECK_DEF( def );\n\tb2World* world = b2GetWorldFromId( worldId );\n\n\tB2_ASSERT( world->locked == false );\n\n\tif ( world->locked )\n\t{\n\t\treturn (b2JointId){ 0 };\n\t}\n\n\tb2JointPair pair = b2CreateJoint( world, &def->base, b2_weldJoint );\n\n\tb2JointSim* joint = pair.jointSim;\n\n\tb2WeldJoint empty = { 0 };\n\tjoint->weldJoint = empty;\n\tjoint->weldJoint.linearHertz = def->linearHertz;\n\tjoint->weldJoint.linearDampingRatio = def->linearDampingRatio;\n\tjoint->weldJoint.angularHertz = def->angularHertz;\n\tjoint->weldJoint.angularDampingRatio = def->angularDampingRatio;\n\tjoint->weldJoint.linearImpulse = b2Vec2_zero;\n\tjoint->weldJoint.angularImpulse = 0.0f;\n\n\tb2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->generation };\n\treturn jointId;\n}\n\nb2JointId b2CreateWheelJoint( b2WorldId worldId, const b2WheelJointDef* def )\n{\n\tB2_CHECK_DEF( def );\n\tB2_ASSERT( def->lowerTranslation <= def->upperTranslation );\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\n\tB2_ASSERT( world->locked == false );\n\n\tif ( world->locked )\n\t{\n\t\treturn (b2JointId){ 0 };\n\t}\n\n\tb2JointPair pair = b2CreateJoint( world, &def->base, b2_wheelJoint );\n\n\tb2JointSim* joint = pair.jointSim;\n\n\tjoint->wheelJoint = (b2WheelJoint){ 0 };\n\tjoint->wheelJoint.perpMass = 0.0f;\n\tjoint->wheelJoint.axialMass = 0.0f;\n\tjoint->wheelJoint.motorImpulse = 0.0f;\n\tjoint->wheelJoint.lowerImpulse = 0.0f;\n\tjoint->wheelJoint.upperImpulse = 0.0f;\n\tjoint->wheelJoint.lowerTranslation = def->lowerTranslation;\n\tjoint->wheelJoint.upperTranslation = def->upperTranslation;\n\tjoint->wheelJoint.maxMotorTorque = def->maxMotorTorque;\n\tjoint->wheelJoint.motorSpeed = def->motorSpeed;\n\tjoint->wheelJoint.hertz = def->hertz;\n\tjoint->wheelJoint.dampingRatio = def->dampingRatio;\n\tjoint->wheelJoint.enableSpring = def->enableSpring;\n\tjoint->wheelJoint.enableLimit = def->enableLimit;\n\tjoint->wheelJoint.enableMotor = def->enableMotor;\n\n\tb2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->generation };\n\treturn jointId;\n}\n\nvoid b2DestroyJointInternal( b2World* world, b2Joint* joint, bool wakeBodies )\n{\n\tint jointId = joint->jointId;\n\n\tb2JointEdge* edgeA = joint->edges + 0;\n\tb2JointEdge* edgeB = joint->edges + 1;\n\n\tint idA = edgeA->bodyId;\n\tint idB = edgeB->bodyId;\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, idA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, idB );\n\n\t// Remove from body A\n\tif ( edgeA->prevKey != B2_NULL_INDEX )\n\t{\n\t\tb2Joint* prevJoint = b2JointArray_Get( &world->joints, edgeA->prevKey >> 1 );\n\t\tb2JointEdge* prevEdge = prevJoint->edges + ( edgeA->prevKey & 1 );\n\t\tprevEdge->nextKey = edgeA->nextKey;\n\t}\n\n\tif ( edgeA->nextKey != B2_NULL_INDEX )\n\t{\n\t\tb2Joint* nextJoint = b2JointArray_Get( &world->joints, edgeA->nextKey >> 1 );\n\t\tb2JointEdge* nextEdge = nextJoint->edges + ( edgeA->nextKey & 1 );\n\t\tnextEdge->prevKey = edgeA->prevKey;\n\t}\n\n\tint edgeKeyA = ( jointId << 1 ) | 0;\n\tif ( bodyA->headJointKey == edgeKeyA )\n\t{\n\t\tbodyA->headJointKey = edgeA->nextKey;\n\t}\n\n\tbodyA->jointCount -= 1;\n\n\t// Remove from body B\n\tif ( edgeB->prevKey != B2_NULL_INDEX )\n\t{\n\t\tb2Joint* prevJoint = b2JointArray_Get( &world->joints, edgeB->prevKey >> 1 );\n\t\tb2JointEdge* prevEdge = prevJoint->edges + ( edgeB->prevKey & 1 );\n\t\tprevEdge->nextKey = edgeB->nextKey;\n\t}\n\n\tif ( edgeB->nextKey != B2_NULL_INDEX )\n\t{\n\t\tb2Joint* nextJoint = b2JointArray_Get( &world->joints, edgeB->nextKey >> 1 );\n\t\tb2JointEdge* nextEdge = nextJoint->edges + ( edgeB->nextKey & 1 );\n\t\tnextEdge->prevKey = edgeB->prevKey;\n\t}\n\n\tint edgeKeyB = ( jointId << 1 ) | 1;\n\tif ( bodyB->headJointKey == edgeKeyB )\n\t{\n\t\tbodyB->headJointKey = edgeB->nextKey;\n\t}\n\n\tbodyB->jointCount -= 1;\n\n\tif ( joint->islandId != B2_NULL_INDEX )\n\t{\n\t\tB2_ASSERT( joint->setIndex > b2_disabledSet );\n\t\tb2UnlinkJoint( world, joint );\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( joint->setIndex <= b2_disabledSet );\n\t}\n\n\t// Remove joint from solver set that owns it\n\tint setIndex = joint->setIndex;\n\tint localIndex = joint->localIndex;\n\n\tif ( setIndex == b2_awakeSet )\n\t{\n\t\tb2RemoveJointFromGraph( world, joint->edges[0].bodyId, joint->edges[1].bodyId, joint->colorIndex, localIndex );\n\t}\n\telse\n\t{\n\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );\n\t\tint movedIndex = b2JointSimArray_RemoveSwap( &set->jointSims, localIndex );\n\t\tif ( movedIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\t// Fix moved joint\n\t\t\tb2JointSim* movedJointSim = set->jointSims.data + localIndex;\n\t\t\tint movedId = movedJointSim->jointId;\n\t\t\tb2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId );\n\t\t\tB2_ASSERT( movedJoint->localIndex == movedIndex );\n\t\t\tmovedJoint->localIndex = localIndex;\n\t\t}\n\t}\n\n\t// Free joint and id (preserve joint generation)\n\tjoint->setIndex = B2_NULL_INDEX;\n\tjoint->localIndex = B2_NULL_INDEX;\n\tjoint->colorIndex = B2_NULL_INDEX;\n\tjoint->jointId = B2_NULL_INDEX;\n\tb2FreeId( &world->jointIdPool, jointId );\n\n\tif ( wakeBodies )\n\t{\n\t\tb2WakeBody( world, bodyA );\n\t\tb2WakeBody( world, bodyB );\n\t}\n\n\tb2ValidateSolverSets( world );\n}\n\nvoid b2DestroyJoint( b2JointId jointId, bool wakeAttached )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tB2_ASSERT( world->locked == false );\n\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\n\tb2DestroyJointInternal( world, joint, wakeAttached );\n}\n\nb2JointType b2Joint_GetType( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\treturn joint->type;\n}\n\nb2BodyId b2Joint_GetBodyA( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\treturn b2MakeBodyId( world, joint->edges[0].bodyId );\n}\n\nb2BodyId b2Joint_GetBodyB( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\treturn b2MakeBodyId( world, joint->edges[1].bodyId );\n}\n\nb2WorldId b2Joint_GetWorld( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\treturn (b2WorldId){ jointId.world0 + 1, world->generation };\n}\n\nvoid b2Joint_SetLocalFrameA( b2JointId jointId, b2Transform localFrame )\n{\n\tB2_ASSERT( b2IsValidTransform( localFrame ) );\n\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* jointSim = b2GetJointSim( world, joint );\n\tjointSim->localFrameA = localFrame;\n}\n\nb2Transform b2Joint_GetLocalFrameA( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* jointSim = b2GetJointSim( world, joint );\n\treturn jointSim->localFrameA;\n}\n\nvoid b2Joint_SetLocalFrameB( b2JointId jointId, b2Transform localFrame )\n{\n\tB2_ASSERT( b2IsValidTransform( localFrame ) );\n\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* jointSim = b2GetJointSim( world, joint );\n\tjointSim->localFrameB = localFrame;\n}\n\nb2Transform b2Joint_GetLocalFrameB( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* jointSim = b2GetJointSim( world, joint );\n\treturn jointSim->localFrameB;\n}\n\nvoid b2Joint_SetCollideConnected( b2JointId jointId, bool shouldCollide )\n{\n\tb2World* world = b2GetWorldLocked( jointId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tif ( joint->collideConnected == shouldCollide )\n\t{\n\t\treturn;\n\t}\n\n\tjoint->collideConnected = shouldCollide;\n\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );\n\n\tif ( shouldCollide )\n\t{\n\t\t// need to tell the broad-phase to look for new pairs for one of the\n\t\t// two bodies. Pick the one with the fewest shapes.\n\t\tint shapeCountA = bodyA->shapeCount;\n\t\tint shapeCountB = bodyB->shapeCount;\n\n\t\tint shapeId = shapeCountA < shapeCountB ? bodyA->headShapeId : bodyB->headShapeId;\n\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\t\t\tif ( shape->proxyKey != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tb2BufferMove( &world->broadPhase, shape->proxyKey );\n\t\t\t}\n\n\t\t\tshapeId = shape->nextShapeId;\n\t\t}\n\t}\n\telse\n\t{\n\t\tb2DestroyContactsBetweenBodies( world, bodyA, bodyB );\n\t}\n}\n\nbool b2Joint_GetCollideConnected( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\treturn joint->collideConnected;\n}\n\nvoid b2Joint_SetUserData( b2JointId jointId, void* userData )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tjoint->userData = userData;\n}\n\nvoid* b2Joint_GetUserData( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\treturn joint->userData;\n}\n\nvoid b2Joint_WakeBodies( b2JointId jointId )\n{\n\tb2World* world = b2GetWorldLocked( jointId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );\n\n\tb2WakeBody( world, bodyA );\n\tb2WakeBody( world, bodyB );\n}\n\nvoid b2GetJointReaction( b2JointSim* sim, float invTimeStep, float* force, float* torque )\n{\n\tfloat linearImpulse = 0.0f;\n\tfloat angularImpulse = 0.0f;\n\n\tswitch ( sim->type )\n\t{\n\t\tcase b2_distanceJoint:\n\t\t{\n\t\t\tb2DistanceJoint* joint = &sim->distanceJoint;\n\t\t\tlinearImpulse = b2AbsFloat( joint->impulse + joint->lowerImpulse - joint->upperImpulse + joint->motorImpulse );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_motorJoint:\n\t\t{\n\t\t\tb2MotorJoint* joint = &sim->motorJoint;\n\t\t\tlinearImpulse = b2Length( b2Add(joint->linearVelocityImpulse, joint->linearSpringImpulse) );\n\t\t\tangularImpulse = b2AbsFloat( joint->angularVelocityImpulse + joint->angularSpringImpulse );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_prismaticJoint:\n\t\t{\n\t\t\tb2PrismaticJoint* joint = &sim->prismaticJoint;\n\t\t\tfloat perpImpulse = joint->impulse.x;\n\t\t\tfloat axialImpulse = joint->motorImpulse + joint->lowerImpulse - joint->upperImpulse;\n\t\t\tlinearImpulse = sqrtf( perpImpulse * perpImpulse + axialImpulse * axialImpulse );\n\t\t\tangularImpulse = b2AbsFloat( joint->impulse.y );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_revoluteJoint:\n\t\t{\n\t\t\tb2RevoluteJoint* joint = &sim->revoluteJoint;\n\n\t\t\tlinearImpulse = b2Length( joint->linearImpulse );\n\t\t\tangularImpulse = b2AbsFloat( joint->motorImpulse + joint->lowerImpulse - joint->upperImpulse );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_weldJoint:\n\t\t{\n\t\t\tb2WeldJoint* joint = &sim->weldJoint;\n\t\t\tlinearImpulse = b2Length( joint->linearImpulse );\n\t\t\tangularImpulse = b2AbsFloat( joint->angularImpulse );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_wheelJoint:\n\t\t{\n\t\t\tb2WheelJoint* joint = &sim->wheelJoint;\n\t\t\tfloat perpImpulse = joint->perpImpulse;\n\t\t\tfloat axialImpulse = joint->springImpulse + joint->lowerImpulse - joint->upperImpulse;\n\t\t\tlinearImpulse = sqrtf( perpImpulse * perpImpulse + axialImpulse * axialImpulse );\n\t\t\tangularImpulse = b2AbsFloat( joint->motorImpulse );\n\t\t}\n\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\t*force = linearImpulse * invTimeStep;\n\t*torque = angularImpulse * invTimeStep;\n}\n\nstatic b2Vec2 b2GetJointConstraintForce( b2World* world, b2Joint* joint )\n{\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\n\tswitch ( joint->type )\n\t{\n\t\tcase b2_distanceJoint:\n\t\t\treturn b2GetDistanceJointForce( world, base );\n\n\t\tcase b2_motorJoint:\n\t\t\treturn b2GetMotorJointForce( world, base );\n\n\t\tcase b2_filterJoint:\n\t\t\treturn b2Vec2_zero;\n\n\t\tcase b2_prismaticJoint:\n\t\t\treturn b2GetPrismaticJointForce( world, base );\n\n\t\tcase b2_revoluteJoint:\n\t\t\treturn b2GetRevoluteJointForce( world, base );\n\n\t\tcase b2_weldJoint:\n\t\t\treturn b2GetWeldJointForce( world, base );\n\n\t\tcase b2_wheelJoint:\n\t\t\treturn b2GetWheelJointForce( world, base );\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t\t\treturn b2Vec2_zero;\n\t}\n}\n\nstatic float b2GetJointConstraintTorque( b2World* world, b2Joint* joint )\n{\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\n\tswitch ( joint->type )\n\t{\n\t\tcase b2_distanceJoint:\n\t\t\treturn 0.0f;\n\n\t\tcase b2_motorJoint:\n\t\t\treturn b2GetMotorJointTorque( world, base );\n\n\t\tcase b2_filterJoint:\n\t\t\treturn 0.0f;\n\n\t\tcase b2_prismaticJoint:\n\t\t\treturn b2GetPrismaticJointTorque( world, base );\n\n\t\tcase b2_revoluteJoint:\n\t\t\treturn b2GetRevoluteJointTorque( world, base );\n\n\t\tcase b2_weldJoint:\n\t\t\treturn b2GetWeldJointTorque( world, base );\n\n\t\tcase b2_wheelJoint:\n\t\t\treturn b2GetWheelJointTorque( world, base );\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t\t\treturn 0.0f;\n\t}\n}\n\nb2Vec2 b2Joint_GetConstraintForce( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\treturn b2GetJointConstraintForce( world, joint );\n}\n\nfloat b2Joint_GetConstraintTorque( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\treturn b2GetJointConstraintTorque( world, joint );\n}\n\nfloat b2Joint_GetLinearSeparation( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\n\tb2Transform xfA = b2GetBodyTransform( world, joint->edges[0].bodyId );\n\tb2Transform xfB = b2GetBodyTransform( world, joint->edges[1].bodyId );\n\n\tb2Vec2 pA = b2TransformPoint( xfA, base->localFrameA.p );\n\tb2Vec2 pB = b2TransformPoint( xfB, base->localFrameB.p );\n\tb2Vec2 dp = b2Sub( pB, pA );\n\n\tswitch ( joint->type )\n\t{\n\t\tcase b2_distanceJoint:\n\t\t{\n\t\t\tb2DistanceJoint* distanceJoint = &base->distanceJoint;\n\t\t\tfloat length = b2Length( dp );\n\t\t\tif ( distanceJoint->enableSpring )\n\t\t\t{\n\t\t\t\tif ( distanceJoint->enableLimit )\n\t\t\t\t{\n\t\t\t\t\tif ( length < distanceJoint->minLength )\n\t\t\t\t\t{\n\t\t\t\t\t\treturn distanceJoint->minLength - length;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( length > distanceJoint->maxLength )\n\t\t\t\t\t{\n\t\t\t\t\t\treturn length - distanceJoint->maxLength;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn 0.0f;\n\t\t\t\t}\n\n\t\t\t\treturn 0.0f;\n\t\t\t}\n\n\t\t\treturn b2AbsFloat( length - distanceJoint->length );\n\t\t}\n\n\t\tcase b2_motorJoint:\n\t\t\treturn 0.0f;\n\n\t\tcase b2_filterJoint:\n\t\t\treturn 0.0f;\n\n\t\tcase b2_prismaticJoint:\n\t\t{\n\t\t\tb2PrismaticJoint* prismaticJoint = &base->prismaticJoint;\n\t\t\tb2Vec2 axisA = b2RotateVector( xfA.q, (b2Vec2){ 1.0f, 0.0f } );\n\t\t\tb2Vec2 perpA = b2LeftPerp( axisA );\n\t\t\tfloat perpendicularSeparation = b2AbsFloat( b2Dot( perpA, dp ) );\n\t\t\tfloat limitSeparation = 0.0f;\n\n\t\t\tif ( prismaticJoint->enableLimit )\n\t\t\t{\n\t\t\t\tfloat translation = b2Dot( axisA, dp );\n\t\t\t\tif ( translation < prismaticJoint->lowerTranslation )\n\t\t\t\t{\n\t\t\t\t\tlimitSeparation = prismaticJoint->lowerTranslation - translation;\n\t\t\t\t}\n\n\t\t\t\tif ( prismaticJoint->upperTranslation < translation )\n\t\t\t\t{\n\t\t\t\t\tlimitSeparation = translation - prismaticJoint->upperTranslation;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn sqrtf( perpendicularSeparation * perpendicularSeparation + limitSeparation * limitSeparation );\n\t\t}\n\n\t\tcase b2_revoluteJoint:\n\t\t\treturn b2Length( dp );\n\n\t\tcase b2_weldJoint:\n\t\t{\n\t\t\tb2WeldJoint* weldJoint = &base->weldJoint;\n\t\t\tif ( weldJoint->linearHertz == 0.0f )\n\t\t\t{\n\t\t\t\treturn b2Length( dp );\n\t\t\t}\n\n\t\t\treturn 0.0f;\n\t\t}\n\n\t\tcase b2_wheelJoint:\n\t\t{\n\t\t\tb2WheelJoint* wheelJoint = &base->wheelJoint;\n\t\t\tb2Vec2 axisA = b2RotateVector( xfA.q, (b2Vec2){ 1.0f, 0.0f } );\n\t\t\tb2Vec2 perpA = b2LeftPerp( axisA );\n\t\t\tfloat perpendicularSeparation = b2AbsFloat( b2Dot( perpA, dp ) );\n\t\t\tfloat limitSeparation = 0.0f;\n\n\t\t\tif ( wheelJoint->enableLimit )\n\t\t\t{\n\t\t\t\tfloat translation = b2Dot( axisA, dp );\n\t\t\t\tif ( translation < wheelJoint->lowerTranslation )\n\t\t\t\t{\n\t\t\t\t\tlimitSeparation = wheelJoint->lowerTranslation - translation;\n\t\t\t\t}\n\n\t\t\t\tif ( wheelJoint->upperTranslation < translation )\n\t\t\t\t{\n\t\t\t\t\tlimitSeparation = translation - wheelJoint->upperTranslation;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn sqrtf( perpendicularSeparation * perpendicularSeparation + limitSeparation * limitSeparation );\n\t\t}\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t\t\treturn 0.0f;\n\t}\n}\n\nfloat b2Joint_GetAngularSeparation( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\n\tb2Transform xfA = b2GetBodyTransform( world, joint->edges[0].bodyId );\n\tb2Transform xfB = b2GetBodyTransform( world, joint->edges[1].bodyId );\n\tfloat relativeAngle = b2RelativeAngle( xfA.q, xfB.q );\n\n\tswitch ( joint->type )\n\t{\n\t\tcase b2_distanceJoint:\n\t\t\treturn 0.0f;\n\n\t\tcase b2_motorJoint:\n\t\t\treturn 0.0f;\n\n\t\tcase b2_filterJoint:\n\t\t\treturn 0.0f;\n\n\t\tcase b2_prismaticJoint:\n\t\t{\n\t\t\treturn relativeAngle;\n\t\t}\n\n\t\tcase b2_revoluteJoint:\n\t\t{\n\t\t\tb2RevoluteJoint* revoluteJoint = &base->revoluteJoint;\n\t\t\tif ( revoluteJoint->enableLimit )\n\t\t\t{\n\t\t\t\tfloat angle = relativeAngle;\n\t\t\t\tif ( angle < revoluteJoint->lowerAngle )\n\t\t\t\t{\n\t\t\t\t\treturn revoluteJoint->lowerAngle - angle;\n\t\t\t\t}\n\n\t\t\t\tif ( revoluteJoint->upperAngle < angle )\n\t\t\t\t{\n\t\t\t\t\treturn angle - revoluteJoint->upperAngle;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn 0.0f;\n\t\t}\n\n\t\tcase b2_weldJoint:\n\t\t{\n\t\t\tb2WeldJoint* weldJoint = &base->weldJoint;\n\t\t\tif ( weldJoint->angularHertz == 0.0f )\n\t\t\t{\n\t\t\t\treturn relativeAngle;\n\t\t\t}\n\n\t\t\treturn 0.0f;\n\t\t}\n\n\t\tcase b2_wheelJoint:\n\t\t\treturn 0.0f;\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t\t\treturn 0.0f;\n\t}\n}\n\nvoid b2Joint_SetConstraintTuning( b2JointId jointId, float hertz, float dampingRatio )\n{\n\tB2_ASSERT( b2IsValidFloat( hertz ) && hertz >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( dampingRatio ) && dampingRatio >= 0.0f );\n\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\tbase->constraintHertz = hertz;\n\tbase->constraintDampingRatio = dampingRatio;\n}\n\nvoid b2Joint_GetConstraintTuning( b2JointId jointId, float* hertz, float* dampingRatio )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\t*hertz = base->constraintHertz;\n\t*dampingRatio = base->constraintDampingRatio;\n}\n\nvoid b2Joint_SetForceThreshold( b2JointId jointId, float threshold )\n{\n\tB2_ASSERT( b2IsValidFloat( threshold ) && threshold >= 0.0f );\n\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\tbase->forceThreshold = threshold;\n}\n\nfloat b2Joint_GetForceThreshold( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\treturn base->forceThreshold;\n}\n\nvoid b2Joint_SetTorqueThreshold( b2JointId jointId, float threshold )\n{\n\tB2_ASSERT( b2IsValidFloat( threshold ) && threshold >= 0.0f );\n\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\tbase->torqueThreshold = threshold;\n}\n\nfloat b2Joint_GetTorqueThreshold( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\treturn base->torqueThreshold;\n}\n\nvoid b2PrepareJoint( b2JointSim* joint, b2StepContext* context )\n{\n\t// Clamp joint hertz based on the time step to reduce jitter.\n\tfloat hertz = b2MinFloat( joint->constraintHertz, 0.25f * context->inv_h );\n\tjoint->constraintSoftness = b2MakeSoft( hertz, joint->constraintDampingRatio, context->h );\n\n\tswitch ( joint->type )\n\t{\n\t\tcase b2_distanceJoint:\n\t\t\tb2PrepareDistanceJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_motorJoint:\n\t\t\tb2PrepareMotorJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_filterJoint:\n\t\t\tbreak;\n\n\t\tcase b2_prismaticJoint:\n\t\t\tb2PreparePrismaticJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_revoluteJoint:\n\t\t\tb2PrepareRevoluteJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_weldJoint:\n\t\t\tb2PrepareWeldJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_wheelJoint:\n\t\t\tb2PrepareWheelJoint( joint, context );\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t}\n}\n\nvoid b2WarmStartJoint( b2JointSim* joint, b2StepContext* context )\n{\n\tswitch ( joint->type )\n\t{\n\t\tcase b2_distanceJoint:\n\t\t\tb2WarmStartDistanceJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_motorJoint:\n\t\t\tb2WarmStartMotorJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_filterJoint:\n\t\t\tbreak;\n\n\t\tcase b2_prismaticJoint:\n\t\t\tb2WarmStartPrismaticJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_revoluteJoint:\n\t\t\tb2WarmStartRevoluteJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_weldJoint:\n\t\t\tb2WarmStartWeldJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_wheelJoint:\n\t\t\tb2WarmStartWheelJoint( joint, context );\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t}\n}\n\nvoid b2SolveJoint( b2JointSim* joint, b2StepContext* context, bool useBias )\n{\n\tswitch ( joint->type )\n\t{\n\t\tcase b2_distanceJoint:\n\t\t\tb2SolveDistanceJoint( joint, context, useBias );\n\t\t\tbreak;\n\n\t\tcase b2_motorJoint:\n\t\t\tb2SolveMotorJoint( joint, context );\n\t\t\tbreak;\n\n\t\tcase b2_filterJoint:\n\t\t\tbreak;\n\n\t\tcase b2_prismaticJoint:\n\t\t\tb2SolvePrismaticJoint( joint, context, useBias );\n\t\t\tbreak;\n\n\t\tcase b2_revoluteJoint:\n\t\t\tb2SolveRevoluteJoint( joint, context, useBias );\n\t\t\tbreak;\n\n\t\tcase b2_weldJoint:\n\t\t\tb2SolveWeldJoint( joint, context, useBias );\n\t\t\tbreak;\n\n\t\tcase b2_wheelJoint:\n\t\t\tb2SolveWheelJoint( joint, context, useBias );\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t}\n}\n\nvoid b2PrepareOverflowJoints( b2StepContext* context )\n{\n\tb2TracyCZoneNC( prepare_joints, \"PrepJoints\", b2_colorOldLace, true );\n\n\tb2ConstraintGraph* graph = context->graph;\n\tb2JointSim* joints = graph->colors[B2_OVERFLOW_INDEX].jointSims.data;\n\tint jointCount = graph->colors[B2_OVERFLOW_INDEX].jointSims.count;\n\n\tfor ( int i = 0; i < jointCount; ++i )\n\t{\n\t\tb2JointSim* joint = joints + i;\n\t\tb2PrepareJoint( joint, context );\n\t}\n\n\tb2TracyCZoneEnd( prepare_joints );\n}\n\nvoid b2WarmStartOverflowJoints( b2StepContext* context )\n{\n\tb2TracyCZoneNC( prepare_joints, \"PrepJoints\", b2_colorOldLace, true );\n\n\tb2ConstraintGraph* graph = context->graph;\n\tb2JointSim* joints = graph->colors[B2_OVERFLOW_INDEX].jointSims.data;\n\tint jointCount = graph->colors[B2_OVERFLOW_INDEX].jointSims.count;\n\n\tfor ( int i = 0; i < jointCount; ++i )\n\t{\n\t\tb2JointSim* joint = joints + i;\n\t\tb2WarmStartJoint( joint, context );\n\t}\n\n\tb2TracyCZoneEnd( prepare_joints );\n}\n\nvoid b2SolveOverflowJoints( b2StepContext* context, bool useBias )\n{\n\tb2TracyCZoneNC( solve_joints, \"SolveJoints\", b2_colorLemonChiffon, true );\n\n\tb2ConstraintGraph* graph = context->graph;\n\tb2JointSim* joints = graph->colors[B2_OVERFLOW_INDEX].jointSims.data;\n\tint jointCount = graph->colors[B2_OVERFLOW_INDEX].jointSims.count;\n\n\tfor ( int i = 0; i < jointCount; ++i )\n\t{\n\t\tb2JointSim* joint = joints + i;\n\t\tb2SolveJoint( joint, context, useBias );\n\t}\n\n\tb2TracyCZoneEnd( solve_joints );\n}\n\nvoid b2DrawJoint( b2DebugDraw* draw, b2World* world, b2Joint* joint )\n{\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );\n\tif ( bodyA->setIndex == b2_disabledSet || bodyB->setIndex == b2_disabledSet )\n\t{\n\t\treturn;\n\t}\n\n\tb2JointSim* jointSim = b2GetJointSim( world, joint );\n\n\tb2Transform transformA = b2GetBodyTransformQuick( world, bodyA );\n\tb2Transform transformB = b2GetBodyTransformQuick( world, bodyB );\n\tb2Vec2 pA = b2TransformPoint( transformA, jointSim->localFrameA.p );\n\tb2Vec2 pB = b2TransformPoint( transformB, jointSim->localFrameB.p );\n\n\tb2HexColor color = b2_colorDarkSeaGreen;\n\n\tfloat scale = b2MaxFloat( 0.0001f, draw->jointScale * joint->drawScale );\n\n\tswitch ( joint->type )\n\t{\n\t\tcase b2_distanceJoint:\n\t\t\tb2DrawDistanceJoint( draw, jointSim, transformA, transformB );\n\t\t\tbreak;\n\n\t\tcase b2_filterJoint:\n\t\t\tdraw->DrawLineFcn( pA, pB, b2_colorGold, draw->context );\n\t\t\tbreak;\n\n\t\tcase b2_motorJoint:\n\t\t\tdraw->DrawPointFcn( pA, 8.0f, b2_colorYellowGreen, draw->context );\n\t\t\tdraw->DrawPointFcn( pB, 8.0f, b2_colorPlum, draw->context );\n\t\t\tdraw->DrawLineFcn( pA, pB, b2_colorLightGray, draw->context );\n\t\t\tbreak;\n\n\t\tcase b2_prismaticJoint:\n\t\t\tb2DrawPrismaticJoint( draw, jointSim, transformA, transformB, scale );\n\t\t\tbreak;\n\n\t\tcase b2_revoluteJoint:\n\t\t\tb2DrawRevoluteJoint( draw, jointSim, transformA, transformB, scale );\n\t\t\tbreak;\n\n\t\tcase b2_weldJoint:\n\t\t\tb2DrawWeldJoint( draw, jointSim, transformA, transformB, scale );\n\t\t\tbreak;\n\n\t\tcase b2_wheelJoint:\n\t\t\tb2DrawWheelJoint( draw, jointSim, transformA, transformB, scale );\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tdraw->DrawLineFcn( transformA.p, pA, color, draw->context );\n\t\t\tdraw->DrawLineFcn( pA, pB, color, draw->context );\n\t\t\tdraw->DrawLineFcn( transformB.p, pB, color, draw->context );\n\t\t\tbreak;\n\t}\n\n\tif ( draw->drawGraphColors )\n\t{\n\t\tint colorIndex = joint->colorIndex;\n\t\tif ( colorIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Vec2 p = b2Lerp( pA, pB, 0.5f );\n\t\t\tdraw->DrawPointFcn( p, 5.0f, b2_graphColors[colorIndex], draw->context );\n\t\t}\n\t}\n\n\tif ( draw->drawJointExtras )\n\t{\n\t\tb2Vec2 force = b2GetJointConstraintForce( world, joint );\n\t\tfloat torque = b2GetJointConstraintTorque( world, joint );\n\t\tb2Vec2 p = b2Lerp( pA, pB, 0.5f );\n\n\t\tdraw->DrawLineFcn( p, b2MulAdd( p, 0.001f, force ), b2_colorAzure, draw->context );\n\n\t\tchar buffer[64];\n\t\tsnprintf( buffer, 64, \"f = [%g, %g], t = %g\", force.x, force.y, torque );\n\t\tdraw->DrawStringFcn( p, buffer, b2_colorAzure, draw->context );\n\t}\n}\n"
  },
  {
    "path": "src/joint.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n#pragma once\n\n#include \"array.h\"\n#include \"solver.h\"\n\n#include \"box2d/types.h\"\n\ntypedef struct b2DebugDraw b2DebugDraw;\ntypedef struct b2StepContext b2StepContext;\ntypedef struct b2World b2World;\n\n/// A joint edge is used to connect bodies and joints together\n/// in a joint graph where each body is a node and each joint\n/// is an edge. A joint edge belongs to a doubly linked list\n/// maintained in each attached body. Each joint has two joint\n/// nodes, one for each attached body.\ntypedef struct b2JointEdge\n{\n\tint bodyId;\n\tint prevKey;\n\tint nextKey;\n} b2JointEdge;\n\n// Map from b2JointId to b2Joint in the solver sets\ntypedef struct b2Joint\n{\n\tvoid* userData;\n\n\t// index of simulation set stored in b2World\n\t// B2_NULL_INDEX when slot is free\n\tint setIndex;\n\n\t// index into the constraint graph color array, may be B2_NULL_INDEX for sleeping/disabled joints\n\t// B2_NULL_INDEX when slot is free\n\tint colorIndex;\n\n\t// joint index within set or graph color\n\t// B2_NULL_INDEX when slot is free\n\tint localIndex;\n\n\tb2JointEdge edges[2];\n\n\tint jointId;\n\tint islandId;\n\tint islandPrev;\n\tint islandNext;\n\n\tfloat drawScale;\n\n\tb2JointType type;\n\n\t// This is monotonically advanced when a body is allocated in this slot\n\t// Used to check for invalid b2JointId\n\tuint16_t generation;\n\n\tbool collideConnected;\n\n} b2Joint;\n\ntypedef struct b2DistanceJoint\n{\n\tfloat length;\n\tfloat hertz;\n\tfloat dampingRatio;\n\tfloat lowerSpringForce;\n\tfloat upperSpringForce;\n\tfloat minLength;\n\tfloat maxLength;\n\n\tfloat maxMotorForce;\n\tfloat motorSpeed;\n\n\tfloat impulse;\n\tfloat lowerImpulse;\n\tfloat upperImpulse;\n\tfloat motorImpulse;\n\n\tint indexA;\n\tint indexB;\n\tb2Vec2 anchorA;\n\tb2Vec2 anchorB;\n\tb2Vec2 deltaCenter;\n\tb2Softness distanceSoftness;\n\tfloat axialMass;\n\n\tbool enableSpring;\n\tbool enableLimit;\n\tbool enableMotor;\n} b2DistanceJoint;\n\ntypedef struct b2MotorJoint\n{\n\tb2Vec2 linearVelocity;\n\tfloat maxVelocityForce;\n\tfloat angularVelocity;\n\tfloat maxVelocityTorque;\n\tfloat linearHertz;\n\tfloat linearDampingRatio;\n\tfloat maxSpringForce;\n\tfloat angularHertz;\n\tfloat angularDampingRatio;\n\tfloat maxSpringTorque;\n\n\tb2Vec2 linearVelocityImpulse;\n\tfloat angularVelocityImpulse;\n\tb2Vec2 linearSpringImpulse;\n\tfloat angularSpringImpulse;\n\n\tb2Softness linearSpring;\n\tb2Softness angularSpring;\n\n\tint indexA;\n\tint indexB;\n\tb2Transform frameA;\n\tb2Transform frameB;\n\tb2Vec2 deltaCenter;\n\tb2Mat22 linearMass;\n\tfloat angularMass;\n} b2MotorJoint;\n\ntypedef struct b2PrismaticJoint\n{\n\tb2Vec2 impulse;\n\tfloat springImpulse;\n\tfloat motorImpulse;\n\tfloat lowerImpulse;\n\tfloat upperImpulse;\n\tfloat hertz;\n\tfloat dampingRatio;\n\tfloat targetTranslation;\n\tfloat maxMotorForce;\n\tfloat motorSpeed;\n\tfloat lowerTranslation;\n\tfloat upperTranslation;\n\n\tint indexA;\n\tint indexB;\n\tb2Transform frameA;\n\tb2Transform frameB;\n\tb2Vec2 deltaCenter;\n\tb2Softness springSoftness;\n\n\tbool enableSpring;\n\tbool enableLimit;\n\tbool enableMotor;\n} b2PrismaticJoint;\n\ntypedef struct b2RevoluteJoint\n{\n\tb2Vec2 linearImpulse;\n\tfloat springImpulse;\n\tfloat motorImpulse;\n\tfloat lowerImpulse;\n\tfloat upperImpulse;\n\tfloat hertz;\n\tfloat dampingRatio;\n\tfloat targetAngle;\n\tfloat maxMotorTorque;\n\tfloat motorSpeed;\n\tfloat lowerAngle;\n\tfloat upperAngle;\n\n\tint indexA;\n\tint indexB;\n\tb2Transform frameA;\n\tb2Transform frameB;\n\tb2Vec2 deltaCenter;\n\tfloat axialMass;\n\tb2Softness springSoftness;\n\n\tbool enableSpring;\n\tbool enableMotor;\n\tbool enableLimit;\n} b2RevoluteJoint;\n\ntypedef struct b2WeldJoint\n{\n\tfloat linearHertz;\n\tfloat linearDampingRatio;\n\tfloat angularHertz;\n\tfloat angularDampingRatio;\n\n\tb2Softness linearSpring;\n\tb2Softness angularSpring;\n\tb2Vec2 linearImpulse;\n\tfloat angularImpulse;\n\n\tint indexA;\n\tint indexB;\n\tb2Transform frameA;\n\tb2Transform frameB;\n\tb2Vec2 deltaCenter;\n\tfloat axialMass;\n} b2WeldJoint;\n\ntypedef struct b2WheelJoint\n{\n\tfloat perpImpulse;\n\tfloat motorImpulse;\n\tfloat springImpulse;\n\tfloat lowerImpulse;\n\tfloat upperImpulse;\n\tfloat maxMotorTorque;\n\tfloat motorSpeed;\n\tfloat lowerTranslation;\n\tfloat upperTranslation;\n\tfloat hertz;\n\tfloat dampingRatio;\n\n\tint indexA;\n\tint indexB;\n\tb2Transform frameA;\n\tb2Transform frameB;\n\tb2Vec2 deltaCenter;\n\tfloat perpMass;\n\tfloat motorMass;\n\tfloat axialMass;\n\tb2Softness springSoftness;\n\n\tbool enableSpring;\n\tbool enableMotor;\n\tbool enableLimit;\n} b2WheelJoint;\n\n/// The base joint class. Joints are used to constraint two bodies together in\n/// various fashions. Some joints also feature limits and motors.\ntypedef struct b2JointSim\n{\n\tint jointId;\n\n\tint bodyIdA;\n\tint bodyIdB;\n\n\tb2JointType type;\n\n\tb2Transform localFrameA;\n\tb2Transform localFrameB;\n\n\tfloat invMassA, invMassB;\n\tfloat invIA, invIB;\n\n\tfloat constraintHertz;\n\tfloat constraintDampingRatio;\n\n\tb2Softness constraintSoftness;\n\n\tfloat forceThreshold;\n\tfloat torqueThreshold;\n\n\tunion\n\t{\n\t\tb2DistanceJoint distanceJoint;\n\t\tb2MotorJoint motorJoint;\n\t\tb2RevoluteJoint revoluteJoint;\n\t\tb2PrismaticJoint prismaticJoint;\n\t\tb2WeldJoint weldJoint;\n\t\tb2WheelJoint wheelJoint;\n\t};\n} b2JointSim;\n\nvoid b2DestroyJointInternal( b2World* world, b2Joint* joint, bool wakeBodies );\n\nb2Joint* b2GetJointFullId( b2World* world, b2JointId jointId );\nb2JointSim* b2GetJointSim( b2World* world, b2Joint* joint );\nb2JointSim* b2GetJointSimCheckType( b2JointId jointId, b2JointType type );\n\nvoid b2PrepareJoint( b2JointSim* joint, b2StepContext* context );\nvoid b2WarmStartJoint( b2JointSim* joint, b2StepContext* context );\nvoid b2SolveJoint( b2JointSim* joint, b2StepContext* context, bool useBias );\n\nvoid b2PrepareOverflowJoints( b2StepContext* context );\nvoid b2WarmStartOverflowJoints( b2StepContext* context );\nvoid b2SolveOverflowJoints( b2StepContext* context, bool useBias );\n\nvoid b2GetJointReaction( b2JointSim* sim, float invTimeStep, float* force, float* torque );\n\nvoid b2DrawJoint( b2DebugDraw* draw, b2World* world, b2Joint* joint );\n\nb2Vec2 b2GetDistanceJointForce( b2World* world, b2JointSim* base );\nb2Vec2 b2GetMotorJointForce( b2World* world, b2JointSim* base );\nb2Vec2 b2GetPrismaticJointForce( b2World* world, b2JointSim* base );\nb2Vec2 b2GetRevoluteJointForce( b2World* world, b2JointSim* base );\nb2Vec2 b2GetWeldJointForce( b2World* world, b2JointSim* base );\nb2Vec2 b2GetWheelJointForce( b2World* world, b2JointSim* base );\n\nfloat b2GetMotorJointTorque( b2World* world, b2JointSim* base );\nfloat b2GetPrismaticJointTorque( b2World* world, b2JointSim* base );\nfloat b2GetRevoluteJointTorque( b2World* world, b2JointSim* base );\nfloat b2GetWeldJointTorque( b2World* world, b2JointSim* base );\nfloat b2GetWheelJointTorque( b2World* world, b2JointSim* base );\n\nvoid b2PrepareDistanceJoint( b2JointSim* base, b2StepContext* context );\nvoid b2PrepareMotorJoint( b2JointSim* base, b2StepContext* context );\nvoid b2PreparePrismaticJoint( b2JointSim* base, b2StepContext* context );\nvoid b2PrepareRevoluteJoint( b2JointSim* base, b2StepContext* context );\nvoid b2PrepareWeldJoint( b2JointSim* base, b2StepContext* context );\nvoid b2PrepareWheelJoint( b2JointSim* base, b2StepContext* context );\n\nvoid b2WarmStartDistanceJoint( b2JointSim* base, b2StepContext* context );\nvoid b2WarmStartMotorJoint( b2JointSim* base, b2StepContext* context );\nvoid b2WarmStartPrismaticJoint( b2JointSim* base, b2StepContext* context );\nvoid b2WarmStartRevoluteJoint( b2JointSim* base, b2StepContext* context );\nvoid b2WarmStartWeldJoint( b2JointSim* base, b2StepContext* context );\nvoid b2WarmStartWheelJoint( b2JointSim* base, b2StepContext* context );\n\nvoid b2SolveDistanceJoint( b2JointSim* base, b2StepContext* context, bool useBias );\nvoid b2SolveMotorJoint( b2JointSim* base, b2StepContext* context );\nvoid b2SolvePrismaticJoint( b2JointSim* base, b2StepContext* context, bool useBias );\nvoid b2SolveRevoluteJoint( b2JointSim* base, b2StepContext* context, bool useBias );\nvoid b2SolveWeldJoint( b2JointSim* base, b2StepContext* context, bool useBias );\nvoid b2SolveWheelJoint( b2JointSim* base, b2StepContext* context, bool useBias );\n\nvoid b2DrawDistanceJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB );\nvoid b2DrawPrismaticJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, float drawScale );\nvoid b2DrawRevoluteJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, float drawScale );\nvoid b2DrawWeldJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, float drawScale );\nvoid b2DrawWheelJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, float drawScale );\n\n// Define inline functions for arrays\nB2_ARRAY_INLINE( b2Joint, b2Joint )\nB2_ARRAY_INLINE( b2JointSim, b2JointSim )\n"
  },
  {
    "path": "src/manifold.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"constants.h\"\n#include \"core.h\"\n\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n#include <stddef.h>\n#include <stdlib.h>\n\n#define B2_MAKE_ID( A, B ) ( (uint8_t)( A ) << 8 | (uint8_t)( B ) )\n\nstatic b2Polygon b2MakeCapsule( b2Vec2 p1, b2Vec2 p2, float radius )\n{\n\tb2Polygon shape = { 0 };\n\tshape.vertices[0] = p1;\n\tshape.vertices[1] = p2;\n\tshape.centroid = b2Lerp( p1, p2, 0.5f );\n\n\tb2Vec2 d = b2Sub( p2, p1 );\n\tB2_ASSERT( b2LengthSquared( d ) > FLT_EPSILON );\n\tb2Vec2 axis = b2Normalize( d );\n\tb2Vec2 normal = b2RightPerp( axis );\n\n\tshape.normals[0] = normal;\n\tshape.normals[1] = b2Neg( normal );\n\tshape.count = 2;\n\tshape.radius = radius;\n\n\treturn shape;\n}\n\n// point = qA * localAnchorA + pA\n// localAnchorB = qBc * (point - pB)\n// anchorB = point - pB = qA * localAnchorA + pA - pB\n//         = anchorA + (pA - pB)\nb2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )\n{\n\tb2Manifold manifold = { 0 };\n\n\tb2Transform xf = b2InvMulTransforms( xfA, xfB );\n\n\tb2Vec2 pointA = circleA->center;\n\tb2Vec2 pointB = b2TransformPoint( xf, circleB->center );\n\n\tfloat distance;\n\tb2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pointB, pointA ) );\n\n\tfloat radiusA = circleA->radius;\n\tfloat radiusB = circleB->radius;\n\n\tfloat separation = distance - radiusA - radiusB;\n\tif ( separation > B2_SPECULATIVE_DISTANCE )\n\t{\n\t\treturn manifold;\n\t}\n\n\tb2Vec2 cA = b2MulAdd( pointA, radiusA, normal );\n\tb2Vec2 cB = b2MulAdd( pointB, -radiusB, normal );\n\tb2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );\n\n\tmanifold.normal = b2RotateVector( xfA.q, normal );\n\tb2ManifoldPoint* mp = manifold.points + 0;\n\tmp->anchorA = b2RotateVector( xfA.q, contactPointA );\n\tmp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );\n\tmp->point = b2Add( mp->anchorA, xfA.p );\n\tmp->separation = separation;\n\tmp->id = 0;\n\tmanifold.pointCount = 1;\n\treturn manifold;\n}\n\n/// Compute the collision manifold between a capsule and circle\nb2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )\n{\n\tb2Manifold manifold = { 0 };\n\n\tb2Transform xf = b2InvMulTransforms( xfA, xfB );\n\n\t// Compute circle position in the frame of the capsule.\n\tb2Vec2 pB = b2TransformPoint( xf, circleB->center );\n\n\t// Compute closest point\n\tb2Vec2 p1 = capsuleA->center1;\n\tb2Vec2 p2 = capsuleA->center2;\n\n\tb2Vec2 e = b2Sub( p2, p1 );\n\n\t// dot(p - pA, e) = 0\n\t// dot(p - (p1 + s1 * e), e) = 0\n\t// s1 = dot(p - p1, e)\n\tb2Vec2 pA;\n\tfloat s1 = b2Dot( b2Sub( pB, p1 ), e );\n\tfloat s2 = b2Dot( b2Sub( p2, pB ), e );\n\tif ( s1 < 0.0f )\n\t{\n\t\t// p1 region\n\t\tpA = p1;\n\t}\n\telse if ( s2 < 0.0f )\n\t{\n\t\t// p2 region\n\t\tpA = p2;\n\t}\n\telse\n\t{\n\t\t// circle colliding with segment interior\n\t\tfloat s = s1 / b2Dot( e, e );\n\t\tpA = b2MulAdd( p1, s, e );\n\t}\n\n\tfloat distance;\n\tb2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pB, pA ) );\n\n\tfloat radiusA = capsuleA->radius;\n\tfloat radiusB = circleB->radius;\n\tfloat separation = distance - radiusA - radiusB;\n\tif ( separation > B2_SPECULATIVE_DISTANCE )\n\t{\n\t\treturn manifold;\n\t}\n\n\tb2Vec2 cA = b2MulAdd( pA, radiusA, normal );\n\tb2Vec2 cB = b2MulAdd( pB, -radiusB, normal );\n\tb2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );\n\n\tmanifold.normal = b2RotateVector( xfA.q, normal );\n\tb2ManifoldPoint* mp = manifold.points + 0;\n\tmp->anchorA = b2RotateVector( xfA.q, contactPointA );\n\tmp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );\n\tmp->point = b2Add( xfA.p, mp->anchorA );\n\tmp->separation = separation;\n\tmp->id = 0;\n\tmanifold.pointCount = 1;\n\treturn manifold;\n}\n\nb2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )\n{\n\tb2Manifold manifold = { 0 };\n\tconst float speculativeDistance = B2_SPECULATIVE_DISTANCE;\n\n\tb2Transform xf = b2InvMulTransforms( xfA, xfB );\n\n\t// Compute circle position in the frame of the polygon.\n\tb2Vec2 center = b2TransformPoint( xf, circleB->center );\n\tfloat radiusA = polygonA->radius;\n\tfloat radiusB = circleB->radius;\n\tfloat radius = radiusA + radiusB;\n\n\t// Find the min separating edge.\n\tint normalIndex = 0;\n\tfloat separation = -FLT_MAX;\n\tint vertexCount = polygonA->count;\n\tconst b2Vec2* vertices = polygonA->vertices;\n\tconst b2Vec2* normals = polygonA->normals;\n\n\tfor ( int i = 0; i < vertexCount; ++i )\n\t{\n\t\tfloat s = b2Dot( normals[i], b2Sub( center, vertices[i] ) );\n\t\tif ( s > separation )\n\t\t{\n\t\t\tseparation = s;\n\t\t\tnormalIndex = i;\n\t\t}\n\t}\n\n\tif ( separation > radius + speculativeDistance )\n\t{\n\t\treturn manifold;\n\t}\n\n\t// Vertices of the reference edge.\n\tint vertIndex1 = normalIndex;\n\tint vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;\n\tb2Vec2 v1 = vertices[vertIndex1];\n\tb2Vec2 v2 = vertices[vertIndex2];\n\n\t// Compute barycentric coordinates\n\tfloat u1 = b2Dot( b2Sub( center, v1 ), b2Sub( v2, v1 ) );\n\tfloat u2 = b2Dot( b2Sub( center, v2 ), b2Sub( v1, v2 ) );\n\n\tif ( u1 < 0.0f && separation > FLT_EPSILON )\n\t{\n\t\t// Circle center is closest to v1 and safely outside the polygon\n\t\tb2Vec2 normal = b2Normalize( b2Sub( center, v1 ) );\n\t\tseparation = b2Dot( b2Sub( center, v1 ), normal );\n\t\tif ( separation > radius + speculativeDistance )\n\t\t{\n\t\t\treturn manifold;\n\t\t}\n\n\t\tb2Vec2 cA = b2MulAdd( v1, radiusA, normal );\n\t\tb2Vec2 cB = b2MulSub( center, radiusB, normal );\n\t\tb2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );\n\n\t\tmanifold.normal = b2RotateVector( xfA.q, normal );\n\t\tb2ManifoldPoint* mp = manifold.points + 0;\n\t\tmp->anchorA = b2RotateVector( xfA.q, contactPointA );\n\t\tmp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );\n\t\tmp->point = b2Add( xfA.p, mp->anchorA );\n\t\tmp->separation = b2Dot( b2Sub( cB, cA ), normal );\n\t\tmp->id = 0;\n\t\tmanifold.pointCount = 1;\n\t}\n\telse if ( u2 < 0.0f && separation > FLT_EPSILON )\n\t{\n\t\t// Circle center is closest to v2 and safely outside the polygon\n\t\tb2Vec2 normal = b2Normalize( b2Sub( center, v2 ) );\n\t\tseparation = b2Dot( b2Sub( center, v2 ), normal );\n\t\tif ( separation > radius + speculativeDistance )\n\t\t{\n\t\t\treturn manifold;\n\t\t}\n\n\t\tb2Vec2 cA = b2MulAdd( v2, radiusA, normal );\n\t\tb2Vec2 cB = b2MulSub( center, radiusB, normal );\n\t\tb2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );\n\n\t\tmanifold.normal = b2RotateVector( xfA.q, normal );\n\t\tb2ManifoldPoint* mp = manifold.points + 0;\n\t\tmp->anchorA = b2RotateVector( xfA.q, contactPointA );\n\t\tmp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );\n\t\tmp->point = b2Add( xfA.p, mp->anchorA );\n\t\tmp->separation = b2Dot( b2Sub( cB, cA ), normal );\n\t\tmp->id = 0;\n\t\tmanifold.pointCount = 1;\n\t}\n\telse\n\t{\n\t\t// Circle center is between v1 and v2. Center may be inside polygon\n\t\tb2Vec2 normal = normals[normalIndex];\n\t\tmanifold.normal = b2RotateVector( xfA.q, normal );\n\n\t\t// cA is the projection of the circle center onto to the reference edge\n\t\tb2Vec2 cA = b2MulAdd( center, radiusA - b2Dot( b2Sub( center, v1 ), normal ), normal );\n\n\t\t// cB is the deepest point on the circle with respect to the reference edge\n\t\tb2Vec2 cB = b2MulSub( center, radiusB, normal );\n\n\t\tb2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );\n\n\t\t// The contact point is the midpoint in world space\n\t\tb2ManifoldPoint* mp = manifold.points + 0;\n\t\tmp->anchorA = b2RotateVector( xfA.q, contactPointA );\n\t\tmp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );\n\t\tmp->point = b2Add( xfA.p, mp->anchorA );\n\t\tmp->separation = separation - radius;\n\t\tmp->id = 0;\n\t\tmanifold.pointCount = 1;\n\t}\n\n\treturn manifold;\n}\n\n// Follows Ericson 5.1.9 Closest Points of Two Line Segments\n// Adds some logic to support clipping to get two contact points\nb2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB )\n{\n\tb2Vec2 origin = capsuleA->center1;\n\n\t// Shift polyA to origin\n\t// pw = q * pb + p\n\t// pw = q * (pbs + origin) + p\n\t// pw = q * pbs + (p + q * origin)\n\tb2Transform sfA = { b2Add( xfA.p, b2RotateVector( xfA.q, origin ) ), xfA.q };\n\tb2Transform xf = b2InvMulTransforms( sfA, xfB );\n\n\tb2Vec2 p1 = b2Vec2_zero;\n\tb2Vec2 q1 = b2Sub( capsuleA->center2, origin );\n\n\tb2Vec2 p2 = b2TransformPoint( xf, capsuleB->center1 );\n\tb2Vec2 q2 = b2TransformPoint( xf, capsuleB->center2 );\n\n\tb2Vec2 d1 = b2Sub( q1, p1 );\n\tb2Vec2 d2 = b2Sub( q2, p2 );\n\n\tfloat dd1 = b2Dot( d1, d1 );\n\tfloat dd2 = b2Dot( d2, d2 );\n\n\tconst float epsSqr = FLT_EPSILON * FLT_EPSILON;\n\tB2_ASSERT( dd1 > epsSqr && dd2 > epsSqr );\n\n\tb2Vec2 r = b2Sub( p1, p2 );\n\tfloat rd1 = b2Dot( r, d1 );\n\tfloat rd2 = b2Dot( r, d2 );\n\n\tfloat d12 = b2Dot( d1, d2 );\n\n\tfloat denom = dd1 * dd2 - d12 * d12;\n\n\t// Fraction on segment 1\n\tfloat f1 = 0.0f;\n\tif ( denom != 0.0f )\n\t{\n\t\t// not parallel\n\t\tf1 = b2ClampFloat( ( d12 * rd2 - rd1 * dd2 ) / denom, 0.0f, 1.0f );\n\t}\n\n\t// Compute point on segment 2 closest to p1 + f1 * d1\n\tfloat f2 = ( d12 * f1 + rd2 ) / dd2;\n\n\t// Clamping of segment 2 requires a do over on segment 1\n\tif ( f2 < 0.0f )\n\t{\n\t\tf2 = 0.0f;\n\t\tf1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f );\n\t}\n\telse if ( f2 > 1.0f )\n\t{\n\t\tf2 = 1.0f;\n\t\tf1 = b2ClampFloat( ( d12 - rd1 ) / dd1, 0.0f, 1.0f );\n\t}\n\n\tb2Vec2 closest1 = b2MulAdd( p1, f1, d1 );\n\tb2Vec2 closest2 = b2MulAdd( p2, f2, d2 );\n\tfloat distanceSquared = b2DistanceSquared( closest1, closest2 );\n\n\tb2Manifold manifold = { 0 };\n\tfloat radiusA = capsuleA->radius;\n\tfloat radiusB = capsuleB->radius;\n\tfloat radius = radiusA + radiusB;\n\tfloat maxDistance = radius + B2_SPECULATIVE_DISTANCE;\n\n\tif ( distanceSquared > maxDistance * maxDistance )\n\t{\n\t\treturn manifold;\n\t}\n\n\tfloat distance = sqrtf( distanceSquared );\n\n\tfloat length1, length2;\n\tb2Vec2 u1 = b2GetLengthAndNormalize( &length1, d1 );\n\tb2Vec2 u2 = b2GetLengthAndNormalize( &length2, d2 );\n\n\t// Does segment B project outside segment A?\n\tfloat fp2 = b2Dot( b2Sub( p2, p1 ), u1 );\n\tfloat fq2 = b2Dot( b2Sub( q2, p1 ), u1 );\n\tbool outsideA = ( fp2 <= 0.0f && fq2 <= 0.0f ) || ( fp2 >= length1 && fq2 >= length1 );\n\n\t// Does segment A project outside segment B?\n\tfloat fp1 = b2Dot( b2Sub( p1, p2 ), u2 );\n\tfloat fq1 = b2Dot( b2Sub( q1, p2 ), u2 );\n\tbool outsideB = ( fp1 <= 0.0f && fq1 <= 0.0f ) || ( fp1 >= length2 && fq1 >= length2 );\n\n\tif ( outsideA == false && outsideB == false )\n\t{\n\t\t// attempt to clip\n\t\t// this may yield contact points with excessive separation\n\t\t// in that case the algorithm falls back to single point collision\n\n\t\t// find reference edge using SAT\n\t\tb2Vec2 normalA;\n\t\tfloat separationA;\n\n\t\t{\n\t\t\tnormalA = b2LeftPerp( u1 );\n\t\t\tfloat ss1 = b2Dot( b2Sub( p2, p1 ), normalA );\n\t\t\tfloat ss2 = b2Dot( b2Sub( q2, p1 ), normalA );\n\t\t\tfloat s1p = ss1 < ss2 ? ss1 : ss2;\n\t\t\tfloat s1n = -ss1 < -ss2 ? -ss1 : -ss2;\n\n\t\t\tif ( s1p > s1n )\n\t\t\t{\n\t\t\t\tseparationA = s1p;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tseparationA = s1n;\n\t\t\t\tnormalA = b2Neg( normalA );\n\t\t\t}\n\t\t}\n\n\t\tb2Vec2 normalB;\n\t\tfloat separationB;\n\t\t{\n\t\t\tnormalB = b2LeftPerp( u2 );\n\t\t\tfloat ss1 = b2Dot( b2Sub( p1, p2 ), normalB );\n\t\t\tfloat ss2 = b2Dot( b2Sub( q1, p2 ), normalB );\n\t\t\tfloat s1p = ss1 < ss2 ? ss1 : ss2;\n\t\t\tfloat s1n = -ss1 < -ss2 ? -ss1 : -ss2;\n\n\t\t\tif ( s1p > s1n )\n\t\t\t{\n\t\t\t\tseparationB = s1p;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tseparationB = s1n;\n\t\t\t\tnormalB = b2Neg( normalB );\n\t\t\t}\n\t\t}\n\n\t\t// biased to avoid feature flip-flop\n\t\t// todo more testing?\n\t\tif ( separationA + 0.1f * B2_LINEAR_SLOP >= separationB )\n\t\t{\n\t\t\tmanifold.normal = normalA;\n\n\t\t\tb2Vec2 cp = p2;\n\t\t\tb2Vec2 cq = q2;\n\n\t\t\t// clip to p1\n\t\t\tif ( fp2 < 0.0f && fq2 > 0.0f )\n\t\t\t{\n\t\t\t\tcp = b2Lerp( p2, q2, ( 0.0f - fp2 ) / ( fq2 - fp2 ) );\n\t\t\t}\n\t\t\telse if ( fq2 < 0.0f && fp2 > 0.0f )\n\t\t\t{\n\t\t\t\tcq = b2Lerp( q2, p2, ( 0.0f - fq2 ) / ( fp2 - fq2 ) );\n\t\t\t}\n\n\t\t\t// clip to q1\n\t\t\tif ( fp2 > length1 && fq2 < length1 )\n\t\t\t{\n\t\t\t\tcp = b2Lerp( p2, q2, ( fp2 - length1 ) / ( fp2 - fq2 ) );\n\t\t\t}\n\t\t\telse if ( fq2 > length1 && fp2 < length1 )\n\t\t\t{\n\t\t\t\tcq = b2Lerp( q2, p2, ( fq2 - length1 ) / ( fq2 - fp2 ) );\n\t\t\t}\n\n\t\t\tfloat sp = b2Dot( b2Sub( cp, p1 ), normalA );\n\t\t\tfloat sq = b2Dot( b2Sub( cq, p1 ), normalA );\n\n\t\t\tif ( sp <= distance + B2_LINEAR_SLOP || sq <= distance + B2_LINEAR_SLOP )\n\t\t\t{\n\t\t\t\tb2ManifoldPoint* mp;\n\t\t\t\tmp = manifold.points + 0;\n\t\t\t\tmp->anchorA = b2MulAdd( cp, 0.5f * ( radiusA - radiusB - sp ), normalA );\n\t\t\t\tmp->separation = sp - radius;\n\t\t\t\tmp->id = B2_MAKE_ID( 0, 0 );\n\n\t\t\t\tmp = manifold.points + 1;\n\t\t\t\tmp->anchorA = b2MulAdd( cq, 0.5f * ( radiusA - radiusB - sq ), normalA );\n\t\t\t\tmp->separation = sq - radius;\n\t\t\t\tmp->id = B2_MAKE_ID( 0, 1 );\n\t\t\t\tmanifold.pointCount = 2;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// normal always points from A to B\n\t\t\tmanifold.normal = b2Neg( normalB );\n\n\t\t\tb2Vec2 cp = p1;\n\t\t\tb2Vec2 cq = q1;\n\n\t\t\t// clip to p2\n\t\t\tif ( fp1 < 0.0f && fq1 > 0.0f )\n\t\t\t{\n\t\t\t\tcp = b2Lerp( p1, q1, ( 0.0f - fp1 ) / ( fq1 - fp1 ) );\n\t\t\t}\n\t\t\telse if ( fq1 < 0.0f && fp1 > 0.0f )\n\t\t\t{\n\t\t\t\tcq = b2Lerp( q1, p1, ( 0.0f - fq1 ) / ( fp1 - fq1 ) );\n\t\t\t}\n\n\t\t\t// clip to q2\n\t\t\tif ( fp1 > length2 && fq1 < length2 )\n\t\t\t{\n\t\t\t\tcp = b2Lerp( p1, q1, ( fp1 - length2 ) / ( fp1 - fq1 ) );\n\t\t\t}\n\t\t\telse if ( fq1 > length2 && fp1 < length2 )\n\t\t\t{\n\t\t\t\tcq = b2Lerp( q1, p1, ( fq1 - length2 ) / ( fq1 - fp1 ) );\n\t\t\t}\n\n\t\t\tfloat sp = b2Dot( b2Sub( cp, p2 ), normalB );\n\t\t\tfloat sq = b2Dot( b2Sub( cq, p2 ), normalB );\n\n\t\t\tif ( sp <= distance + B2_LINEAR_SLOP || sq <= distance + B2_LINEAR_SLOP )\n\t\t\t{\n\t\t\t\tb2ManifoldPoint* mp;\n\t\t\t\tmp = manifold.points + 0;\n\t\t\t\tmp->anchorA = b2MulAdd( cp, 0.5f * ( radiusB - radiusA - sp ), normalB );\n\t\t\t\tmp->separation = sp - radius;\n\t\t\t\tmp->id = B2_MAKE_ID( 0, 0 );\n\t\t\t\tmp = manifold.points + 1;\n\t\t\t\tmp->anchorA = b2MulAdd( cq, 0.5f * ( radiusB - radiusA - sq ), normalB );\n\t\t\t\tmp->separation = sq - radius;\n\t\t\t\tmp->id = B2_MAKE_ID( 1, 0 );\n\t\t\t\tmanifold.pointCount = 2;\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( manifold.pointCount == 0 )\n\t{\n\t\t// single point collision\n\t\tb2Vec2 normal = b2Sub( closest2, closest1 );\n\t\tif ( b2Dot( normal, normal ) > epsSqr )\n\t\t{\n\t\t\tnormal = b2Normalize( normal );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnormal = b2LeftPerp( u1 );\n\t\t}\n\n\t\tb2Vec2 c1 = b2MulAdd( closest1, radiusA, normal );\n\t\tb2Vec2 c2 = b2MulAdd( closest2, -radiusB, normal );\n\n\t\tint i1 = f1 == 0.0f ? 0 : 1;\n\t\tint i2 = f2 == 0.0f ? 0 : 1;\n\n\t\tmanifold.normal = normal;\n\t\tmanifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );\n\t\tmanifold.points[0].separation = sqrtf( distanceSquared ) - radius;\n\t\tmanifold.points[0].id = B2_MAKE_ID( i1, i2 );\n\t\tmanifold.pointCount = 1;\n\t}\n\n\t// Convert manifold to world space\n\tmanifold.normal = b2RotateVector( xfA.q, manifold.normal );\n\tfor ( int i = 0; i < manifold.pointCount; ++i )\n\t{\n\t\tb2ManifoldPoint* mp = manifold.points + i;\n\n\t\t// anchor points relative to shape origin in world space\n\t\tmp->anchorA = b2RotateVector( xfA.q, b2Add( mp->anchorA, origin ) );\n\t\tmp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );\n\t\tmp->point = b2Add( xfA.p, mp->anchorA );\n\t}\n\n\treturn manifold;\n}\n\nb2Manifold b2CollideSegmentAndCapsule( const b2Segment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB )\n{\n\tb2Capsule capsuleA = { segmentA->point1, segmentA->point2, 0.0f };\n\treturn b2CollideCapsules( &capsuleA, xfA, capsuleB, xfB );\n}\n\nb2Manifold b2CollidePolygonAndCapsule( const b2Polygon* polygonA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB )\n{\n\tb2Polygon polyB = b2MakeCapsule( capsuleB->center1, capsuleB->center2, capsuleB->radius );\n\treturn b2CollidePolygons( polygonA, xfA, &polyB, xfB );\n}\n\n// Polygon clipper used to compute contact points when there are potentially two contact points.\nstatic b2Manifold b2ClipPolygons( const b2Polygon* polyA, const b2Polygon* polyB, int edgeA, int edgeB, bool flip )\n{\n\tb2Manifold manifold = { 0 };\n\n\t// reference polygon\n\tconst b2Polygon* poly1;\n\tint i11, i12;\n\n\t// incident polygon\n\tconst b2Polygon* poly2;\n\tint i21, i22;\n\n\tif ( flip )\n\t{\n\t\tpoly1 = polyB;\n\t\tpoly2 = polyA;\n\t\ti11 = edgeB;\n\t\ti12 = edgeB + 1 < polyB->count ? edgeB + 1 : 0;\n\t\ti21 = edgeA;\n\t\ti22 = edgeA + 1 < polyA->count ? edgeA + 1 : 0;\n\t}\n\telse\n\t{\n\t\tpoly1 = polyA;\n\t\tpoly2 = polyB;\n\t\ti11 = edgeA;\n\t\ti12 = edgeA + 1 < polyA->count ? edgeA + 1 : 0;\n\t\ti21 = edgeB;\n\t\ti22 = edgeB + 1 < polyB->count ? edgeB + 1 : 0;\n\t}\n\n\tb2Vec2 normal = poly1->normals[i11];\n\n\t// Reference edge vertices\n\tb2Vec2 v11 = poly1->vertices[i11];\n\tb2Vec2 v12 = poly1->vertices[i12];\n\n\t// Incident edge vertices\n\tb2Vec2 v21 = poly2->vertices[i21];\n\tb2Vec2 v22 = poly2->vertices[i22];\n\n\tb2Vec2 tangent = b2CrossSV( 1.0f, normal );\n\n\tfloat lower1 = 0.0f;\n\tfloat upper1 = b2Dot( b2Sub( v12, v11 ), tangent );\n\n\t// Incident edge points opposite of tangent due to CCW winding\n\tfloat upper2 = b2Dot( b2Sub( v21, v11 ), tangent );\n\tfloat lower2 = b2Dot( b2Sub( v22, v11 ), tangent );\n\n\t// Are the segments disjoint?\n\tif ( upper2 < lower1 || upper1 < lower2 )\n\t{\n\t\treturn manifold;\n\t}\n\n\tb2Vec2 vLower;\n\tif ( lower2 < lower1 && upper2 - lower2 > FLT_EPSILON )\n\t{\n\t\tvLower = b2Lerp( v22, v21, ( lower1 - lower2 ) / ( upper2 - lower2 ) );\n\t}\n\telse\n\t{\n\t\tvLower = v22;\n\t}\n\n\tb2Vec2 vUpper;\n\tif ( upper2 > upper1 && upper2 - lower2 > FLT_EPSILON )\n\t{\n\t\tvUpper = b2Lerp( v22, v21, ( upper1 - lower2 ) / ( upper2 - lower2 ) );\n\t}\n\telse\n\t{\n\t\tvUpper = v21;\n\t}\n\n\t// todo vLower can be very close to vUpper, reduce to one point?\n\n\tfloat separationLower = b2Dot( b2Sub( vLower, v11 ), normal );\n\tfloat separationUpper = b2Dot( b2Sub( vUpper, v11 ), normal );\n\n\tfloat r1 = poly1->radius;\n\tfloat r2 = poly2->radius;\n\n\t// Put contact points at midpoint, accounting for radii\n\tvLower = b2MulAdd( vLower, 0.5f * ( r1 - r2 - separationLower ), normal );\n\tvUpper = b2MulAdd( vUpper, 0.5f * ( r1 - r2 - separationUpper ), normal );\n\n\tfloat radius = r1 + r2;\n\n\tif ( flip == false )\n\t{\n\t\tmanifold.normal = normal;\n\t\tb2ManifoldPoint* cp = manifold.points + 0;\n\n\t\t{\n\t\t\tcp->anchorA = vLower;\n\t\t\tcp->separation = separationLower - radius;\n\t\t\tcp->id = B2_MAKE_ID( i11, i22 );\n\t\t\tmanifold.pointCount += 1;\n\t\t\tcp += 1;\n\t\t}\n\n\t\t{\n\t\t\tcp->anchorA = vUpper;\n\t\t\tcp->separation = separationUpper - radius;\n\t\t\tcp->id = B2_MAKE_ID( i12, i21 );\n\t\t\tmanifold.pointCount += 1;\n\t\t}\n\t}\n\telse\n\t{\n\t\tmanifold.normal = b2Neg( normal );\n\t\tb2ManifoldPoint* cp = manifold.points + 0;\n\n\t\t{\n\t\t\tcp->anchorA = vUpper;\n\t\t\tcp->separation = separationUpper - radius;\n\t\t\tcp->id = B2_MAKE_ID( i21, i12 );\n\t\t\tmanifold.pointCount += 1;\n\t\t\tcp += 1;\n\t\t}\n\n\t\t{\n\t\t\tcp->anchorA = vLower;\n\t\t\tcp->separation = separationLower - radius;\n\t\t\tcp->id = B2_MAKE_ID( i22, i11 );\n\t\t\tmanifold.pointCount += 1;\n\t\t}\n\t}\n\n\treturn manifold;\n}\n\n// Find the max separation between poly1 and poly2 using edge normals from poly1.\nstatic float b2FindMaxSeparation( int* edgeIndex, const b2Polygon* poly1, const b2Polygon* poly2 )\n{\n\tint count1 = poly1->count;\n\tint count2 = poly2->count;\n\tconst b2Vec2* n1s = poly1->normals;\n\tconst b2Vec2* v1s = poly1->vertices;\n\tconst b2Vec2* v2s = poly2->vertices;\n\n\tint bestIndex = 0;\n\tfloat maxSeparation = -FLT_MAX;\n\tfor ( int i = 0; i < count1; ++i )\n\t{\n\t\t// Get poly1 normal in frame2.\n\t\tb2Vec2 n = n1s[i];\n\t\tb2Vec2 v1 = v1s[i];\n\n\t\t// Find the deepest point for normal i.\n\t\tfloat si = FLT_MAX;\n\t\tfor ( int j = 0; j < count2; ++j )\n\t\t{\n\t\t\tfloat sij = b2Dot( n, b2Sub( v2s[j], v1 ) );\n\t\t\tif ( sij < si )\n\t\t\t{\n\t\t\t\tsi = sij;\n\t\t\t}\n\t\t}\n\n\t\tif ( si > maxSeparation )\n\t\t{\n\t\t\tmaxSeparation = si;\n\t\t\tbestIndex = i;\n\t\t}\n\t}\n\n\t*edgeIndex = bestIndex;\n\treturn maxSeparation;\n}\n\n// Due to speculation, every polygon is rounded\n// Algorithm:\n//\n// compute edge separation using the separating axis test (SAT)\n// if (separation > speculation_distance)\n//   return\n// find reference and incident edge\n// if separation >= 0.1f * B2_LINEAR_SLOP\n//   compute closest points between reference and incident edge\n//   if vertices are closest\n//      single vertex-vertex contact\n//   else\n//      clip edges\n//   end\n// else\n//   clip edges\n// end\n\nb2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB )\n{\n\tb2Vec2 origin = polygonA->vertices[0];\n\tfloat linearSlop = B2_LINEAR_SLOP;\n\tfloat speculativeDistance = B2_SPECULATIVE_DISTANCE;\n\n\t// Shift polyA to origin\n\t// pw = q * pb + p\n\t// pw = q * (pbs + origin) + p\n\t// pw = q * pbs + (p + q * origin)\n\tb2Transform sfA = { b2Add( xfA.p, b2RotateVector( xfA.q, origin ) ), xfA.q };\n\tb2Transform xf = b2InvMulTransforms( sfA, xfB );\n\n\tb2Polygon localPolyA;\n\tlocalPolyA.count = polygonA->count;\n\tlocalPolyA.radius = polygonA->radius;\n\tlocalPolyA.vertices[0] = b2Vec2_zero;\n\tlocalPolyA.normals[0] = polygonA->normals[0];\n\tfor ( int i = 1; i < localPolyA.count; ++i )\n\t{\n\t\tlocalPolyA.vertices[i] = b2Sub( polygonA->vertices[i], origin );\n\t\tlocalPolyA.normals[i] = polygonA->normals[i];\n\t}\n\n\t// Put polyB in polyA's frame to reduce round-off error\n\tb2Polygon localPolyB;\n\tlocalPolyB.count = polygonB->count;\n\tlocalPolyB.radius = polygonB->radius;\n\tfor ( int i = 0; i < localPolyB.count; ++i )\n\t{\n\t\tlocalPolyB.vertices[i] = b2TransformPoint( xf, polygonB->vertices[i] );\n\t\tlocalPolyB.normals[i] = b2RotateVector( xf.q, polygonB->normals[i] );\n\t}\n\n\tint edgeA = 0;\n\tfloat separationA = b2FindMaxSeparation( &edgeA, &localPolyA, &localPolyB );\n\n\tint edgeB = 0;\n\tfloat separationB = b2FindMaxSeparation( &edgeB, &localPolyB, &localPolyA );\n\n\tfloat radius = localPolyA.radius + localPolyB.radius;\n\n\tif ( separationA > speculativeDistance + radius || separationB > speculativeDistance + radius )\n\t{\n\t\treturn (b2Manifold){ 0 };\n\t}\n\n\t// Find incident edge\n\tbool flip;\n\tif ( separationA >= separationB )\n\t{\n\t\tflip = false;\n\n\t\tb2Vec2 searchDirection = localPolyA.normals[edgeA];\n\n\t\t// Find the incident edge on polyB\n\t\tint count = localPolyB.count;\n\t\tconst b2Vec2* normals = localPolyB.normals;\n\t\tedgeB = 0;\n\t\tfloat minDot = FLT_MAX;\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tfloat dot = b2Dot( searchDirection, normals[i] );\n\t\t\tif ( dot < minDot )\n\t\t\t{\n\t\t\t\tminDot = dot;\n\t\t\t\tedgeB = i;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tflip = true;\n\n\t\tb2Vec2 searchDirection = localPolyB.normals[edgeB];\n\n\t\t// Find the incident edge on polyA\n\t\tint count = localPolyA.count;\n\t\tconst b2Vec2* normals = localPolyA.normals;\n\t\tedgeA = 0;\n\t\tfloat minDot = FLT_MAX;\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tfloat dot = b2Dot( searchDirection, normals[i] );\n\t\t\tif ( dot < minDot )\n\t\t\t{\n\t\t\t\tminDot = dot;\n\t\t\t\tedgeA = i;\n\t\t\t}\n\t\t}\n\t}\n\n\tb2Manifold manifold = { 0 };\n\n\t// Using slop here to ensure vertex-vertex normal vectors can be safely normalized\n\t// todo this means edge clipping needs to handle slightly non-overlapping edges.\n\tif ( separationA > 0.1f * linearSlop || separationB > 0.1f * linearSlop )\n\t{\n#if 1\n\t\t// Edges are disjoint. Find closest points between reference edge and incident edge\n\t\t// Reference edge on polygon A\n\t\tint i11 = edgeA;\n\t\tint i12 = edgeA + 1 < localPolyA.count ? edgeA + 1 : 0;\n\t\tint i21 = edgeB;\n\t\tint i22 = edgeB + 1 < localPolyB.count ? edgeB + 1 : 0;\n\n\t\tb2Vec2 v11 = localPolyA.vertices[i11];\n\t\tb2Vec2 v12 = localPolyA.vertices[i12];\n\t\tb2Vec2 v21 = localPolyB.vertices[i21];\n\t\tb2Vec2 v22 = localPolyB.vertices[i22];\n\n\t\tb2SegmentDistanceResult result = b2SegmentDistance( v11, v12, v21, v22 );\n\t\tB2_ASSERT( result.distanceSquared > 0.0f );\n\t\tfloat distance = sqrtf( result.distanceSquared );\n\t\tfloat separation = distance - radius;\n\n\t\tif ( distance - radius > speculativeDistance )\n\t\t{\n\t\t\t// This can happen in the vertex-vertex case\n\t\t\treturn manifold;\n\t\t}\n\n\t\t// Attempt to clip edges\n\t\tmanifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip );\n\n\t\tfloat minSeparation = FLT_MAX;\n\t\tfor ( int i = 0; i < manifold.pointCount; ++i )\n\t\t{\n\t\t\tminSeparation = b2MinFloat( minSeparation, manifold.points[i].separation );\n\t\t}\n\n\t\t// Does vertex-vertex have substantially larger separation?\n\t\tif ( separation + 0.1f * linearSlop < minSeparation )\n\t\t{\n\t\t\tif ( result.fraction1 == 0.0f && result.fraction2 == 0.0f )\n\t\t\t{\n\t\t\t\t// v11 - v21\n\t\t\t\tb2Vec2 normal = b2Sub( v21, v11 );\n\t\t\t\tfloat invDistance = 1.0f / distance;\n\t\t\t\tnormal.x *= invDistance;\n\t\t\t\tnormal.y *= invDistance;\n\n\t\t\t\tb2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );\n\t\t\t\tb2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );\n\n\t\t\t\tmanifold.normal = normal;\n\t\t\t\tmanifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );\n\t\t\t\tmanifold.points[0].separation = distance - radius;\n\t\t\t\tmanifold.points[0].id = B2_MAKE_ID( i11, i21 );\n\t\t\t\tmanifold.pointCount = 1;\n\t\t\t}\n\t\t\telse if ( result.fraction1 == 0.0f && result.fraction2 == 1.0f )\n\t\t\t{\n\t\t\t\t// v11 - v22\n\t\t\t\tb2Vec2 normal = b2Sub( v22, v11 );\n\t\t\t\tfloat invDistance = 1.0f / distance;\n\t\t\t\tnormal.x *= invDistance;\n\t\t\t\tnormal.y *= invDistance;\n\n\t\t\t\tb2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );\n\t\t\t\tb2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );\n\n\t\t\t\tmanifold.normal = normal;\n\t\t\t\tmanifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );\n\t\t\t\tmanifold.points[0].separation = distance - radius;\n\t\t\t\tmanifold.points[0].id = B2_MAKE_ID( i11, i22 );\n\t\t\t\tmanifold.pointCount = 1;\n\t\t\t}\n\t\t\telse if ( result.fraction1 == 1.0f && result.fraction2 == 0.0f )\n\t\t\t{\n\t\t\t\t// v12 - v21\n\t\t\t\tb2Vec2 normal = b2Sub( v21, v12 );\n\t\t\t\tfloat invDistance = 1.0f / distance;\n\t\t\t\tnormal.x *= invDistance;\n\t\t\t\tnormal.y *= invDistance;\n\n\t\t\t\tb2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );\n\t\t\t\tb2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );\n\n\t\t\t\tmanifold.normal = normal;\n\t\t\t\tmanifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );\n\t\t\t\tmanifold.points[0].separation = distance - radius;\n\t\t\t\tmanifold.points[0].id = B2_MAKE_ID( i12, i21 );\n\t\t\t\tmanifold.pointCount = 1;\n\t\t\t}\n\t\t\telse if ( result.fraction1 == 1.0f && result.fraction2 == 1.0f )\n\t\t\t{\n\t\t\t\t// v12 - v22\n\t\t\t\tb2Vec2 normal = b2Sub( v22, v12 );\n\t\t\t\tfloat invDistance = 1.0f / distance;\n\t\t\t\tnormal.x *= invDistance;\n\t\t\t\tnormal.y *= invDistance;\n\n\t\t\t\tb2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );\n\t\t\t\tb2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );\n\n\t\t\t\tmanifold.normal = normal;\n\t\t\t\tmanifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );\n\t\t\t\tmanifold.points[0].separation = distance - radius;\n\t\t\t\tmanifold.points[0].id = B2_MAKE_ID( i12, i22 );\n\t\t\t\tmanifold.pointCount = 1;\n\t\t\t}\n\t\t}\n#else\n\t\t// Polygons are disjoint. Find closest points between reference edge and incident edge\n\t\t// Reference edge on polygon A\n\t\tint i11 = edgeA;\n\t\tint i12 = edgeA + 1 < localPolyA.count ? edgeA + 1 : 0;\n\t\tint i21 = edgeB;\n\t\tint i22 = edgeB + 1 < localPolyB.count ? edgeB + 1 : 0;\n\n\t\tb2Vec2 v11 = localPolyA.vertices[i11];\n\t\tb2Vec2 v12 = localPolyA.vertices[i12];\n\t\tb2Vec2 v21 = localPolyB.vertices[i21];\n\t\tb2Vec2 v22 = localPolyB.vertices[i22];\n\n\t\tb2SegmentDistanceResult result = b2SegmentDistance( v11, v12, v21, v22 );\n\n\t\tif ( result.fraction1 == 0.0f && result.fraction2 == 0.0f )\n\t\t{\n\t\t\t// v11 - v21\n\t\t\tb2Vec2 normal = b2Sub( v21, v11 );\n\t\t\tB2_ASSERT( result.distanceSquared > 0.0f );\n\t\t\tfloat distance = sqrtf( result.distanceSquared );\n\t\t\tif ( distance > B2_SPECULATIVE_DISTANCE + radius )\n\t\t\t{\n\t\t\t\treturn manifold;\n\t\t\t}\n\t\t\tfloat invDistance = 1.0f / distance;\n\t\t\tnormal.x *= invDistance;\n\t\t\tnormal.y *= invDistance;\n\n\t\t\tb2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );\n\t\t\tb2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );\n\n\t\t\tmanifold.normal = normal;\n\t\t\tmanifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );\n\t\t\tmanifold.points[0].separation = distance - radius;\n\t\t\tmanifold.points[0].id = B2_MAKE_ID( i11, i21 );\n\t\t\tmanifold.pointCount = 1;\n\t\t}\n\t\telse if ( result.fraction1 == 0.0f && result.fraction2 == 1.0f )\n\t\t{\n\t\t\t// v11 - v22\n\t\t\tb2Vec2 normal = b2Sub( v22, v11 );\n\t\t\tB2_ASSERT( result.distanceSquared > 0.0f );\n\t\t\tfloat distance = sqrtf( result.distanceSquared );\n\t\t\tif ( distance > B2_SPECULATIVE_DISTANCE + radius )\n\t\t\t{\n\t\t\t\treturn manifold;\n\t\t\t}\n\t\t\tfloat invDistance = 1.0f / distance;\n\t\t\tnormal.x *= invDistance;\n\t\t\tnormal.y *= invDistance;\n\n\t\t\tb2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );\n\t\t\tb2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );\n\n\t\t\tmanifold.normal = normal;\n\t\t\tmanifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );\n\t\t\tmanifold.points[0].separation = distance - radius;\n\t\t\tmanifold.points[0].id = B2_MAKE_ID( i11, i22 );\n\t\t\tmanifold.pointCount = 1;\n\t\t}\n\t\telse if ( result.fraction1 == 1.0f && result.fraction2 == 0.0f )\n\t\t{\n\t\t\t// v12 - v21\n\t\t\tb2Vec2 normal = b2Sub( v21, v12 );\n\t\t\tB2_ASSERT( result.distanceSquared > 0.0f );\n\t\t\tfloat distance = sqrtf( result.distanceSquared );\n\t\t\tif ( distance > B2_SPECULATIVE_DISTANCE + radius )\n\t\t\t{\n\t\t\t\treturn manifold;\n\t\t\t}\n\t\t\tfloat invDistance = 1.0f / distance;\n\t\t\tnormal.x *= invDistance;\n\t\t\tnormal.y *= invDistance;\n\n\t\t\tb2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );\n\t\t\tb2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );\n\n\t\t\tmanifold.normal = normal;\n\t\t\tmanifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );\n\t\t\tmanifold.points[0].separation = distance - radius;\n\t\t\tmanifold.points[0].id = B2_MAKE_ID( i12, i21 );\n\t\t\tmanifold.pointCount = 1;\n\t\t}\n\t\telse if ( result.fraction1 == 1.0f && result.fraction2 == 1.0f )\n\t\t{\n\t\t\t// v12 - v22\n\t\t\tb2Vec2 normal = b2Sub( v22, v12 );\n\t\t\tB2_ASSERT( result.distanceSquared > 0.0f );\n\t\t\tfloat distance = sqrtf( result.distanceSquared );\n\t\t\tif ( distance > B2_SPECULATIVE_DISTANCE + radius )\n\t\t\t{\n\t\t\t\treturn manifold;\n\t\t\t}\n\t\t\tfloat invDistance = 1.0f / distance;\n\t\t\tnormal.x *= invDistance;\n\t\t\tnormal.y *= invDistance;\n\n\t\t\tb2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );\n\t\t\tb2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );\n\n\t\t\tmanifold.normal = normal;\n\t\t\tmanifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );\n\t\t\tmanifold.points[0].separation = distance - radius;\n\t\t\tmanifold.points[0].id = B2_MAKE_ID( i12, i22 );\n\t\t\tmanifold.pointCount = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Edge region\n\t\t\tmanifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip );\n\t\t}\n#endif\n\t}\n\telse\n\t{\n\t\t// Polygons overlap\n\t\tmanifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip );\n\t}\n\n\t// Convert manifold to world space\n\tif ( manifold.pointCount > 0 )\n\t{\n\t\tmanifold.normal = b2RotateVector( xfA.q, manifold.normal );\n\t\tfor ( int i = 0; i < manifold.pointCount; ++i )\n\t\t{\n\t\t\tb2ManifoldPoint* mp = manifold.points + i;\n\n\t\t\t// anchor points relative to shape origin in world space\n\t\t\tmp->anchorA = b2RotateVector( xfA.q, b2Add( mp->anchorA, origin ) );\n\t\t\tmp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );\n\t\t\tmp->point = b2Add( xfA.p, mp->anchorA );\n\t\t}\n\t}\n\n\treturn manifold;\n}\n\nb2Manifold b2CollideSegmentAndCircle( const b2Segment* segmentA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )\n{\n\tb2Capsule capsuleA = { segmentA->point1, segmentA->point2, 0.0f };\n\treturn b2CollideCapsuleAndCircle( &capsuleA, xfA, circleB, xfB );\n}\n\nb2Manifold b2CollideSegmentAndPolygon( const b2Segment* segmentA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB )\n{\n\tb2Polygon polygonA = b2MakeCapsule( segmentA->point1, segmentA->point2, 0.0f );\n\treturn b2CollidePolygons( &polygonA, xfA, polygonB, xfB );\n}\n\nb2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Transform xfA, const b2Circle* circleB,\n\t\t\t\t\t\t\t\t\t\t   b2Transform xfB )\n{\n\tb2Manifold manifold = { 0 };\n\n\tb2Transform xf = b2InvMulTransforms( xfA, xfB );\n\n\t// Compute circle in frame of segment\n\tb2Vec2 pB = b2TransformPoint( xf, circleB->center );\n\n\tb2Vec2 p1 = segmentA->segment.point1;\n\tb2Vec2 p2 = segmentA->segment.point2;\n\tb2Vec2 e = b2Sub( p2, p1 );\n\n\t// Normal points to the right\n\tfloat offset = b2Dot( b2RightPerp( e ), b2Sub( pB, p1 ) );\n\tif ( offset < 0.0f )\n\t{\n\t\t// collision is one-sided\n\t\treturn manifold;\n\t}\n\n\t// Barycentric coordinates\n\tfloat u = b2Dot( e, b2Sub( p2, pB ) );\n\tfloat v = b2Dot( e, b2Sub( pB, p1 ) );\n\n\tb2Vec2 pA;\n\n\tif ( v <= 0.0f )\n\t{\n\t\t// Behind point1?\n\t\t// Is pB in the Voronoi region of the previous edge?\n\t\tb2Vec2 prevEdge = b2Sub( p1, segmentA->ghost1 );\n\t\tfloat uPrev = b2Dot( prevEdge, b2Sub( pB, p1 ) );\n\t\tif ( uPrev <= 0.0f )\n\t\t{\n\t\t\treturn manifold;\n\t\t}\n\n\t\tpA = p1;\n\t}\n\telse if ( u <= 0.0f )\n\t{\n\t\t// Ahead of point2?\n\t\tb2Vec2 nextEdge = b2Sub( segmentA->ghost2, p2 );\n\t\tfloat vNext = b2Dot( nextEdge, b2Sub( pB, p2 ) );\n\n\t\t// Is pB in the Voronoi region of the next edge?\n\t\tif ( vNext > 0.0f )\n\t\t{\n\t\t\treturn manifold;\n\t\t}\n\n\t\tpA = p2;\n\t}\n\telse\n\t{\n\t\tfloat ee = b2Dot( e, e );\n\t\tpA = (b2Vec2){ u * p1.x + v * p2.x, u * p1.y + v * p2.y };\n\t\tpA = ee > 0.0f ? b2MulSV( 1.0f / ee, pA ) : p1;\n\t}\n\n\tfloat distance;\n\tb2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pB, pA ) );\n\n\tfloat radius = circleB->radius;\n\tfloat separation = distance - radius;\n\tif ( separation > B2_SPECULATIVE_DISTANCE )\n\t{\n\t\treturn manifold;\n\t}\n\n\tb2Vec2 cA = pA;\n\tb2Vec2 cB = b2MulAdd( pB, -radius, normal );\n\tb2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );\n\n\tmanifold.normal = b2RotateVector( xfA.q, normal );\n\n\tb2ManifoldPoint* mp = manifold.points + 0;\n\tmp->anchorA = b2RotateVector( xfA.q, contactPointA );\n\tmp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );\n\tmp->point = b2Add( xfA.p, mp->anchorA );\n\tmp->separation = separation;\n\tmp->id = 0;\n\tmanifold.pointCount = 1;\n\treturn manifold;\n}\n\nb2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB,\n\t\t\t\t\t\t\t\t\t\t\tb2Transform xfB, b2SimplexCache* cache )\n{\n\tb2Polygon polyB = b2MakeCapsule( capsuleB->center1, capsuleB->center2, capsuleB->radius );\n\treturn b2CollideChainSegmentAndPolygon( segmentA, xfA, &polyB, xfB, cache );\n}\n\nstatic b2Manifold b2ClipSegments( b2Vec2 a1, b2Vec2 a2, b2Vec2 b1, b2Vec2 b2, b2Vec2 normal, float ra, float rb, uint16_t id1,\n\t\t\t\t\t\t\t\t  uint16_t id2 )\n{\n\tb2Manifold manifold = { 0 };\n\n\tb2Vec2 tangent = b2LeftPerp( normal );\n\n\t// Barycentric coordinates of each point relative to a1 along tangent\n\tfloat lower1 = 0.0f;\n\tfloat upper1 = b2Dot( b2Sub( a2, a1 ), tangent );\n\n\t// Incident edge points opposite of tangent due to CCW winding\n\tfloat upper2 = b2Dot( b2Sub( b1, a1 ), tangent );\n\tfloat lower2 = b2Dot( b2Sub( b2, a1 ), tangent );\n\n\t// Do segments overlap?\n\tif ( upper2 < lower1 || upper1 < lower2 )\n\t{\n\t\treturn manifold;\n\t}\n\n\tb2Vec2 vLower;\n\tif ( lower2 < lower1 && upper2 - lower2 > FLT_EPSILON )\n\t{\n\t\tvLower = b2Lerp( b2, b1, ( lower1 - lower2 ) / ( upper2 - lower2 ) );\n\t}\n\telse\n\t{\n\t\tvLower = b2;\n\t}\n\n\tb2Vec2 vUpper;\n\tif ( upper2 > upper1 && upper2 - lower2 > FLT_EPSILON )\n\t{\n\t\tvUpper = b2Lerp( b2, b1, ( upper1 - lower2 ) / ( upper2 - lower2 ) );\n\t}\n\telse\n\t{\n\t\tvUpper = b1;\n\t}\n\n\t// todo vLower can be very close to vUpper, reduce to one point?\n\n\tfloat separationLower = b2Dot( b2Sub( vLower, a1 ), normal );\n\tfloat separationUpper = b2Dot( b2Sub( vUpper, a1 ), normal );\n\n\t// Put contact points at midpoint, accounting for radii\n\tvLower = b2MulAdd( vLower, 0.5f * ( ra - rb - separationLower ), normal );\n\tvUpper = b2MulAdd( vUpper, 0.5f * ( ra - rb - separationUpper ), normal );\n\n\tfloat radius = ra + rb;\n\n\tmanifold.normal = normal;\n\t{\n\t\tb2ManifoldPoint* cp = manifold.points + 0;\n\t\tcp->anchorA = vLower;\n\t\tcp->separation = separationLower - radius;\n\t\tcp->id = id1;\n\t}\n\n\t{\n\t\tb2ManifoldPoint* cp = manifold.points + 1;\n\t\tcp->anchorA = vUpper;\n\t\tcp->separation = separationUpper - radius;\n\t\tcp->id = id2;\n\t}\n\n\tmanifold.pointCount = 2;\n\n\treturn manifold;\n}\n\nenum b2NormalType\n{\n\t// This means the normal points in a direction that is non-smooth relative to a convex vertex and should be skipped\n\tb2_normalSkip,\n\n\t// This means the normal points in a direction that is smooth relative to a convex vertex and should be used for collision\n\tb2_normalAdmit,\n\n\t// This means the normal is in a region of a concave vertex and should be snapped to the segment normal\n\tb2_normalSnap\n};\n\nstruct b2ChainSegmentParams\n{\n\tb2Vec2 edge1;\n\tb2Vec2 normal0;\n\tb2Vec2 normal2;\n\tbool convex1;\n\tbool convex2;\n};\n\n// Evaluate Gauss map\n// See https://box2d.org/posts/2020/06/ghost-collisions/\nstatic enum b2NormalType b2ClassifyNormal( struct b2ChainSegmentParams params, b2Vec2 normal )\n{\n\tconst float sinTol = 0.01f;\n\n\tif ( b2Dot( normal, params.edge1 ) <= 0.0f )\n\t{\n\t\t// Normal points towards the segment tail\n\t\tif ( params.convex1 )\n\t\t{\n\t\t\tif ( b2Cross( normal, params.normal0 ) > sinTol )\n\t\t\t{\n\t\t\t\treturn b2_normalSkip;\n\t\t\t}\n\n\t\t\treturn b2_normalAdmit;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn b2_normalSnap;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Normal points towards segment head\n\t\tif ( params.convex2 )\n\t\t{\n\t\t\tif ( b2Cross( params.normal2, normal ) > sinTol )\n\t\t\t{\n\t\t\t\treturn b2_normalSkip;\n\t\t\t}\n\n\t\t\treturn b2_normalAdmit;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn b2_normalSnap;\n\t\t}\n\t}\n}\n\nb2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB,\n\t\t\t\t\t\t\t\t\t\t\tb2Transform xfB, b2SimplexCache* cache )\n{\n\tb2Manifold manifold = { 0 };\n\n\tb2Transform xf = b2InvMulTransforms( xfA, xfB );\n\n\tb2Vec2 centroidB = b2TransformPoint( xf, polygonB->centroid );\n\tfloat radiusB = polygonB->radius;\n\n\tb2Vec2 p1 = segmentA->segment.point1;\n\tb2Vec2 p2 = segmentA->segment.point2;\n\n\tb2Vec2 edge1 = b2Normalize( b2Sub( p2, p1 ) );\n\n\tstruct b2ChainSegmentParams smoothParams = { 0 };\n\tsmoothParams.edge1 = edge1;\n\n\tconst float convexTol = 0.01f;\n\tb2Vec2 edge0 = b2Normalize( b2Sub( p1, segmentA->ghost1 ) );\n\tsmoothParams.normal0 = b2RightPerp( edge0 );\n\tsmoothParams.convex1 = b2Cross( edge0, edge1 ) >= convexTol;\n\n\tb2Vec2 edge2 = b2Normalize( b2Sub( segmentA->ghost2, p2 ) );\n\tsmoothParams.normal2 = b2RightPerp( edge2 );\n\tsmoothParams.convex2 = b2Cross( edge1, edge2 ) >= convexTol;\n\n\t// Normal points to the right\n\tb2Vec2 normal1 = b2RightPerp( edge1 );\n\tbool behind1 = b2Dot( normal1, b2Sub( centroidB, p1 ) ) < 0.0f;\n\tbool behind0 = true;\n\tbool behind2 = true;\n\tif ( smoothParams.convex1 )\n\t{\n\t\tbehind0 = b2Dot( smoothParams.normal0, b2Sub( centroidB, p1 ) ) < 0.0f;\n\t}\n\n\tif ( smoothParams.convex2 )\n\t{\n\t\tbehind2 = b2Dot( smoothParams.normal2, b2Sub( centroidB, p2 ) ) < 0.0f;\n\t}\n\n\tif ( behind1 && behind0 && behind2 )\n\t{\n\t\t// one-sided collision\n\t\treturn manifold;\n\t}\n\n\t// Get polygonB in frameA\n\tint count = polygonB->count;\n\tb2Vec2 vertices[B2_MAX_POLYGON_VERTICES];\n\tb2Vec2 normals[B2_MAX_POLYGON_VERTICES];\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tvertices[i] = b2TransformPoint( xf, polygonB->vertices[i] );\n\t\tnormals[i] = b2RotateVector( xf.q, polygonB->normals[i] );\n\t}\n\n\t// Distance doesn't work correctly with partial polygons\n\tb2DistanceInput input;\n\tinput.proxyA = b2MakeProxy( &segmentA->segment.point1, 2, 0.0f );\n\tinput.proxyB = b2MakeProxy( vertices, count, 0.0f );\n\tinput.transformA = b2Transform_identity;\n\tinput.transformB = b2Transform_identity;\n\tinput.useRadii = false;\n\n\tb2DistanceOutput output = b2ShapeDistance( &input, cache, NULL, 0 );\n\n\tif ( output.distance > radiusB + B2_SPECULATIVE_DISTANCE )\n\t{\n\t\treturn manifold;\n\t}\n\n\t// Snap concave normals for partial polygon\n\tb2Vec2 n0 = smoothParams.convex1 ? smoothParams.normal0 : normal1;\n\tb2Vec2 n2 = smoothParams.convex2 ? smoothParams.normal2 : normal1;\n\n\t// Index of incident vertex on polygon\n\tint incidentIndex = -1;\n\tint incidentNormal = -1;\n\n\tif ( behind1 == false && output.distance > 0.1f * B2_LINEAR_SLOP )\n\t{\n\t\t// The closest features may be two vertices or an edge and a vertex even when there should\n\t\t// be face contact\n\n\t\tif ( cache->count == 1 )\n\t\t{\n\t\t\t// vertex-vertex collision\n\t\t\tb2Vec2 pA = output.pointA;\n\t\t\tb2Vec2 pB = output.pointB;\n\n\t\t\tb2Vec2 normal = b2Normalize( b2Sub( pB, pA ) );\n\n\t\t\tenum b2NormalType type = b2ClassifyNormal( smoothParams, normal );\n\t\t\tif ( type == b2_normalSkip )\n\t\t\t{\n\t\t\t\treturn manifold;\n\t\t\t}\n\n\t\t\tif ( type == b2_normalAdmit )\n\t\t\t{\n\t\t\t\tmanifold.normal = b2RotateVector( xfA.q, normal );\n\t\t\t\tb2ManifoldPoint* cp = manifold.points + 0;\n\t\t\t\tcp->anchorA = b2RotateVector( xfA.q, pA );\n\t\t\t\tcp->anchorB = b2Add( cp->anchorA, b2Sub( xfA.p, xfB.p ) );\n\t\t\t\tcp->point = b2Add( xfA.p, cp->anchorA );\n\t\t\t\tcp->separation = output.distance - radiusB;\n\t\t\t\tcp->id = B2_MAKE_ID( cache->indexA[0], cache->indexB[0] );\n\t\t\t\tmanifold.pointCount = 1;\n\t\t\t\treturn manifold;\n\t\t\t}\n\n\t\t\t// fall through b2_normalSnap\n\t\t\tincidentIndex = cache->indexB[0];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// vertex-edge collision\n\t\t\tB2_ASSERT( cache->count == 2 );\n\n\t\t\tint ia1 = cache->indexA[0];\n\t\t\tint ia2 = cache->indexA[1];\n\t\t\tint ib1 = cache->indexB[0];\n\t\t\tint ib2 = cache->indexB[1];\n\n\t\t\tif ( ia1 == ia2 )\n\t\t\t{\n\t\t\t\t// 1 point on A, expect 2 points on B\n\t\t\t\tB2_ASSERT( ib1 != ib2 );\n\n\t\t\t\t// Find polygon normal most aligned with vector between closest points.\n\t\t\t\t// This effectively sorts ib1 and ib2\n\t\t\t\tb2Vec2 normalB = b2Sub( output.pointA, output.pointB );\n\t\t\t\tfloat dot1 = b2Dot( normalB, normals[ib1] );\n\t\t\t\tfloat dot2 = b2Dot( normalB, normals[ib2] );\n\t\t\t\tint ib = dot1 > dot2 ? ib1 : ib2;\n\n\t\t\t\t// Use accurate normal\n\t\t\t\tnormalB = normals[ib];\n\n\t\t\t\tenum b2NormalType type = b2ClassifyNormal( smoothParams, b2Neg( normalB ) );\n\t\t\t\tif ( type == b2_normalSkip )\n\t\t\t\t{\n\t\t\t\t\treturn manifold;\n\t\t\t\t}\n\n\t\t\t\tif ( type == b2_normalAdmit )\n\t\t\t\t{\n\t\t\t\t\t// Get polygon edge associated with normal\n\t\t\t\t\tib1 = ib;\n\t\t\t\t\tib2 = ib < count - 1 ? ib + 1 : 0;\n\n\t\t\t\t\tb2Vec2 b1 = vertices[ib1];\n\t\t\t\t\tb2Vec2 b2 = vertices[ib2];\n\n\t\t\t\t\t// Find incident segment vertex\n\t\t\t\t\tdot1 = b2Dot( normalB, b2Sub( p1, b1 ) );\n\t\t\t\t\tdot2 = b2Dot( normalB, b2Sub( p2, b1 ) );\n\n\t\t\t\t\tif ( dot1 < dot2 )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( b2Dot( n0, normalB ) < b2Dot( normal1, normalB ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Neighbor is incident\n\t\t\t\t\t\t\treturn manifold;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( b2Dot( n2, normalB ) < b2Dot( normal1, normalB ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Neighbor is incident\n\t\t\t\t\t\t\treturn manifold;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tmanifold =\n\t\t\t\t\t\tb2ClipSegments( b1, b2, p1, p2, normalB, radiusB, 0.0f, B2_MAKE_ID( ib1, 1 ), B2_MAKE_ID( ib2, 0 ) );\n\n\t\t\t\t\tB2_ASSERT( manifold.pointCount == 0 || manifold.pointCount == 2 );\n\t\t\t\t\tif ( manifold.pointCount == 2 )\n\t\t\t\t\t{\n\t\t\t\t\t\tmanifold.normal = b2RotateVector( xfA.q, b2Neg( normalB ) );\n\t\t\t\t\t\tmanifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA );\n\t\t\t\t\t\tmanifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA );\n\t\t\t\t\t\tb2Vec2 pAB = b2Sub( xfA.p, xfB.p );\n\t\t\t\t\t\tmanifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB );\n\t\t\t\t\t\tmanifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB );\n\t\t\t\t\t\tmanifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA );\n\t\t\t\t\t\tmanifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA );\n\t\t\t\t\t}\n\t\t\t\t\treturn manifold;\n\t\t\t\t}\n\n\t\t\t\t// fall through b2_normalSnap\n\t\t\t\tincidentNormal = ib;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Get index of incident polygonB vertex\n\t\t\t\tfloat dot1 = b2Dot( normal1, b2Sub( vertices[ib1], p1 ) );\n\t\t\t\tfloat dot2 = b2Dot( normal1, b2Sub( vertices[ib2], p2 ) );\n\t\t\t\tincidentIndex = dot1 < dot2 ? ib1 : ib2;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// SAT edge normal\n\t\tfloat edgeSeparation = FLT_MAX;\n\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tfloat s = b2Dot( normal1, b2Sub( vertices[i], p1 ) );\n\t\t\tif ( s < edgeSeparation )\n\t\t\t{\n\t\t\t\tedgeSeparation = s;\n\t\t\t\tincidentIndex = i;\n\t\t\t}\n\t\t}\n\n\t\t// Check convex neighbor for edge separation\n\t\tif ( smoothParams.convex1 )\n\t\t{\n\t\t\tfloat s0 = FLT_MAX;\n\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tfloat s = b2Dot( smoothParams.normal0, b2Sub( vertices[i], p1 ) );\n\t\t\t\tif ( s < s0 )\n\t\t\t\t{\n\t\t\t\t\ts0 = s;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( s0 > edgeSeparation )\n\t\t\t{\n\t\t\t\tedgeSeparation = s0;\n\n\t\t\t\t// Indicate neighbor owns edge separation\n\t\t\t\tincidentIndex = -1;\n\t\t\t}\n\t\t}\n\n\t\t// Check convex neighbor for edge separation\n\t\tif ( smoothParams.convex2 )\n\t\t{\n\t\t\tfloat s2 = FLT_MAX;\n\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tfloat s = b2Dot( smoothParams.normal2, b2Sub( vertices[i], p2 ) );\n\t\t\t\tif ( s < s2 )\n\t\t\t\t{\n\t\t\t\t\ts2 = s;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( s2 > edgeSeparation )\n\t\t\t{\n\t\t\t\tedgeSeparation = s2;\n\n\t\t\t\t// Indicate neighbor owns edge separation\n\t\t\t\tincidentIndex = -1;\n\t\t\t}\n\t\t}\n\n\t\t// SAT polygon normals\n\t\tfloat polygonSeparation = -FLT_MAX;\n\t\tint referenceIndex = -1;\n\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tb2Vec2 n = normals[i];\n\n\t\t\tenum b2NormalType type = b2ClassifyNormal( smoothParams, b2Neg( n ) );\n\t\t\tif ( type != b2_normalAdmit )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check the infinite sides of the partial polygon\n\t\t\t// if ((smoothParams.convex1 && b2Cross(n0, n) > 0.0f) || (smoothParams.convex2 && b2Cross(n, n2) > 0.0f))\n\t\t\t//{\n\t\t\t//\tcontinue;\n\t\t\t//}\n\n\t\t\tb2Vec2 p = vertices[i];\n\t\t\tfloat s = b2MinFloat( b2Dot( n, b2Sub( p2, p ) ), b2Dot( n, b2Sub( p1, p ) ) );\n\n\t\t\tif ( s > polygonSeparation )\n\t\t\t{\n\t\t\t\tpolygonSeparation = s;\n\t\t\t\treferenceIndex = i;\n\t\t\t}\n\t\t}\n\n\t\tif ( polygonSeparation > edgeSeparation )\n\t\t{\n\t\t\tint ia1 = referenceIndex;\n\t\t\tint ia2 = ia1 < count - 1 ? ia1 + 1 : 0;\n\t\t\tb2Vec2 a1 = vertices[ia1];\n\t\t\tb2Vec2 a2 = vertices[ia2];\n\n\t\t\tb2Vec2 n = normals[ia1];\n\n\t\t\tfloat dot1 = b2Dot( n, b2Sub( p1, a1 ) );\n\t\t\tfloat dot2 = b2Dot( n, b2Sub( p2, a1 ) );\n\n\t\t\tif ( dot1 < dot2 )\n\t\t\t{\n\t\t\t\tif ( b2Dot( n0, n ) < b2Dot( normal1, n ) )\n\t\t\t\t{\n\t\t\t\t\t// Neighbor is incident\n\t\t\t\t\treturn manifold;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ( b2Dot( n2, n ) < b2Dot( normal1, n ) )\n\t\t\t\t{\n\t\t\t\t\t// Neighbor is incident\n\t\t\t\t\treturn manifold;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmanifold = b2ClipSegments( a1, a2, p1, p2, normals[ia1], radiusB, 0.0f, B2_MAKE_ID( ia1, 1 ), B2_MAKE_ID( ia2, 0 ) );\n\n\t\t\tB2_ASSERT( manifold.pointCount == 0 || manifold.pointCount == 2 );\n\t\t\tif ( manifold.pointCount == 2 )\n\t\t\t{\n\n\t\t\t\tmanifold.normal = b2RotateVector( xfA.q, b2Neg( normals[ia1] ) );\n\t\t\t\tmanifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA );\n\t\t\t\tmanifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA );\n\t\t\t\tb2Vec2 pAB = b2Sub( xfA.p, xfB.p );\n\t\t\t\tmanifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB );\n\t\t\t\tmanifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB );\n\t\t\t\tmanifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA );\n\t\t\t\tmanifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA );\n\t\t\t}\n\n\t\t\treturn manifold;\n\t\t}\n\n\t\tif ( incidentIndex == -1 )\n\t\t{\n\t\t\t// neighboring segment is the separating axis\n\t\t\treturn manifold;\n\t\t}\n\n\t\t// fall through segment normal axis\n\t}\n\n\tB2_ASSERT( incidentNormal != -1 || incidentIndex != -1 );\n\n\t// Segment normal\n\n\t// Find incident polygon normal: normal adjacent to deepest vertex that is most anti-parallel to segment normal\n\tb2Vec2 b1, b2;\n\tint ib1, ib2;\n\n\tif ( incidentNormal != -1 )\n\t{\n\t\tib1 = incidentNormal;\n\t\tib2 = ib1 < count - 1 ? ib1 + 1 : 0;\n\t\tb1 = vertices[ib1];\n\t\tb2 = vertices[ib2];\n\t}\n\telse\n\t{\n\t\tint i2 = incidentIndex;\n\t\tint i1 = i2 > 0 ? i2 - 1 : count - 1;\n\t\tfloat d1 = b2Dot( normal1, normals[i1] );\n\t\tfloat d2 = b2Dot( normal1, normals[i2] );\n\t\tif ( d1 < d2 )\n\t\t{\n\t\t\tib1 = i1, ib2 = i2;\n\t\t\tb1 = vertices[ib1];\n\t\t\tb2 = vertices[ib2];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tib1 = i2, ib2 = i2 < count - 1 ? i2 + 1 : 0;\n\t\t\tb1 = vertices[ib1];\n\t\t\tb2 = vertices[ib2];\n\t\t}\n\t}\n\n\tmanifold = b2ClipSegments( p1, p2, b1, b2, normal1, 0.0f, radiusB, B2_MAKE_ID( 0, ib2 ), B2_MAKE_ID( 1, ib1 ) );\n\n\tB2_ASSERT( manifold.pointCount == 0 || manifold.pointCount == 2 );\n\tif ( manifold.pointCount == 2 )\n\t{\n\t\t// There may be no points c\n\t\tmanifold.normal = b2RotateVector( xfA.q, manifold.normal );\n\t\tmanifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA );\n\t\tmanifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA );\n\t\tb2Vec2 pAB = b2Sub( xfA.p, xfB.p );\n\t\tmanifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB );\n\t\tmanifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB );\n\t\tmanifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA );\n\t\tmanifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA );\n\t}\n\n\treturn manifold;\n}\n"
  },
  {
    "path": "src/math_functions.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n\n_Static_assert( sizeof( int32_t ) == sizeof( int ), \"Box2D expects int32_t and int to be the same\" );\n\nbool b2IsValidFloat( float a )\n{\n\tif ( isnan( a ) )\n\t{\n\t\treturn false;\n\t}\n\n\tif ( isinf( a ) )\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool b2IsValidVec2( b2Vec2 v )\n{\n\tif ( isnan( v.x ) || isnan( v.y ) )\n\t{\n\t\treturn false;\n\t}\n\n\tif ( isinf( v.x ) || isinf( v.y ) )\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool b2IsValidRotation( b2Rot q )\n{\n\tif ( isnan( q.s ) || isnan( q.c ) )\n\t{\n\t\treturn false;\n\t}\n\n\tif ( isinf( q.s ) || isinf( q.c ) )\n\t{\n\t\treturn false;\n\t}\n\n\treturn b2IsNormalizedRot( q );\n}\n\nbool b2IsValidTransform(b2Transform t)\n{\n\tif (b2IsValidVec2(t.p) == false)\n\t{\n\t\treturn false;\n\t}\n\n\treturn b2IsValidRotation( t.q );\n}\n\nbool b2IsValidPlane( b2Plane a )\n{\n\treturn b2IsValidVec2( a.normal ) && b2IsNormalized( a.normal ) && b2IsValidFloat( a.offset );\n}\n\n// https://stackoverflow.com/questions/46210708/atan2-approximation-with-11bits-in-mantissa-on-x86with-sse2-and-armwith-vfpv4\nfloat b2Atan2( float y, float x )\n{\n\t// Added check for (0,0) to match atan2f and avoid NaN\n\tif (x == 0.0f && y == 0.0f)\n\t{\n\t\treturn 0.0f;\n\t}\n\n\tfloat ax = b2AbsFloat( x );\n\tfloat ay = b2AbsFloat( y );\n\tfloat mx = b2MaxFloat( ay, ax );\n\tfloat mn = b2MinFloat( ay, ax );\n\tfloat a = mn / mx;\n\n\t// Minimax polynomial approximation to atan(a) on [0,1]\n\tfloat s = a * a;\n\tfloat c = s * a;\n\tfloat q = s * s;\n\tfloat r = 0.024840285f * q + 0.18681418f;\n\tfloat t = -0.094097948f * q - 0.33213072f;\n\tr = r * s + t;\n\tr = r * c + a;\n\n\t// Map to full circle\n\tif ( ay > ax )\n\t{\n\t\tr = 1.57079637f - r;\n\t}\n\n\tif ( x < 0 )\n\t{\n\t\tr = 3.14159274f - r;\n\t}\n\n\tif ( y < 0 )\n\t{\n\t\tr = -r;\n\t}\n\n\treturn r;\n}\n\n// Approximate cosine and sine for determinism. In my testing cosf and sinf produced\n// the same results on x64 and ARM using MSVC, GCC, and Clang. However, I don't trust\n// this result.\n// https://en.wikipedia.org/wiki/Bh%C4%81skara_I%27s_sine_approximation_formula\nb2CosSin b2ComputeCosSin( float radians )\n{\n\tfloat x = b2UnwindAngle( radians );\n\tfloat pi2 = B2_PI * B2_PI;\n\n\t// cosine needs angle in [-pi/2, pi/2]\n\tfloat c;\n\tif ( x < -0.5f * B2_PI )\n\t{\n\t\tfloat y = x + B2_PI;\n\t\tfloat y2 = y * y;\n\t\tc = -( pi2 - 4.0f * y2 ) / ( pi2 + y2 );\n\t}\n\telse if ( x > 0.5f * B2_PI )\n\t{\n\t\tfloat y = x - B2_PI;\n\t\tfloat y2 = y * y;\n\t\tc = -( pi2 - 4.0f * y2 ) / ( pi2 + y2 );\n\t}\n\telse\n\t{\n\t\tfloat y2 = x * x;\n\t\tc = ( pi2 - 4.0f * y2 ) / ( pi2 + y2 );\n\t}\n\n\t// sine needs angle in [0, pi]\n\tfloat s;\n\tif ( x < 0.0f )\n\t{\n\t\tfloat y = x + B2_PI;\n\t\ts = -16.0f * y * ( B2_PI - y ) / ( 5.0f * pi2 - 4.0f * y * ( B2_PI - y ) );\n\t}\n\telse\n\t{\n\t\ts = 16.0f * x * ( B2_PI - x ) / ( 5.0f * pi2 - 4.0f * x * ( B2_PI - x ) );\n\t}\n\n\tfloat mag = sqrtf( s * s + c * c );\n\tfloat invMag = mag > 0.0f ? 1.0f / mag : 0.0f;\n\tb2CosSin cs = { c * invMag, s * invMag };\n\treturn cs;\n}\n\nb2Rot b2ComputeRotationBetweenUnitVectors(b2Vec2 v1, b2Vec2 v2)\n{\n\tB2_ASSERT( b2AbsFloat( 1.0f - b2Length( v1 ) ) < 100.0f * FLT_EPSILON );\n\tB2_ASSERT( b2AbsFloat( 1.0f - b2Length( v2 ) ) < 100.0f * FLT_EPSILON );\n\n\tb2Rot rot;\n\trot.c = b2Dot( v1, v2 );\n\trot.s = b2Cross( v1, v2 );\n\treturn b2NormalizeRot( rot );\n}\n"
  },
  {
    "path": "src/motor_joint.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"body.h\"\n#include \"core.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"solver.h\"\n#include \"solver_set.h\"\n\n// needed for dll export\n#include \"box2d/box2d.h\"\n\nvoid b2MotorJoint_SetLinearVelocity( b2JointId jointId, b2Vec2 velocity )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.linearVelocity = velocity;\n}\n\nb2Vec2 b2MotorJoint_GetLinearVelocity( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.linearVelocity;\n}\n\nvoid b2MotorJoint_SetAngularVelocity( b2JointId jointId, float velocity )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.angularVelocity = velocity;\n}\n\nfloat b2MotorJoint_GetAngularVelocity( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.angularVelocity;\n}\n\nvoid b2MotorJoint_SetMaxVelocityTorque( b2JointId jointId, float maxTorque )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.maxVelocityTorque = maxTorque;\n}\n\nfloat b2MotorJoint_GetMaxVelocityTorque( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.maxVelocityTorque;\n}\n\nvoid b2MotorJoint_SetMaxVelocityForce( b2JointId jointId, float maxForce )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.maxVelocityForce = maxForce;\n}\n\nfloat b2MotorJoint_GetMaxVelocityForce( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.maxVelocityForce;\n}\n\nvoid b2MotorJoint_SetLinearHertz( b2JointId jointId, float hertz )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.linearHertz = hertz;\n}\n\nfloat b2MotorJoint_GetLinearHertz( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.linearHertz;\n}\n\nvoid b2MotorJoint_SetLinearDampingRatio( b2JointId jointId, float damping )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.linearDampingRatio = damping;\n}\n\nfloat b2MotorJoint_GetLinearDampingRatio( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.linearDampingRatio;\n}\n\nvoid b2MotorJoint_SetAngularHertz( b2JointId jointId, float hertz )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.angularHertz = hertz;\n}\n\nfloat b2MotorJoint_GetAngularHertz( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.angularHertz;\n}\n\nvoid b2MotorJoint_SetAngularDampingRatio( b2JointId jointId, float damping )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.angularDampingRatio = damping;\n}\n\nfloat b2MotorJoint_GetAngularDampingRatio( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.angularDampingRatio;\n}\n\nvoid b2MotorJoint_SetMaxSpringForce( b2JointId jointId, float maxForce )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.maxSpringForce = b2MaxFloat( 0.0f, maxForce );\n}\n\nfloat b2MotorJoint_GetMaxSpringForce( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.maxSpringForce;\n}\n\nvoid b2MotorJoint_SetMaxSpringTorque( b2JointId jointId, float maxTorque )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\tjoint->motorJoint.maxSpringTorque = b2MaxFloat( 0.0f, maxTorque );\n}\n\nfloat b2MotorJoint_GetMaxSpringTorque( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint );\n\treturn joint->motorJoint.maxSpringTorque;\n}\n\nb2Vec2 b2GetMotorJointForce( b2World* world, b2JointSim* base )\n{\n\tb2Vec2 force = b2MulSV( world->inv_h, b2Add( base->motorJoint.linearVelocityImpulse, base->motorJoint.linearSpringImpulse ) );\n\treturn force;\n}\n\nfloat b2GetMotorJointTorque( b2World* world, b2JointSim* base )\n{\n\treturn world->inv_h * ( base->motorJoint.angularVelocityImpulse + base->motorJoint.angularSpringImpulse );\n}\n\n// Point-to-point constraint\n// C = p2 - p1\n// Cdot = v2 - v1\n//      = v2 + cross(w2, r2) - v1 - cross(w1, r1)\n// J = [-I -r1_skew I r2_skew ]\n// Identity used:\n// w k % (rx i + ry j) = w * (-ry i + rx j)\n\n// Angle constraint\n// C = angle2 - angle1 - referenceAngle\n// Cdot = w2 - w1\n// J = [0 0 -1 0 0 1]\n// K = invI1 + invI2\n\nvoid b2PrepareMotorJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_motorJoint );\n\n\t// chase body id to the solver set where the body lives\n\tint idA = base->bodyIdA;\n\tint idB = base->bodyIdB;\n\n\tb2World* world = context->world;\n\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, idA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, idB );\n\n\tB2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet );\n\n\tb2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex );\n\tb2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex );\n\n\tint localIndexA = bodyA->localIndex;\n\tint localIndexB = bodyB->localIndex;\n\n\tb2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA );\n\tb2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB );\n\n\tfloat mA = bodySimA->invMass;\n\tfloat iA = bodySimA->invInertia;\n\tfloat mB = bodySimB->invMass;\n\tfloat iB = bodySimB->invInertia;\n\n\tbase->invMassA = mA;\n\tbase->invMassB = mB;\n\tbase->invIA = iA;\n\tbase->invIB = iB;\n\n\tb2MotorJoint* joint = &base->motorJoint;\n\tjoint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX;\n\tjoint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX;\n\n\t// Compute joint anchor frames with world space rotation, relative to center of mass\n\tjoint->frameA.q = b2MulRot( bodySimA->transform.q, base->localFrameA.q );\n\tjoint->frameA.p = b2RotateVector( bodySimA->transform.q, b2Sub( base->localFrameA.p, bodySimA->localCenter ) );\n\tjoint->frameB.q = b2MulRot( bodySimB->transform.q, base->localFrameB.q );\n\tjoint->frameB.p = b2RotateVector( bodySimB->transform.q, b2Sub( base->localFrameB.p, bodySimB->localCenter ) );\n\n\t// Compute the initial center delta. Incremental position updates are relative to this.\n\tjoint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center );\n\n\tb2Vec2 rA = joint->frameA.p;\n\tb2Vec2 rB = joint->frameB.p;\n\n\tjoint->linearSpring = b2MakeSoft( joint->linearHertz, joint->linearDampingRatio, context->h );\n\tjoint->angularSpring = b2MakeSoft( joint->angularHertz, joint->angularDampingRatio, context->h );\n\n\tb2Mat22 kl;\n\tkl.cx.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB;\n\tkl.cx.y = -rA.y * rA.x * iA - rB.y * rB.x * iB;\n\tkl.cy.x = kl.cx.y;\n\tkl.cy.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB;\n\tjoint->linearMass = b2GetInverse22( kl );\n\n\tfloat ka = iA + iB;\n\tjoint->angularMass = ka > 0.0f ? 1.0f / ka : 0.0f;\n\n\tif ( context->enableWarmStarting == false )\n\t{\n\t\tjoint->linearVelocityImpulse = b2Vec2_zero;\n\t\tjoint->angularVelocityImpulse = 0.0f;\n\t\tjoint->linearSpringImpulse = b2Vec2_zero;\n\t\tjoint->angularSpringImpulse = 0.0f;\n\t}\n}\n\nvoid b2WarmStartMotorJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_motorJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\tb2MotorJoint* joint = &base->motorJoint;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\tb2Vec2 linearImpulse = b2Add( joint->linearVelocityImpulse, joint->linearSpringImpulse );\n\tfloat angularImpulse = joint->angularVelocityImpulse + joint->angularSpringImpulse;\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, linearImpulse );\n\t\tstateA->angularVelocity -= iA * ( b2Cross( rA, linearImpulse ) + angularImpulse );\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, linearImpulse );\n\t\tstateB->angularVelocity += iB * ( b2Cross( rB, linearImpulse ) + angularImpulse );\n\t}\n}\n\nvoid b2SolveMotorJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_motorJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2MotorJoint* joint = &base->motorJoint;\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 vA = stateA->linearVelocity;\n\tfloat wA = stateA->angularVelocity;\n\tb2Vec2 vB = stateB->linearVelocity;\n\tfloat wB = stateB->angularVelocity;\n\n\t// angular spring\n\tif ( joint->maxSpringTorque > 0.0f && joint->angularHertz > 0.0f )\n\t{\n\t\tb2Rot qA = b2MulRot( stateA->deltaRotation, joint->frameA.q );\n\t\tb2Rot qB = b2MulRot( stateB->deltaRotation, joint->frameB.q );\n\t\tb2Rot relQ = b2InvMulRot( qA, qB );\n\n\t\tfloat c = b2Rot_GetAngle( relQ );\n\t\tfloat bias = joint->angularSpring.biasRate * c;\n\t\tfloat massScale = joint->angularSpring.massScale;\n\t\tfloat impulseScale = joint->angularSpring.impulseScale;\n\n\t\tfloat cdot = wB - wA;\n\n\t\tfloat maxImpulse = context->h * joint->maxSpringTorque;\n\t\tfloat oldImpulse = joint->angularSpringImpulse;\n\t\tfloat impulse = -massScale * joint->angularMass * ( cdot + bias ) - impulseScale * oldImpulse;\n\t\tjoint->angularSpringImpulse = b2ClampFloat( oldImpulse + impulse, -maxImpulse, maxImpulse );\n\t\timpulse = joint->angularSpringImpulse - oldImpulse;\n\n\t\twA -= iA * impulse;\n\t\twB += iB * impulse;\n\t}\n\n\t// angular velocity\n\tif ( joint->maxVelocityTorque > 0.0f )\n\t{\n\t\tfloat cdot = wB - wA - joint->angularVelocity;\n\t\tfloat impulse = -joint->angularMass * cdot;\n\n\t\tfloat maxImpulse = context->h * joint->maxVelocityTorque;\n\t\tfloat oldImpulse = joint->angularVelocityImpulse;\n\t\tjoint->angularVelocityImpulse = b2ClampFloat( oldImpulse + impulse, -maxImpulse, maxImpulse );\n\t\timpulse = joint->angularVelocityImpulse - oldImpulse;\n\n\t\twA -= iA * impulse;\n\t\twB += iB * impulse;\n\t}\n\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\t// linear spring\n\tif ( joint->maxSpringForce > 0.0f && joint->linearHertz > 0.0f )\n\t{\n\t\tb2Vec2 dcA = stateA->deltaPosition;\n\t\tb2Vec2 dcB = stateB->deltaPosition;\n\t\tb2Vec2 c = b2Add( b2Add( b2Sub( dcB, dcA ), b2Sub( rB, rA ) ), joint->deltaCenter );\n\n\t\tb2Vec2 bias = b2MulSV( joint->linearSpring.biasRate, c );\n\t\tfloat massScale = joint->linearSpring.massScale;\n\t\tfloat impulseScale = joint->linearSpring.impulseScale;\n\n\t\tb2Vec2 cdot = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) );\n\t\tcdot = b2Add( cdot, bias );\n\n\t\t// Updating the effective mass here may be overkill\n\t\tb2Mat22 kl;\n\t\tkl.cx.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB;\n\t\tkl.cx.y = -rA.y * rA.x * iA - rB.y * rB.x * iB;\n\t\tkl.cy.x = kl.cx.y;\n\t\tkl.cy.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB;\n\t\tjoint->linearMass = b2GetInverse22( kl );\n\n\t\tb2Vec2 b = b2MulMV( joint->linearMass, cdot );\n\n\t\tb2Vec2 oldImpulse = joint->linearSpringImpulse;\n\t\tb2Vec2 impulse = {\n\t\t\t-massScale * b.x - impulseScale * oldImpulse.x,\n\t\t\t-massScale * b.y - impulseScale * oldImpulse.y,\n\t\t};\n\n\t\tfloat maxImpulse = context->h * joint->maxSpringForce;\n\t\tjoint->linearSpringImpulse = b2Add( joint->linearSpringImpulse, impulse );\n\n\t\tif ( b2LengthSquared( joint->linearSpringImpulse ) > maxImpulse * maxImpulse )\n\t\t{\n\t\t\tjoint->linearSpringImpulse = b2Normalize( joint->linearSpringImpulse );\n\t\t\tjoint->linearSpringImpulse.x *= maxImpulse;\n\t\t\tjoint->linearSpringImpulse.y *= maxImpulse;\n\t\t}\n\n\t\timpulse = b2Sub( joint->linearSpringImpulse, oldImpulse );\n\n\t\tvA = b2MulSub( vA, mA, impulse );\n\t\twA -= iA * b2Cross( rA, impulse );\n\t\tvB = b2MulAdd( vB, mB, impulse );\n\t\twB += iB * b2Cross( rB, impulse );\n\t}\n\n\t// linear velocity\n\tif ( joint->maxVelocityForce > 0.0f )\n\t{\n\t\tb2Vec2 cdot = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) );\n\t\tcdot = b2Sub( cdot, joint->linearVelocity );\n\t\tb2Vec2 b = b2MulMV( joint->linearMass, cdot );\n\t\tb2Vec2 impulse = { -b.x, -b.y };\n\n\t\tb2Vec2 oldImpulse = joint->linearVelocityImpulse;\n\t\tfloat maxImpulse = context->h * joint->maxVelocityForce;\n\t\tjoint->linearVelocityImpulse = b2Add( joint->linearVelocityImpulse, impulse );\n\n\t\tif ( b2LengthSquared( joint->linearVelocityImpulse ) > maxImpulse * maxImpulse )\n\t\t{\n\t\t\tjoint->linearVelocityImpulse = b2Normalize( joint->linearVelocityImpulse );\n\t\t\tjoint->linearVelocityImpulse.x *= maxImpulse;\n\t\t\tjoint->linearVelocityImpulse.y *= maxImpulse;\n\t\t}\n\n\t\timpulse = b2Sub( joint->linearVelocityImpulse, oldImpulse );\n\n\t\tvA = b2MulSub( vA, mA, impulse );\n\t\twA -= iA * b2Cross( rA, impulse );\n\t\tvB = b2MulAdd( vB, mB, impulse );\n\t\twB += iB * b2Cross( rB, impulse );\n\t}\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = vA;\n\t\tstateA->angularVelocity = wA;\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = vB;\n\t\tstateB->angularVelocity = wB;\n\t}\n}\n\n#if 0\nvoid b2DumpMotorJoint()\n{\n\tint32 indexA = m_bodyA->m_islandIndex;\n\tint32 indexB = m_bodyB->m_islandIndex;\n\n\tb2Dump(\"  b2MotorJointDef jd;\\n\");\n\tb2Dump(\"  jd.bodyA = sims[%d];\\n\", indexA);\n\tb2Dump(\"  jd.bodyB = sims[%d];\\n\", indexB);\n\tb2Dump(\"  jd.collideConnected = bool(%d);\\n\", m_collideConnected);\n\tb2Dump(\"  jd.localAnchorA.Set(%.9g, %.9g);\\n\", m_localAnchorA.x, m_localAnchorA.y);\n\tb2Dump(\"  jd.localAnchorB.Set(%.9g, %.9g);\\n\", m_localAnchorB.x, m_localAnchorB.y);\n\tb2Dump(\"  jd.referenceAngle = %.9g;\\n\", m_referenceAngle);\n\tb2Dump(\"  jd.stiffness = %.9g;\\n\", m_stiffness);\n\tb2Dump(\"  jd.damping = %.9g;\\n\", m_damping);\n\tb2Dump(\"  joints[%d] = m_world->CreateJoint(&jd);\\n\", m_index);\n}\n#endif\n"
  },
  {
    "path": "src/mover.c",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"constants.h\"\n\n#include \"box2d/collision.h\"\n\nb2PlaneSolverResult b2SolvePlanes( b2Vec2 targetDelta, b2CollisionPlane* planes, int count )\n{\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tplanes[i].push = 0.0f;\n\t}\n\n\tb2Vec2 delta = targetDelta;\n\tfloat tolerance = B2_LINEAR_SLOP;\n\n\tint iteration;\n\tfor ( iteration = 0; iteration < 20; ++iteration )\n\t{\n\t\tfloat totalPush = 0.0f;\n\t\tfor ( int planeIndex = 0; planeIndex < count; ++planeIndex )\n\t\t{\n\t\t\tb2CollisionPlane* plane = planes + planeIndex;\n\n\t\t\t// Add slop to prevent jitter\n\t\t\tfloat separation = b2PlaneSeparation( plane->plane, delta ) + B2_LINEAR_SLOP;\n\t\t\t// if (separation > 0.0f)\n\t\t\t//{\n\t\t\t//\tcontinue;\n\t\t\t// }\n\n\t\t\tfloat push = -separation;\n\n\t\t\t// Clamp accumulated push\n\t\t\tfloat accumulatedPush = plane->push;\n\t\t\tplane->push = b2ClampFloat( plane->push + push, 0.0f, plane->pushLimit );\n\t\t\tpush = plane->push - accumulatedPush;\n\t\t\tdelta = b2MulAdd( delta, push, plane->plane.normal );\n\n\t\t\t// Track maximum push for convergence\n\t\t\ttotalPush += b2AbsFloat( push );\n\t\t}\n\n\t\tif ( totalPush < tolerance )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn (b2PlaneSolverResult){\n\t\t.translation = delta,\n\t\t.iterationCount = iteration,\n\t};\n}\n\nb2Vec2 b2ClipVector( b2Vec2 vector, const b2CollisionPlane* planes, int count )\n{\n\tb2Vec2 v = vector;\n\n\tfor ( int planeIndex = 0; planeIndex < count; ++planeIndex )\n\t{\n\t\tconst b2CollisionPlane* plane = planes + planeIndex;\n\t\tif ( plane->push == 0.0f || plane->clipVelocity == false )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tv = b2MulSub( v, b2MinFloat( 0.0f, b2Dot( v, plane->plane.normal ) ), plane->plane.normal );\n\t}\n\n\treturn v;\n}\n"
  },
  {
    "path": "src/physics_world.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"physics_world.h\"\n\n#include \"arena_allocator.h\"\n#include \"array.h\"\n#include \"bitset.h\"\n#include \"body.h\"\n#include \"broad_phase.h\"\n#include \"constants.h\"\n#include \"constraint_graph.h\"\n#include \"contact.h\"\n#include \"core.h\"\n#include \"ctz.h\"\n#include \"island.h\"\n#include \"joint.h\"\n#include \"sensor.h\"\n#include \"shape.h\"\n#include \"solver.h\"\n#include \"solver_set.h\"\n\n#include \"box2d/box2d.h\"\n\n#include <float.h>\n#include <stdio.h>\n#include <string.h>\n\n_Static_assert( B2_MAX_WORLDS > 0, \"must be 1 or more\" );\n_Static_assert( B2_MAX_WORLDS < UINT16_MAX, \"B2_MAX_WORLDS limit exceeded\" );\nstatic b2World b2_worlds[B2_MAX_WORLDS];\n\nB2_ARRAY_SOURCE( b2BodyMoveEvent, b2BodyMoveEvent )\nB2_ARRAY_SOURCE( b2ContactBeginTouchEvent, b2ContactBeginTouchEvent )\nB2_ARRAY_SOURCE( b2ContactEndTouchEvent, b2ContactEndTouchEvent )\nB2_ARRAY_SOURCE( b2ContactHitEvent, b2ContactHitEvent )\nB2_ARRAY_SOURCE( b2SensorBeginTouchEvent, b2SensorBeginTouchEvent )\nB2_ARRAY_SOURCE( b2SensorEndTouchEvent, b2SensorEndTouchEvent )\nB2_ARRAY_SOURCE( b2TaskContext, b2TaskContext )\n\nb2World* b2GetWorldFromId( b2WorldId id )\n{\n\tB2_ASSERT( 1 <= id.index1 && id.index1 <= B2_MAX_WORLDS );\n\tb2World* world = b2_worlds + ( id.index1 - 1 );\n\tB2_ASSERT( id.index1 == world->worldId + 1 );\n\tB2_ASSERT( id.generation == world->generation );\n\treturn world;\n}\n\nb2World* b2GetWorld( int index )\n{\n\tB2_ASSERT( 0 <= index && index < B2_MAX_WORLDS );\n\tb2World* world = b2_worlds + index;\n\tB2_ASSERT( world->worldId == index );\n\treturn world;\n}\n\nb2World* b2GetWorldLocked( int index )\n{\n\tB2_ASSERT( 0 <= index && index < B2_MAX_WORLDS );\n\tb2World* world = b2_worlds + index;\n\tB2_ASSERT( world->worldId == index );\n\tif ( world->locked )\n\t{\n\t\tB2_ASSERT( false );\n\t\treturn NULL;\n\t}\n\n\treturn world;\n}\n\nstatic void* b2DefaultAddTaskFcn( b2TaskCallback* task, int count, int minRange, void* taskContext, void* userContext )\n{\n\tB2_UNUSED( minRange, userContext );\n\ttask( 0, count, 0, taskContext );\n\treturn NULL;\n}\n\nstatic void b2DefaultFinishTaskFcn( void* userTask, void* userContext )\n{\n\tB2_UNUSED( userTask, userContext );\n}\n\nstatic float b2DefaultFrictionCallback( float frictionA, uint64_t materialA, float frictionB, uint64_t materialB )\n{\n\tB2_UNUSED( materialA, materialB );\n\treturn sqrtf( frictionA * frictionB );\n}\n\nstatic float b2DefaultRestitutionCallback( float restitutionA, uint64_t materialA, float restitutionB, uint64_t materialB )\n{\n\tB2_UNUSED( materialA, materialB );\n\treturn b2MaxFloat( restitutionA, restitutionB );\n}\n\nb2WorldId b2CreateWorld( const b2WorldDef* def )\n{\n\t_Static_assert( B2_MAX_WORLDS < UINT16_MAX, \"B2_MAX_WORLDS limit exceeded\" );\n\tB2_CHECK_DEF( def );\n\n\tint worldId = B2_NULL_INDEX;\n\tfor ( int i = 0; i < B2_MAX_WORLDS; ++i )\n\t{\n\t\tif ( b2_worlds[i].inUse == false )\n\t\t{\n\t\t\tworldId = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif ( worldId == B2_NULL_INDEX )\n\t{\n\t\treturn (b2WorldId){ 0 };\n\t}\n\n\tb2InitializeContactRegisters();\n\n\tb2World* world = b2_worlds + worldId;\n\tuint16_t generation = world->generation;\n\n\t*world = (b2World){ 0 };\n\n\tworld->worldId = (uint16_t)worldId;\n\tworld->generation = generation;\n\tworld->inUse = true;\n\n\tworld->arena = b2CreateArenaAllocator( 2048 );\n\tb2CreateBroadPhase( &world->broadPhase );\n\tb2CreateGraph( &world->constraintGraph, 16 );\n\n\t// pools\n\tworld->bodyIdPool = b2CreateIdPool();\n\tworld->bodies = b2BodyArray_Create( 16 );\n\tworld->solverSets = b2SolverSetArray_Create( 8 );\n\n\t// add empty static, active, and disabled body sets\n\tworld->solverSetIdPool = b2CreateIdPool();\n\tb2SolverSet set = { 0 };\n\n\t// static set\n\tset.setIndex = b2AllocId( &world->solverSetIdPool );\n\tb2SolverSetArray_Push( &world->solverSets, set );\n\tB2_ASSERT( world->solverSets.data[b2_staticSet].setIndex == b2_staticSet );\n\n\t// disabled set\n\tset.setIndex = b2AllocId( &world->solverSetIdPool );\n\tb2SolverSetArray_Push( &world->solverSets, set );\n\tB2_ASSERT( world->solverSets.data[b2_disabledSet].setIndex == b2_disabledSet );\n\n\t// awake set\n\tset.setIndex = b2AllocId( &world->solverSetIdPool );\n\tb2SolverSetArray_Push( &world->solverSets, set );\n\tB2_ASSERT( world->solverSets.data[b2_awakeSet].setIndex == b2_awakeSet );\n\n\tworld->shapeIdPool = b2CreateIdPool();\n\tworld->shapes = b2ShapeArray_Create( 16 );\n\n\tworld->chainIdPool = b2CreateIdPool();\n\tworld->chainShapes = b2ChainShapeArray_Create( 4 );\n\n\tworld->contactIdPool = b2CreateIdPool();\n\tworld->contacts = b2ContactArray_Create( 16 );\n\n\tworld->jointIdPool = b2CreateIdPool();\n\tworld->joints = b2JointArray_Create( 16 );\n\n\tworld->islandIdPool = b2CreateIdPool();\n\tworld->islands = b2IslandArray_Create( 8 );\n\n\tworld->sensors = b2SensorArray_Create( 4 );\n\n\tworld->bodyMoveEvents = b2BodyMoveEventArray_Create( 4 );\n\tworld->sensorBeginEvents = b2SensorBeginTouchEventArray_Create( 4 );\n\tworld->sensorEndEvents[0] = b2SensorEndTouchEventArray_Create( 4 );\n\tworld->sensorEndEvents[1] = b2SensorEndTouchEventArray_Create( 4 );\n\tworld->contactBeginEvents = b2ContactBeginTouchEventArray_Create( 4 );\n\tworld->contactEndEvents[0] = b2ContactEndTouchEventArray_Create( 4 );\n\tworld->contactEndEvents[1] = b2ContactEndTouchEventArray_Create( 4 );\n\tworld->contactHitEvents = b2ContactHitEventArray_Create( 4 );\n\tworld->jointEvents = b2JointEventArray_Create( 4 );\n\tworld->endEventArrayIndex = 0;\n\n\tworld->stepIndex = 0;\n\tworld->splitIslandId = B2_NULL_INDEX;\n\tworld->activeTaskCount = 0;\n\tworld->taskCount = 0;\n\tworld->gravity = def->gravity;\n\tworld->hitEventThreshold = def->hitEventThreshold;\n\tworld->restitutionThreshold = def->restitutionThreshold;\n\tworld->maxLinearSpeed = def->maximumLinearSpeed;\n\tworld->contactSpeed = def->contactSpeed;\n\tworld->contactHertz = def->contactHertz;\n\tworld->contactDampingRatio = def->contactDampingRatio;\n\n\tif ( def->frictionCallback == NULL )\n\t{\n\t\tworld->frictionCallback = b2DefaultFrictionCallback;\n\t}\n\telse\n\t{\n\t\tworld->frictionCallback = def->frictionCallback;\n\t}\n\n\tif ( def->restitutionCallback == NULL )\n\t{\n\t\tworld->restitutionCallback = b2DefaultRestitutionCallback;\n\t}\n\telse\n\t{\n\t\tworld->restitutionCallback = def->restitutionCallback;\n\t}\n\n\tworld->enableSleep = def->enableSleep;\n\tworld->locked = false;\n\tworld->enableWarmStarting = true;\n\tworld->enableContactSoftening = def->enableContactSoftening;\n\tworld->enableContinuous = def->enableContinuous;\n\tworld->enableSpeculative = true;\n\tworld->userTreeTask = NULL;\n\tworld->userData = def->userData;\n\n\tif ( def->workerCount > 0 && def->enqueueTask != NULL && def->finishTask != NULL )\n\t{\n\t\tworld->workerCount = b2MinInt( def->workerCount, B2_MAX_WORKERS );\n\t\tworld->enqueueTaskFcn = def->enqueueTask;\n\t\tworld->finishTaskFcn = def->finishTask;\n\t\tworld->userTaskContext = def->userTaskContext;\n\t}\n\telse\n\t{\n\t\tworld->workerCount = 1;\n\t\tworld->enqueueTaskFcn = b2DefaultAddTaskFcn;\n\t\tworld->finishTaskFcn = b2DefaultFinishTaskFcn;\n\t\tworld->userTaskContext = NULL;\n\t}\n\n\tworld->taskContexts = b2TaskContextArray_Create( world->workerCount );\n\tb2TaskContextArray_Resize( &world->taskContexts, world->workerCount );\n\n\tworld->sensorTaskContexts = b2SensorTaskContextArray_Create( world->workerCount );\n\tb2SensorTaskContextArray_Resize( &world->sensorTaskContexts, world->workerCount );\n\n\tfor ( int i = 0; i < world->workerCount; ++i )\n\t{\n\t\tworld->taskContexts.data[i].sensorHits = b2SensorHitArray_Create( 8 );\n\t\tworld->taskContexts.data[i].contactStateBitSet = b2CreateBitSet( 1024 );\n\t\tworld->taskContexts.data[i].jointStateBitSet = b2CreateBitSet( 1024 );\n\t\tworld->taskContexts.data[i].enlargedSimBitSet = b2CreateBitSet( 256 );\n\t\tworld->taskContexts.data[i].awakeIslandBitSet = b2CreateBitSet( 256 );\n\n\t\tworld->sensorTaskContexts.data[i].eventBits = b2CreateBitSet( 128 );\n\t}\n\n\tworld->debugBodySet = b2CreateBitSet( 256 );\n\tworld->debugJointSet = b2CreateBitSet( 256 );\n\tworld->debugContactSet = b2CreateBitSet( 256 );\n\tworld->debugIslandSet = b2CreateBitSet( 256 );\n\n\t// add one to worldId so that 0 represents a null b2WorldId\n\treturn (b2WorldId){ (uint16_t)( worldId + 1 ), world->generation };\n}\n\nvoid b2DestroyWorld( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\n\tb2DestroyBitSet( &world->debugBodySet );\n\tb2DestroyBitSet( &world->debugJointSet );\n\tb2DestroyBitSet( &world->debugContactSet );\n\tb2DestroyBitSet( &world->debugIslandSet );\n\n\tfor ( int i = 0; i < world->workerCount; ++i )\n\t{\n\t\tb2SensorHitArray_Destroy( &world->taskContexts.data[i].sensorHits );\n\t\tb2DestroyBitSet( &world->taskContexts.data[i].contactStateBitSet );\n\t\tb2DestroyBitSet( &world->taskContexts.data[i].jointStateBitSet );\n\t\tb2DestroyBitSet( &world->taskContexts.data[i].enlargedSimBitSet );\n\t\tb2DestroyBitSet( &world->taskContexts.data[i].awakeIslandBitSet );\n\n\t\tb2DestroyBitSet( &world->sensorTaskContexts.data[i].eventBits );\n\t}\n\n\tb2TaskContextArray_Destroy( &world->taskContexts );\n\tb2SensorTaskContextArray_Destroy( &world->sensorTaskContexts );\n\n\tb2BodyMoveEventArray_Destroy( &world->bodyMoveEvents );\n\tb2SensorBeginTouchEventArray_Destroy( &world->sensorBeginEvents );\n\tb2SensorEndTouchEventArray_Destroy( world->sensorEndEvents + 0 );\n\tb2SensorEndTouchEventArray_Destroy( world->sensorEndEvents + 1 );\n\tb2ContactBeginTouchEventArray_Destroy( &world->contactBeginEvents );\n\tb2ContactEndTouchEventArray_Destroy( world->contactEndEvents + 0 );\n\tb2ContactEndTouchEventArray_Destroy( world->contactEndEvents + 1 );\n\tb2ContactHitEventArray_Destroy( &world->contactHitEvents );\n\tb2JointEventArray_Destroy( &world->jointEvents );\n\n\tint chainCapacity = world->chainShapes.count;\n\tfor ( int i = 0; i < chainCapacity; ++i )\n\t{\n\t\tb2ChainShape* chain = world->chainShapes.data + i;\n\t\tif ( chain->id != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2FreeChainData( chain );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tB2_ASSERT( chain->shapeIndices == NULL );\n\t\t\tB2_ASSERT( chain->materials == NULL );\n\t\t}\n\t}\n\n\tint sensorCount = world->sensors.count;\n\tfor ( int i = 0; i < sensorCount; ++i )\n\t{\n\t\tb2VisitorArray_Destroy( &world->sensors.data[i].hits );\n\t\tb2VisitorArray_Destroy( &world->sensors.data[i].overlaps1 );\n\t\tb2VisitorArray_Destroy( &world->sensors.data[i].overlaps2 );\n\t}\n\n\tb2SensorArray_Destroy( &world->sensors );\n\n\tb2BodyArray_Destroy( &world->bodies );\n\tb2ShapeArray_Destroy( &world->shapes );\n\tb2ChainShapeArray_Destroy( &world->chainShapes );\n\tb2ContactArray_Destroy( &world->contacts );\n\tb2JointArray_Destroy( &world->joints );\n\tb2IslandArray_Destroy( &world->islands );\n\n\t// Destroy solver sets\n\tint setCapacity = world->solverSets.count;\n\tfor ( int i = 0; i < setCapacity; ++i )\n\t{\n\t\tb2SolverSet* set = world->solverSets.data + i;\n\t\tif ( set->setIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2DestroySolverSet( world, i );\n\t\t}\n\t}\n\n\tb2SolverSetArray_Destroy( &world->solverSets );\n\n\tb2DestroyGraph( &world->constraintGraph );\n\tb2DestroyBroadPhase( &world->broadPhase );\n\n\tb2DestroyIdPool( &world->bodyIdPool );\n\tb2DestroyIdPool( &world->shapeIdPool );\n\tb2DestroyIdPool( &world->chainIdPool );\n\tb2DestroyIdPool( &world->contactIdPool );\n\tb2DestroyIdPool( &world->jointIdPool );\n\tb2DestroyIdPool( &world->islandIdPool );\n\tb2DestroyIdPool( &world->solverSetIdPool );\n\n\tb2DestroyArenaAllocator( &world->arena );\n\n\t// Wipe world but preserve generation\n\tuint16_t generation = world->generation;\n\t*world = (b2World){ 0 };\n\tworld->worldId = 0;\n\tworld->generation = generation + 1;\n}\n\nstatic void b2CollideTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )\n{\n\tb2TracyCZoneNC( collide_task, \"Collide\", b2_colorDodgerBlue, true );\n\n\tb2StepContext* stepContext = context;\n\tb2World* world = stepContext->world;\n\tB2_ASSERT( (int)threadIndex < world->workerCount );\n\tb2TaskContext* taskContext = world->taskContexts.data + threadIndex;\n\tb2ContactSim** contactSims = stepContext->contacts;\n\tb2Shape* shapes = world->shapes.data;\n\tb2Body* bodies = world->bodies.data;\n\n\tB2_ASSERT( startIndex < endIndex );\n\n\tfor ( int contactIndex = startIndex; contactIndex < endIndex; ++contactIndex )\n\t{\n\t\tb2ContactSim* contactSim = contactSims[contactIndex];\n\n\t\tint contactId = contactSim->contactId;\n\n\t\tb2Shape* shapeA = shapes + contactSim->shapeIdA;\n\t\tb2Shape* shapeB = shapes + contactSim->shapeIdB;\n\n\t\t// Do proxies still overlap?\n\t\tbool overlap = b2AABB_Overlaps( shapeA->fatAABB, shapeB->fatAABB );\n\t\tif ( overlap == false )\n\t\t{\n\t\t\tcontactSim->simFlags |= b2_simDisjoint;\n\t\t\tcontactSim->simFlags &= ~b2_simTouchingFlag;\n\t\t\tb2SetBit( &taskContext->contactStateBitSet, contactId );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbool wasTouching = ( contactSim->simFlags & b2_simTouchingFlag );\n\n\t\t\t// Update contact respecting shape/body order (A,B)\n\t\t\tb2Body* bodyA = bodies + shapeA->bodyId;\n\t\t\tb2Body* bodyB = bodies + shapeB->bodyId;\n\t\t\tb2BodySim* bodySimA = b2GetBodySim( world, bodyA );\n\t\t\tb2BodySim* bodySimB = b2GetBodySim( world, bodyB );\n\n\t\t\t// avoid cache misses in b2PrepareContactsTask\n\t\t\tcontactSim->bodySimIndexA = bodyA->setIndex == b2_awakeSet ? bodyA->localIndex : B2_NULL_INDEX;\n\t\t\tcontactSim->invMassA = bodySimA->invMass;\n\t\t\tcontactSim->invIA = bodySimA->invInertia;\n\n\t\t\tcontactSim->bodySimIndexB = bodyB->setIndex == b2_awakeSet ? bodyB->localIndex : B2_NULL_INDEX;\n\t\t\tcontactSim->invMassB = bodySimB->invMass;\n\t\t\tcontactSim->invIB = bodySimB->invInertia;\n\n\t\t\tb2Transform transformA = bodySimA->transform;\n\t\t\tb2Transform transformB = bodySimB->transform;\n\n\t\t\tb2Vec2 centerOffsetA = b2RotateVector( transformA.q, bodySimA->localCenter );\n\t\t\tb2Vec2 centerOffsetB = b2RotateVector( transformB.q, bodySimB->localCenter );\n\n\t\t\t// This updates solid contacts\n\t\t\tbool touching =\n\t\t\t\tb2UpdateContact( world, contactSim, shapeA, transformA, centerOffsetA, shapeB, transformB, centerOffsetB );\n\n\t\t\t// State changes that affect island connectivity. Also affects contact events.\n\t\t\tif ( touching == true && wasTouching == false )\n\t\t\t{\n\t\t\t\tcontactSim->simFlags |= b2_simStartedTouching;\n\t\t\t\tb2SetBit( &taskContext->contactStateBitSet, contactId );\n\t\t\t}\n\t\t\telse if ( touching == false && wasTouching == true )\n\t\t\t{\n\t\t\t\tcontactSim->simFlags |= b2_simStoppedTouching;\n\t\t\t\tb2SetBit( &taskContext->contactStateBitSet, contactId );\n\t\t\t}\n\n\t\t\t// To make this work, the time of impact code needs to adjust the target\n\t\t\t// distance based on the number of TOI events for a body.\n\t\t\t// if (touching && bodySimB->isFast)\n\t\t\t//{\n\t\t\t//\tb2Manifold* manifold = &contactSim->manifold;\n\t\t\t//\tint pointCount = manifold->pointCount;\n\t\t\t//\tfor (int i = 0; i < pointCount; ++i)\n\t\t\t//\t{\n\t\t\t//\t\t// trick the solver into pushing the fast shapes apart\n\t\t\t//\t\tmanifold->points[i].separation -= 0.25f * B2_SPECULATIVE_DISTANCE;\n\t\t\t//\t}\n\t\t\t//}\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( collide_task );\n}\n\nstatic void b2UpdateTreesTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )\n{\n\tB2_UNUSED( startIndex );\n\tB2_UNUSED( endIndex );\n\tB2_UNUSED( threadIndex );\n\n\tb2TracyCZoneNC( tree_task, \"Rebuild BVH\", b2_colorFireBrick, true );\n\n\tb2World* world = context;\n\tb2BroadPhase_RebuildTrees( &world->broadPhase );\n\n\tb2TracyCZoneEnd( tree_task );\n}\n\nstatic void b2AddNonTouchingContact( b2World* world, b2Contact* contact, b2ContactSim* contactSim )\n{\n\tB2_ASSERT( contact->setIndex == b2_awakeSet );\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tcontact->colorIndex = B2_NULL_INDEX;\n\tcontact->localIndex = set->contactSims.count;\n\n\tb2ContactSim* newContactSim = b2ContactSimArray_Add( &set->contactSims );\n\tmemcpy( newContactSim, contactSim, sizeof( b2ContactSim ) );\n}\n\nstatic void b2RemoveNonTouchingContact( b2World* world, int setIndex, int localIndex )\n{\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );\n\tint movedIndex = b2ContactSimArray_RemoveSwap( &set->contactSims, localIndex );\n\tif ( movedIndex != B2_NULL_INDEX )\n\t{\n\t\tb2ContactSim* movedContactSim = set->contactSims.data + localIndex;\n\t\tb2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );\n\t\tB2_ASSERT( movedContact->setIndex == setIndex );\n\t\tB2_ASSERT( movedContact->localIndex == movedIndex );\n\t\tB2_ASSERT( movedContact->colorIndex == B2_NULL_INDEX );\n\t\tmovedContact->localIndex = localIndex;\n\t}\n}\n\n// Narrow-phase collision\nstatic void b2Collide( b2StepContext* context )\n{\n\tb2World* world = context->world;\n\n\tB2_ASSERT( world->workerCount > 0 );\n\n\tb2TracyCZoneNC( collide, \"Narrow Phase\", b2_colorDodgerBlue, true );\n\n\t// Task that can be done in parallel with the narrow-phase\n\t// - rebuild the collision tree for dynamic and kinematic bodies to keep their query performance good\n\t// todo_erin move this to start when contacts are being created\n\tworld->userTreeTask = world->enqueueTaskFcn( &b2UpdateTreesTask, 1, 1, world, world->userTaskContext );\n\tworld->taskCount += 1;\n\tworld->activeTaskCount += world->userTreeTask == NULL ? 0 : 1;\n\n\t// gather contacts into a single array for easier parallel-for\n\tint contactCount = 0;\n\tb2GraphColor* graphColors = world->constraintGraph.colors;\n\tfor ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )\n\t{\n\t\tcontactCount += graphColors[i].contactSims.count;\n\t}\n\n\tint nonTouchingCount = world->solverSets.data[b2_awakeSet].contactSims.count;\n\tcontactCount += nonTouchingCount;\n\n\tif ( contactCount == 0 )\n\t{\n\t\tb2TracyCZoneEnd( collide );\n\t\treturn;\n\t}\n\n\tb2ContactSim** contactSims = b2AllocateArenaItem( &world->arena, contactCount * sizeof( b2ContactSim* ), \"contacts\" );\n\n\tint contactIndex = 0;\n\tfor ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )\n\t{\n\t\tb2GraphColor* color = graphColors + i;\n\t\tint count = color->contactSims.count;\n\t\tb2ContactSim* base = color->contactSims.data;\n\t\tfor ( int j = 0; j < count; ++j )\n\t\t{\n\t\t\tcontactSims[contactIndex] = base + j;\n\t\t\tcontactIndex += 1;\n\t\t}\n\t}\n\n\t{\n\t\tb2ContactSim* base = world->solverSets.data[b2_awakeSet].contactSims.data;\n\t\tfor ( int i = 0; i < nonTouchingCount; ++i )\n\t\t{\n\t\t\tcontactSims[contactIndex] = base + i;\n\t\t\tcontactIndex += 1;\n\t\t}\n\t}\n\n\tB2_ASSERT( contactIndex == contactCount );\n\n\tcontext->contacts = contactSims;\n\n\t// Contact bit set on ids because contact pointers are unstable as they move between touching and not touching.\n\tint contactIdCapacity = b2GetIdCapacity( &world->contactIdPool );\n\tfor ( int i = 0; i < world->workerCount; ++i )\n\t{\n\t\tb2SetBitCountAndClear( &world->taskContexts.data[i].contactStateBitSet, contactIdCapacity );\n\t}\n\n\t// Task should take at least 40us on a 4GHz CPU (10K cycles)\n\tint minRange = 64;\n\tvoid* userCollideTask = world->enqueueTaskFcn( &b2CollideTask, contactCount, minRange, context, world->userTaskContext );\n\tworld->taskCount += 1;\n\tif ( userCollideTask != NULL )\n\t{\n\t\tworld->finishTaskFcn( userCollideTask, world->userTaskContext );\n\t}\n\n\tb2FreeArenaItem( &world->arena, contactSims );\n\tcontext->contacts = NULL;\n\tcontactSims = NULL;\n\n\t// Serially update contact state\n\t// todo_erin bring this zone together with island merge\n\tb2TracyCZoneNC( contact_state, \"Contact State\", b2_colorLightSlateGray, true );\n\n\t// Bitwise OR all contact bits\n\tb2BitSet* bitSet = &world->taskContexts.data[0].contactStateBitSet;\n\tfor ( int i = 1; i < world->workerCount; ++i )\n\t{\n\t\tb2InPlaceUnion( bitSet, &world->taskContexts.data[i].contactStateBitSet );\n\t}\n\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\n\tint endEventArrayIndex = world->endEventArrayIndex;\n\n\tconst b2Shape* shapes = world->shapes.data;\n\tuint16_t worldId = world->worldId;\n\n\t// Process contact state changes. Iterate over set bits\n\tfor ( uint32_t k = 0; k < bitSet->blockCount; ++k )\n\t{\n\t\tuint64_t bits = bitSet->bits[k];\n\t\twhile ( bits != 0 )\n\t\t{\n\t\t\tuint32_t ctz = b2CTZ64( bits );\n\t\t\tint contactId = (int)( 64 * k + ctz );\n\n\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\t\tB2_ASSERT( contact->setIndex == b2_awakeSet );\n\n\t\t\tint colorIndex = contact->colorIndex;\n\t\t\tint localIndex = contact->localIndex;\n\n\t\t\tb2ContactSim* contactSim = NULL;\n\t\t\tif ( colorIndex != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\t// contact lives in constraint graph\n\t\t\t\tB2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );\n\t\t\t\tb2GraphColor* color = graphColors + colorIndex;\n\t\t\t\tcontactSim = b2ContactSimArray_Get( &color->contactSims, localIndex );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcontactSim = b2ContactSimArray_Get( &awakeSet->contactSims, localIndex );\n\t\t\t}\n\n\t\t\tconst b2Shape* shapeA = shapes + contact->shapeIdA;\n\t\t\tconst b2Shape* shapeB = shapes + contact->shapeIdB;\n\t\t\tb2ShapeId shapeIdA = { shapeA->id + 1, worldId, shapeA->generation };\n\t\t\tb2ShapeId shapeIdB = { shapeB->id + 1, worldId, shapeB->generation };\n\t\t\tb2ContactId contactFullId = {\n\t\t\t\t.index1 = contactId + 1,\n\t\t\t\t.world0 = worldId,\n\t\t\t\t.padding = 0,\n\t\t\t\t.generation = contact->generation,\n\t\t\t};\n\t\t\tuint32_t flags = contact->flags;\n\t\t\tuint32_t simFlags = contactSim->simFlags;\n\n\t\t\tif ( simFlags & b2_simDisjoint )\n\t\t\t{\n\t\t\t\t// Bounding boxes no longer overlap\n\t\t\t\tb2DestroyContact( world, contact, false );\n\t\t\t\tcontact = NULL;\n\t\t\t\tcontactSim = NULL;\n\t\t\t}\n\t\t\telse if ( simFlags & b2_simStartedTouching )\n\t\t\t{\n\t\t\t\tB2_ASSERT( contact->islandId == B2_NULL_INDEX );\n\n\t\t\t\tif ( flags & b2_contactEnableContactEvents )\n\t\t\t\t{\n\t\t\t\t\tb2ContactBeginTouchEvent event = { shapeIdA, shapeIdB, contactFullId };\n\t\t\t\t\tb2ContactBeginTouchEventArray_Push( &world->contactBeginEvents, event );\n\t\t\t\t}\n\n\t\t\t\tB2_ASSERT( contactSim->manifold.pointCount > 0 );\n\t\t\t\tB2_ASSERT( contact->setIndex == b2_awakeSet );\n\n\t\t\t\t// Link first because this wakes colliding bodies and ensures the body sims\n\t\t\t\t// are in the correct place.\n\t\t\t\tcontact->flags |= b2_contactTouchingFlag;\n\t\t\t\tb2LinkContact( world, contact );\n\n\t\t\t\t// Make sure these didn't change\n\t\t\t\tB2_ASSERT( contact->colorIndex == B2_NULL_INDEX );\n\t\t\t\tB2_ASSERT( contact->localIndex == localIndex );\n\n\t\t\t\t// Contact sim pointer may have become orphaned due to awake set growth,\n\t\t\t\t// so I just need to refresh it.\n\t\t\t\tcontactSim = b2ContactSimArray_Get( &awakeSet->contactSims, localIndex );\n\n\t\t\t\tcontactSim->simFlags &= ~b2_simStartedTouching;\n\n\t\t\t\tb2AddContactToGraph( world, contactSim, contact );\n\t\t\t\tb2RemoveNonTouchingContact( world, b2_awakeSet, localIndex );\n\t\t\t\tcontactSim = NULL;\n\t\t\t}\n\t\t\telse if ( simFlags & b2_simStoppedTouching )\n\t\t\t{\n\t\t\t\tcontactSim->simFlags &= ~b2_simStoppedTouching;\n\t\t\t\tcontact->flags &= ~b2_contactTouchingFlag;\n\n\t\t\t\tif ( contact->flags & b2_contactEnableContactEvents )\n\t\t\t\t{\n\t\t\t\t\tb2ContactEndTouchEvent event = { shapeIdA, shapeIdB, contactFullId };\n\t\t\t\t\tb2ContactEndTouchEventArray_Push( world->contactEndEvents + endEventArrayIndex, event );\n\t\t\t\t}\n\n\t\t\t\tB2_ASSERT( contactSim->manifold.pointCount == 0 );\n\n\t\t\t\tb2UnlinkContact( world, contact );\n\t\t\t\tint bodyIdA = contact->edges[0].bodyId;\n\t\t\t\tint bodyIdB = contact->edges[1].bodyId;\n\n\t\t\t\tb2AddNonTouchingContact( world, contact, contactSim );\n\t\t\t\tb2RemoveContactFromGraph( world, bodyIdA, bodyIdB, colorIndex, localIndex );\n\t\t\t\tcontact = NULL;\n\t\t\t\tcontactSim = NULL;\n\t\t\t}\n\n\t\t\t// Clear the smallest set bit\n\t\t\tbits = bits & ( bits - 1 );\n\t\t}\n\t}\n\n\tb2ValidateSolverSets( world );\n\tb2ValidateContacts( world );\n\n\tb2TracyCZoneEnd( contact_state );\n\tb2TracyCZoneEnd( collide );\n}\n\nvoid b2World_Step( b2WorldId worldId, float timeStep, int subStepCount )\n{\n\tB2_ASSERT( b2IsValidFloat( timeStep ) );\n\tB2_ASSERT( 0 < subStepCount );\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\tb2TracyCFrame;\n\t\treturn;\n\t}\n\n\t// Prepare to capture events\n\t// Ensure user does not access stale data if there is an early return\n\tb2BodyMoveEventArray_Clear( &world->bodyMoveEvents );\n\tb2SensorBeginTouchEventArray_Clear( &world->sensorBeginEvents );\n\tb2ContactBeginTouchEventArray_Clear( &world->contactBeginEvents );\n\tb2ContactHitEventArray_Clear( &world->contactHitEvents );\n\tb2JointEventArray_Clear( &world->jointEvents );\n\n\tworld->profile = (b2Profile){ 0 };\n\n\tif ( timeStep == 0.0f )\n\t{\n\t\t// Swap end event array buffers\n\t\tworld->endEventArrayIndex = 1 - world->endEventArrayIndex;\n\t\tb2SensorEndTouchEventArray_Clear( world->sensorEndEvents + world->endEventArrayIndex );\n\t\tb2ContactEndTouchEventArray_Clear( world->contactEndEvents + world->endEventArrayIndex );\n\n\t\t// todo_erin would be useful to still process collision while paused\n\t\tb2TracyCFrame;\n\t\treturn;\n\t}\n\n\tb2TracyCZoneNC( world_step, \"Step\", b2_colorBox2DGreen, true );\n\n\tworld->locked = true;\n\tworld->activeTaskCount = 0;\n\tworld->taskCount = 0;\n\n\tuint64_t stepTicks = b2GetTicks();\n\n\t// Update collision pairs and create contacts\n\t{\n\t\tuint64_t pairTicks = b2GetTicks();\n\t\tb2UpdateBroadPhasePairs( world );\n\t\tworld->profile.pairs = b2GetMilliseconds( pairTicks );\n\t}\n\n\tb2StepContext context = { 0 };\n\tcontext.world = world;\n\tcontext.dt = timeStep;\n\tcontext.subStepCount = b2MaxInt( 1, subStepCount );\n\n\tif ( timeStep > 0.0f )\n\t{\n\t\tcontext.inv_dt = 1.0f / timeStep;\n\t\tcontext.h = timeStep / context.subStepCount;\n\t\tcontext.inv_h = context.subStepCount * context.inv_dt;\n\t}\n\telse\n\t{\n\t\tcontext.inv_dt = 0.0f;\n\t\tcontext.h = 0.0f;\n\t\tcontext.inv_h = 0.0f;\n\t}\n\n\tworld->inv_h = context.inv_h;\n\tworld->inv_dt = context.inv_dt;\n\n\t// Hertz values get reduced for large time steps\n\tfloat contactHertz = b2MinFloat( world->contactHertz, 0.125f * context.inv_h );\n\tcontext.contactSoftness = b2MakeSoft( contactHertz, world->contactDampingRatio, context.h );\n\tcontext.staticSoftness = b2MakeSoft( 2.0f * contactHertz, world->contactDampingRatio, context.h );\n\n\tcontext.restitutionThreshold = world->restitutionThreshold;\n\tcontext.maxLinearVelocity = world->maxLinearSpeed;\n\tcontext.enableWarmStarting = world->enableWarmStarting;\n\n\t// Update contacts\n\t{\n\t\tuint64_t collideTicks = b2GetTicks();\n\t\tb2Collide( &context );\n\t\tworld->profile.collide = b2GetMilliseconds( collideTicks );\n\t}\n\n\t// Integrate velocities, solve velocity constraints, and integrate positions.\n\tif ( context.dt > 0.0f )\n\t{\n\t\tuint64_t solveTicks = b2GetTicks();\n\t\tb2Solve( world, &context );\n\t\tworld->profile.solve = b2GetMilliseconds( solveTicks );\n\t}\n\n\t// Update sensors\n\t{\n\t\tuint64_t sensorTicks = b2GetTicks();\n\t\tb2OverlapSensors( world );\n\t\tworld->profile.sensors = b2GetMilliseconds( sensorTicks );\n\t}\n\n\tworld->profile.step = b2GetMilliseconds( stepTicks );\n\n\tB2_ASSERT( b2GetArenaAllocation( &world->arena ) == 0 );\n\n\t// Ensure stack is large enough\n\tb2GrowArena( &world->arena );\n\n\t// Make sure all tasks that were started were also finished\n\tB2_ASSERT( world->activeTaskCount == 0 );\n\n\tb2TracyCZoneEnd( world_step );\n\n\t// Swap end event array buffers\n\tworld->endEventArrayIndex = 1 - world->endEventArrayIndex;\n\tb2SensorEndTouchEventArray_Clear( world->sensorEndEvents + world->endEventArrayIndex );\n\tb2ContactEndTouchEventArray_Clear( world->contactEndEvents + world->endEventArrayIndex );\n\tworld->locked = false;\n\tb2TracyCFrame;\n}\n\nstatic void b2DrawShape( b2DebugDraw* draw, b2Shape* shape, b2Transform xf, b2HexColor color )\n{\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t{\n\t\t\tb2Capsule* capsule = &shape->capsule;\n\t\t\tb2Vec2 p1 = b2TransformPoint( xf, capsule->center1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( xf, capsule->center2 );\n\t\t\tdraw->DrawSolidCapsuleFcn( p1, p2, capsule->radius, color, draw->context );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_circleShape:\n\t\t{\n\t\t\tb2Circle* circle = &shape->circle;\n\t\t\txf.p = b2TransformPoint( xf, circle->center );\n\t\t\tdraw->DrawSolidCircleFcn( xf, circle->radius, color, draw->context );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_polygonShape:\n\t\t{\n\t\t\tb2Polygon* poly = &shape->polygon;\n\t\t\tdraw->DrawSolidPolygonFcn( xf, poly->vertices, poly->count, poly->radius, color, draw->context );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_segmentShape:\n\t\t{\n\t\t\tb2Segment* segment = &shape->segment;\n\t\t\tb2Vec2 p1 = b2TransformPoint( xf, segment->point1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( xf, segment->point2 );\n\t\t\tdraw->DrawLineFcn( p1, p2, color, draw->context );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_chainSegmentShape:\n\t\t{\n\t\t\tb2Segment* segment = &shape->chainSegment.segment;\n\t\t\tb2Vec2 p1 = b2TransformPoint( xf, segment->point1 );\n\t\t\tb2Vec2 p2 = b2TransformPoint( xf, segment->point2 );\n\t\t\tdraw->DrawLineFcn( p1, p2, color, draw->context );\n\t\t\tdraw->DrawPointFcn( p2, 4.0f, color, draw->context );\n\t\t\tdraw->DrawLineFcn( p1, b2Lerp( p1, p2, 0.1f ), b2_colorPaleGreen, draw->context );\n\t\t}\n\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n}\n\nstruct DrawContext\n{\n\tb2World* world;\n\tb2DebugDraw* draw;\n};\n\nstatic bool DrawQueryCallback( int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\n\tstruct DrawContext* drawContext = context;\n\tb2World* world = drawContext->world;\n\tb2DebugDraw* draw = drawContext->draw;\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\tB2_ASSERT( shape->id == shapeId );\n\n\tb2SetBit( &world->debugBodySet, shape->bodyId );\n\n\tif ( draw->drawShapes )\n\t{\n\t\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\t\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\n\t\tb2HexColor color;\n\n\t\tif ( shape->material.customColor != 0 )\n\t\t{\n\t\t\tcolor = shape->material.customColor;\n\t\t}\n\t\telse if ( body->type == b2_dynamicBody && body->mass == 0.0f )\n\t\t{\n\t\t\t// Bad body\n\t\t\tcolor = b2_colorRed;\n\t\t}\n\t\telse if ( body->setIndex == b2_disabledSet )\n\t\t{\n\t\t\tcolor = b2_colorSlateGray;\n\t\t}\n\t\telse if ( shape->sensorIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\tcolor = b2_colorWheat;\n\t\t}\n\t\telse if ( body->flags & b2_hadTimeOfImpact )\n\t\t{\n\t\t\tcolor = b2_colorLime;\n\t\t}\n\t\telse if ( ( bodySim->flags & b2_isBullet ) && body->setIndex == b2_awakeSet )\n\t\t{\n\t\t\tcolor = b2_colorTurquoise;\n\t\t}\n\t\telse if ( body->flags & b2_isSpeedCapped )\n\t\t{\n\t\t\tcolor = b2_colorYellow;\n\t\t}\n\t\telse if ( bodySim->flags & b2_isFast )\n\t\t{\n\t\t\tcolor = b2_colorSalmon;\n\t\t}\n\t\telse if ( body->type == b2_staticBody )\n\t\t{\n\t\t\tcolor = b2_colorPaleGreen;\n\t\t}\n\t\telse if ( body->type == b2_kinematicBody )\n\t\t{\n\t\t\tcolor = b2_colorRoyalBlue;\n\t\t}\n\t\telse if ( body->setIndex == b2_awakeSet )\n\t\t{\n\t\t\tcolor = b2_colorPink;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcolor = b2_colorGray;\n\t\t}\n\n\t\tb2DrawShape( draw, shape, bodySim->transform, color );\n\t}\n\n\tif ( draw->drawBounds )\n\t{\n\t\tb2AABB aabb = shape->fatAABB;\n\n\t\tb2Vec2 vs[4] = { { aabb.lowerBound.x, aabb.lowerBound.y },\n\t\t\t\t\t\t { aabb.upperBound.x, aabb.lowerBound.y },\n\t\t\t\t\t\t { aabb.upperBound.x, aabb.upperBound.y },\n\t\t\t\t\t\t { aabb.lowerBound.x, aabb.upperBound.y } };\n\n\t\tdraw->DrawPolygonFcn( vs, 4, b2_colorGold, draw->context );\n\t}\n\n\treturn true;\n}\n\n// todo this has varying order for moving shapes, causing flicker when overlapping shapes are moving\n// solution: display order by shape id modulus 3, keep 3 buckets in GLSolid* and flush in 3 passes.\nvoid b2World_Draw( b2WorldId worldId, b2DebugDraw* draw )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tB2_ASSERT( b2IsValidAABB( draw->drawingBounds ) );\n\n\tconst float k_axisScale = 0.3f;\n\tb2HexColor speculativeColor = b2_colorGainsboro;\n\tb2HexColor addColor = b2_colorGreen;\n\tb2HexColor persistColor = b2_colorBlue;\n\tb2HexColor normalColor = b2_colorDimGray;\n\tb2HexColor impulseColor = b2_colorMagenta;\n\tb2HexColor frictionColor = b2_colorYellow;\n\n\tint bodyCapacity = b2GetIdCapacity( &world->bodyIdPool );\n\tb2SetBitCountAndClear( &world->debugBodySet, bodyCapacity );\n\n\tint jointCapacity = b2GetIdCapacity( &world->jointIdPool );\n\tb2SetBitCountAndClear( &world->debugJointSet, jointCapacity );\n\n\tint contactCapacity = b2GetIdCapacity( &world->contactIdPool );\n\tb2SetBitCountAndClear( &world->debugContactSet, contactCapacity );\n\n\tint islandCapacity = b2GetIdCapacity( &world->islandIdPool );\n\tb2SetBitCountAndClear( &world->debugIslandSet, islandCapacity );\n\n\tstruct DrawContext drawContext = { world, draw };\n\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tb2DynamicTree_QueryAll( world->broadPhase.trees + i, draw->drawingBounds, DrawQueryCallback, &drawContext );\n\t}\n\n\tuint32_t wordCount = world->debugBodySet.blockCount;\n\tuint64_t* bits = world->debugBodySet.bits;\n\tfor ( uint32_t k = 0; k < wordCount; ++k )\n\t{\n\t\tuint64_t word = bits[k];\n\t\twhile ( word != 0 )\n\t\t{\n\t\t\tuint32_t ctz = b2CTZ64( word );\n\t\t\tuint32_t bodyId = 64 * k + ctz;\n\n\t\t\tb2Body* body = b2BodyArray_Get( &world->bodies, bodyId );\n\n\t\t\tif ( draw->drawBodyNames && body->name[0] != 0 )\n\t\t\t{\n\t\t\t\tb2Vec2 offset = { 0.1f, 0.1f };\n\t\t\t\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\n\t\t\t\tb2Transform transform = { bodySim->center, bodySim->transform.q };\n\t\t\t\tb2Vec2 p = b2TransformPoint( transform, offset );\n\t\t\t\tdraw->DrawStringFcn( p, body->name, b2_colorBlueViolet, draw->context );\n\t\t\t}\n\n\t\t\tif ( draw->drawMass && body->type == b2_dynamicBody )\n\t\t\t{\n\t\t\t\tb2Vec2 offset = { 0.1f, 0.1f };\n\t\t\t\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\n\t\t\t\tb2Transform transform = { bodySim->center, bodySim->transform.q };\n\t\t\t\tdraw->DrawLineFcn( bodySim->center0, bodySim->center, b2_colorWhiteSmoke, draw->context );\n\t\t\t\tdraw->DrawTransformFcn( transform, draw->context );\n\n\t\t\t\tb2Vec2 p = b2TransformPoint( transform, offset );\n\t\t\t\tchar buffer[32];\n\t\t\t\tsnprintf( buffer, 32, \"  %.2f\", body->mass );\n\t\t\t\tdraw->DrawStringFcn( p, buffer, b2_colorWhite, draw->context );\n\t\t\t}\n\n\t\t\tif ( draw->drawJoints )\n\t\t\t{\n\t\t\t\tint jointKey = body->headJointKey;\n\t\t\t\twhile ( jointKey != B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\tint jointId = jointKey >> 1;\n\t\t\t\t\tint edgeIndex = jointKey & 1;\n\t\t\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\n\t\t\t\t\t// avoid double draw\n\t\t\t\t\tif ( b2GetBit( &world->debugJointSet, jointId ) == false )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2DrawJoint( draw, world, joint );\n\t\t\t\t\t\tb2SetBit( &world->debugJointSet, jointId );\n\t\t\t\t\t}\n\n\t\t\t\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst float linearSlop = B2_LINEAR_SLOP;\n\t\t\tif ( draw->drawContactPoints && body->type == b2_dynamicBody )\n\t\t\t{\n\t\t\t\tint contactKey = body->headContactKey;\n\t\t\t\twhile ( contactKey != B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\tint contactId = contactKey >> 1;\n\t\t\t\t\tint edgeIndex = contactKey & 1;\n\t\t\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\t\t\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\n\t\t\t\t\t// avoid double draw\n\t\t\t\t\tif ( b2GetBit( &world->debugContactSet, contactId ) == false )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2ContactSim* contactSim = b2GetContactSim( world, contact );\n\n\t\t\t\t\t\tint pointCount = contactSim->manifold.pointCount;\n\t\t\t\t\t\tb2Vec2 normal = contactSim->manifold.normal;\n\t\t\t\t\t\tchar buffer[32];\n\n\t\t\t\t\t\tfor ( int j = 0; j < pointCount; ++j )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb2ManifoldPoint* point = contactSim->manifold.points + j;\n\n\t\t\t\t\t\t\tif ( draw->drawGraphColors && contact->colorIndex != B2_NULL_INDEX )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// graph color\n\t\t\t\t\t\t\t\tfloat pointSize = contact->colorIndex == B2_OVERFLOW_INDEX ? 7.5f : 5.0f;\n\t\t\t\t\t\t\t\tdraw->DrawPointFcn( point->point, pointSize, b2_graphColors[contact->colorIndex], draw->context );\n\t\t\t\t\t\t\t\t// m_context->draw.DrawString(point->position, \"%d\", point->color);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if ( point->separation > linearSlop )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// Speculative\n\t\t\t\t\t\t\t\tdraw->DrawPointFcn( point->point, 5.0f, speculativeColor, draw->context );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if ( point->persisted == false )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// Add\n\t\t\t\t\t\t\t\tdraw->DrawPointFcn( point->point, 10.0f, addColor, draw->context );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if ( point->persisted == true )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// Persist\n\t\t\t\t\t\t\t\tdraw->DrawPointFcn( point->point, 5.0f, persistColor, draw->context );\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( draw->drawContactNormals )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tb2Vec2 p1 = point->point;\n\t\t\t\t\t\t\t\tb2Vec2 p2 = b2MulAdd( p1, k_axisScale, normal );\n\t\t\t\t\t\t\t\tdraw->DrawLineFcn( p1, p2, normalColor, draw->context );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if ( draw->drawContactForces )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// multiply by one-half due to relax iteration\n\t\t\t\t\t\t\t\tfloat force = 0.5f * point->totalNormalImpulse * world->inv_dt;\n\t\t\t\t\t\t\t\tb2Vec2 p1 = point->point;\n\t\t\t\t\t\t\t\tb2Vec2 p2 = b2MulAdd( p1, draw->forceScale * force, normal );\n\t\t\t\t\t\t\t\tdraw->DrawLineFcn( p1, p2, impulseColor, draw->context );\n\t\t\t\t\t\t\t\tsnprintf( buffer, B2_ARRAY_COUNT( buffer ), \"%.1f\", force );\n\t\t\t\t\t\t\t\tdraw->DrawStringFcn( p1, buffer, b2_colorWhite, draw->context );\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( draw->drawContactFeatures )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsnprintf( buffer, B2_ARRAY_COUNT( buffer ), \"%d\", point->id );\n\t\t\t\t\t\t\t\tdraw->DrawStringFcn( point->point, buffer, b2_colorOrange, draw->context );\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( draw->drawFrictionForces )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfloat force = 0.5f * point->tangentImpulse * world->inv_h;\n\t\t\t\t\t\t\t\tb2Vec2 tangent = b2RightPerp( normal );\n\t\t\t\t\t\t\t\tb2Vec2 p1 = point->point;\n\t\t\t\t\t\t\t\tb2Vec2 p2 = b2MulAdd( p1, draw->forceScale * force, tangent );\n\t\t\t\t\t\t\t\tdraw->DrawLineFcn( p1, p2, frictionColor, draw->context );\n\t\t\t\t\t\t\t\tsnprintf( buffer, B2_ARRAY_COUNT( buffer ), \"%.1f\", force );\n\t\t\t\t\t\t\t\tdraw->DrawStringFcn( p1, buffer, b2_colorWhite, draw->context );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tb2SetBit( &world->debugContactSet, contactId );\n\t\t\t\t\t}\n\n\t\t\t\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( draw->drawIslands )\n\t\t\t{\n\t\t\t\tint islandId = body->islandId;\n\t\t\t\tif ( islandId != B2_NULL_INDEX && b2GetBit( &world->debugIslandSet, islandId ) == false )\n\t\t\t\t{\n\t\t\t\t\tb2Island* island = world->islands.data + islandId;\n\t\t\t\t\tif ( island->setIndex == B2_NULL_INDEX )\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tint shapeCount = 0;\n\t\t\t\t\tb2AABB aabb = {\n\t\t\t\t\t\t.lowerBound = { FLT_MAX, FLT_MAX },\n\t\t\t\t\t\t.upperBound = { -FLT_MAX, -FLT_MAX },\n\t\t\t\t\t};\n\n\t\t\t\t\tint islandBodyId = island->headBody;\n\t\t\t\t\twhile ( islandBodyId != B2_NULL_INDEX )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Body* islandBody = b2BodyArray_Get( &world->bodies, islandBodyId );\n\t\t\t\t\t\tint shapeId = islandBody->headShapeId;\n\t\t\t\t\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\t\t\t\t\t\taabb = b2AABB_Union( aabb, shape->fatAABB );\n\t\t\t\t\t\t\tshapeCount += 1;\n\t\t\t\t\t\t\tshapeId = shape->nextShapeId;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tislandBodyId = islandBody->islandNext;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( shapeCount > 0 )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Vec2 vs[4] = { { aabb.lowerBound.x, aabb.lowerBound.y },\n\t\t\t\t\t\t\t\t\t\t { aabb.upperBound.x, aabb.lowerBound.y },\n\t\t\t\t\t\t\t\t\t\t { aabb.upperBound.x, aabb.upperBound.y },\n\t\t\t\t\t\t\t\t\t\t { aabb.lowerBound.x, aabb.upperBound.y } };\n\n\t\t\t\t\t\tdraw->DrawPolygonFcn( vs, 4, b2_colorOrangeRed, draw->context );\n\t\t\t\t\t}\n\n\t\t\t\t\tb2SetBit( &world->debugIslandSet, islandId );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear the smallest set bit\n\t\t\tword = word & ( word - 1 );\n\t\t}\n\t}\n}\n\nb2BodyEvents b2World_GetBodyEvents( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn (b2BodyEvents){ 0 };\n\t}\n\n\tint count = world->bodyMoveEvents.count;\n\tb2BodyEvents events = { world->bodyMoveEvents.data, count };\n\treturn events;\n}\n\nb2SensorEvents b2World_GetSensorEvents( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn (b2SensorEvents){ 0 };\n\t}\n\n\t// Careful to use previous buffer\n\tint endEventArrayIndex = 1 - world->endEventArrayIndex;\n\n\tint beginCount = world->sensorBeginEvents.count;\n\tint endCount = world->sensorEndEvents[endEventArrayIndex].count;\n\n\tb2SensorEvents events = {\n\t\t.beginEvents = world->sensorBeginEvents.data,\n\t\t.endEvents = world->sensorEndEvents[endEventArrayIndex].data,\n\t\t.beginCount = beginCount,\n\t\t.endCount = endCount,\n\t};\n\treturn events;\n}\n\nb2ContactEvents b2World_GetContactEvents( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn (b2ContactEvents){ 0 };\n\t}\n\n\t// Careful to use previous buffer\n\tint endEventArrayIndex = 1 - world->endEventArrayIndex;\n\n\tint beginCount = world->contactBeginEvents.count;\n\tint endCount = world->contactEndEvents[endEventArrayIndex].count;\n\tint hitCount = world->contactHitEvents.count;\n\n\tb2ContactEvents events = {\n\t\t.beginEvents = world->contactBeginEvents.data,\n\t\t.endEvents = world->contactEndEvents[endEventArrayIndex].data,\n\t\t.hitEvents = world->contactHitEvents.data,\n\t\t.beginCount = beginCount,\n\t\t.endCount = endCount,\n\t\t.hitCount = hitCount,\n\t};\n\n\treturn events;\n}\n\nb2JointEvents b2World_GetJointEvents( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn (b2JointEvents){ 0 };\n\t}\n\n\tint count = world->jointEvents.count;\n\tb2JointEvents events = { world->jointEvents.data, count };\n\treturn events;\n}\n\nbool b2World_IsValid( b2WorldId id )\n{\n\tif ( id.index1 < 1 || B2_MAX_WORLDS < id.index1 )\n\t{\n\t\treturn false;\n\t}\n\n\tb2World* world = b2_worlds + ( id.index1 - 1 );\n\n\tif ( world->worldId != id.index1 - 1 )\n\t{\n\t\t// world is not allocated\n\t\treturn false;\n\t}\n\n\treturn id.generation == world->generation;\n}\n\nbool b2Body_IsValid( b2BodyId id )\n{\n\tif ( B2_MAX_WORLDS <= id.world0 )\n\t{\n\t\t// invalid world\n\t\treturn false;\n\t}\n\n\tb2World* world = b2_worlds + id.world0;\n\tif ( world->worldId != id.world0 )\n\t{\n\t\t// world is free\n\t\treturn false;\n\t}\n\n\tif ( id.index1 < 1 || world->bodies.count < id.index1 )\n\t{\n\t\t// invalid index\n\t\treturn false;\n\t}\n\n\tb2Body* body = world->bodies.data + ( id.index1 - 1 );\n\tif ( body->setIndex == B2_NULL_INDEX )\n\t{\n\t\t// this was freed\n\t\treturn false;\n\t}\n\n\tB2_ASSERT( body->localIndex != B2_NULL_INDEX );\n\n\tif ( body->generation != id.generation )\n\t{\n\t\t// this id is orphaned\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool b2Shape_IsValid( b2ShapeId id )\n{\n\tif ( B2_MAX_WORLDS <= id.world0 )\n\t{\n\t\treturn false;\n\t}\n\n\tb2World* world = b2_worlds + id.world0;\n\tif ( world->worldId != id.world0 )\n\t{\n\t\t// world is free\n\t\treturn false;\n\t}\n\n\tint shapeId = id.index1 - 1;\n\tif ( shapeId < 0 || world->shapes.count <= shapeId )\n\t{\n\t\treturn false;\n\t}\n\n\tb2Shape* shape = world->shapes.data + shapeId;\n\tif ( shape->id == B2_NULL_INDEX )\n\t{\n\t\t// shape is free\n\t\treturn false;\n\t}\n\n\tB2_ASSERT( shape->id == shapeId );\n\n\treturn id.generation == shape->generation;\n}\n\nbool b2Chain_IsValid( b2ChainId id )\n{\n\tif ( B2_MAX_WORLDS <= id.world0 )\n\t{\n\t\treturn false;\n\t}\n\n\tb2World* world = b2_worlds + id.world0;\n\tif ( world->worldId != id.world0 )\n\t{\n\t\t// world is free\n\t\treturn false;\n\t}\n\n\tint chainId = id.index1 - 1;\n\tif ( chainId < 0 || world->chainShapes.count <= chainId )\n\t{\n\t\treturn false;\n\t}\n\n\tb2ChainShape* chain = world->chainShapes.data + chainId;\n\tif ( chain->id == B2_NULL_INDEX )\n\t{\n\t\t// chain is free\n\t\treturn false;\n\t}\n\n\tB2_ASSERT( chain->id == chainId );\n\n\treturn id.generation == chain->generation;\n}\n\nbool b2Joint_IsValid( b2JointId id )\n{\n\tif ( B2_MAX_WORLDS <= id.world0 )\n\t{\n\t\treturn false;\n\t}\n\n\tb2World* world = b2_worlds + id.world0;\n\tif ( world->worldId != id.world0 )\n\t{\n\t\t// world is free\n\t\treturn false;\n\t}\n\n\tint jointId = id.index1 - 1;\n\tif ( jointId < 0 || world->joints.count <= jointId )\n\t{\n\t\treturn false;\n\t}\n\n\tb2Joint* joint = world->joints.data + jointId;\n\tif ( joint->jointId == B2_NULL_INDEX )\n\t{\n\t\t// joint is free\n\t\treturn false;\n\t}\n\n\tB2_ASSERT( joint->jointId == jointId );\n\n\treturn id.generation == joint->generation;\n}\n\nbool b2Contact_IsValid( b2ContactId id )\n{\n\tif ( B2_MAX_WORLDS <= id.world0 )\n\t{\n\t\treturn false;\n\t}\n\n\tb2World* world = b2_worlds + id.world0;\n\tif ( world->worldId != id.world0 )\n\t{\n\t\t// world is free\n\t\treturn false;\n\t}\n\n\tint contactId = id.index1 - 1;\n\tif ( contactId < 0 || world->contacts.count <= contactId )\n\t{\n\t\treturn false;\n\t}\n\n\tb2Contact* contact = world->contacts.data + contactId;\n\tif ( contact->contactId == B2_NULL_INDEX )\n\t{\n\t\t// contact is free\n\t\treturn false;\n\t}\n\n\tB2_ASSERT( contact->contactId == contactId );\n\n\treturn id.generation == contact->generation;\n}\n\nvoid b2World_EnableSleeping( b2WorldId worldId, bool flag )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tif ( flag == world->enableSleep )\n\t{\n\t\treturn;\n\t}\n\n\tworld->enableSleep = flag;\n\n\tif ( flag == false )\n\t{\n\t\tint setCount = world->solverSets.count;\n\t\tfor ( int i = b2_firstSleepingSet; i < setCount; ++i )\n\t\t{\n\t\t\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, i );\n\t\t\tif ( set->bodySims.count > 0 )\n\t\t\t{\n\t\t\t\tb2WakeSolverSet( world, i );\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool b2World_IsSleepingEnabled( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\treturn world->enableSleep;\n}\n\nvoid b2World_EnableWarmStarting( b2WorldId worldId, bool flag )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tworld->enableWarmStarting = flag;\n}\n\nbool b2World_IsWarmStartingEnabled( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\treturn world->enableWarmStarting;\n}\n\nint b2World_GetAwakeBodyCount( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\treturn awakeSet->bodySims.count;\n}\n\nvoid b2World_EnableContinuous( b2WorldId worldId, bool flag )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tworld->enableContinuous = flag;\n}\n\nbool b2World_IsContinuousEnabled( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\treturn world->enableContinuous;\n}\n\nvoid b2World_SetRestitutionThreshold( b2WorldId worldId, float value )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tworld->restitutionThreshold = b2ClampFloat( value, 0.0f, FLT_MAX );\n}\n\nfloat b2World_GetRestitutionThreshold( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\treturn world->restitutionThreshold;\n}\n\nvoid b2World_SetHitEventThreshold( b2WorldId worldId, float value )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tworld->hitEventThreshold = b2ClampFloat( value, 0.0f, FLT_MAX );\n}\n\nfloat b2World_GetHitEventThreshold( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\treturn world->hitEventThreshold;\n}\n\nvoid b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushSpeed )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tworld->contactHertz = b2ClampFloat( hertz, 0.0f, FLT_MAX );\n\tworld->contactDampingRatio = b2ClampFloat( dampingRatio, 0.0f, FLT_MAX );\n\tworld->contactSpeed = b2ClampFloat( pushSpeed, 0.0f, FLT_MAX );\n}\n\nvoid b2World_SetMaximumLinearSpeed( b2WorldId worldId, float maximumLinearSpeed )\n{\n\tB2_ASSERT( b2IsValidFloat( maximumLinearSpeed ) && maximumLinearSpeed > 0.0f );\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tworld->maxLinearSpeed = maximumLinearSpeed;\n}\n\nfloat b2World_GetMaximumLinearSpeed( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\treturn world->maxLinearSpeed;\n}\n\nb2Profile b2World_GetProfile( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\treturn world->profile;\n}\n\nb2Counters b2World_GetCounters( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tb2Counters s = { 0 };\n\ts.bodyCount = b2GetIdCount( &world->bodyIdPool );\n\ts.shapeCount = b2GetIdCount( &world->shapeIdPool );\n\ts.contactCount = b2GetIdCount( &world->contactIdPool );\n\ts.jointCount = b2GetIdCount( &world->jointIdPool );\n\ts.islandCount = b2GetIdCount( &world->islandIdPool );\n\n\tb2DynamicTree* staticTree = world->broadPhase.trees + b2_staticBody;\n\ts.staticTreeHeight = b2DynamicTree_GetHeight( staticTree );\n\n\tb2DynamicTree* dynamicTree = world->broadPhase.trees + b2_dynamicBody;\n\tb2DynamicTree* kinematicTree = world->broadPhase.trees + b2_kinematicBody;\n\ts.treeHeight = b2MaxInt( b2DynamicTree_GetHeight( dynamicTree ), b2DynamicTree_GetHeight( kinematicTree ) );\n\n\ts.stackUsed = b2GetMaxArenaAllocation( &world->arena );\n\ts.byteCount = b2GetByteCount();\n\ts.taskCount = world->taskCount;\n\n\tfor ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )\n\t{\n\t\ts.colorCounts[i] = world->constraintGraph.colors[i].contactSims.count + world->constraintGraph.colors[i].jointSims.count;\n\t}\n\treturn s;\n}\n\nvoid b2World_SetUserData( b2WorldId worldId, void* userData )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tworld->userData = userData;\n}\n\nvoid* b2World_GetUserData( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\treturn world->userData;\n}\n\nvoid b2World_SetFrictionCallback( b2WorldId worldId, b2FrictionCallback* callback )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tif ( callback != NULL )\n\t{\n\t\tworld->frictionCallback = callback;\n\t}\n\telse\n\t{\n\t\tworld->frictionCallback = b2DefaultFrictionCallback;\n\t}\n}\n\nvoid b2World_SetRestitutionCallback( b2WorldId worldId, b2RestitutionCallback* callback )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tif ( callback != NULL )\n\t{\n\t\tworld->restitutionCallback = callback;\n\t}\n\telse\n\t{\n\t\tworld->restitutionCallback = b2DefaultRestitutionCallback;\n\t}\n}\n\nvoid b2World_DumpMemoryStats( b2WorldId worldId )\n{\n\tFILE* file = fopen( \"box2d_memory.txt\", \"w\" );\n\tif ( file == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\n\t// id pools\n\tfprintf( file, \"id pools\\n\" );\n\tfprintf( file, \"body ids: %d\\n\", b2GetIdBytes( &world->bodyIdPool ) );\n\tfprintf( file, \"solver set ids: %d\\n\", b2GetIdBytes( &world->solverSetIdPool ) );\n\tfprintf( file, \"joint ids: %d\\n\", b2GetIdBytes( &world->jointIdPool ) );\n\tfprintf( file, \"contact ids: %d\\n\", b2GetIdBytes( &world->contactIdPool ) );\n\tfprintf( file, \"island ids: %d\\n\", b2GetIdBytes( &world->islandIdPool ) );\n\tfprintf( file, \"shape ids: %d\\n\", b2GetIdBytes( &world->shapeIdPool ) );\n\tfprintf( file, \"chain ids: %d\\n\", b2GetIdBytes( &world->chainIdPool ) );\n\tfprintf( file, \"\\n\" );\n\n\t// world arrays\n\tfprintf( file, \"world arrays\\n\" );\n\tfprintf( file, \"bodies: %d\\n\", b2BodyArray_ByteCount( &world->bodies ) );\n\tfprintf( file, \"solver sets: %d\\n\", b2SolverSetArray_ByteCount( &world->solverSets ) );\n\tfprintf( file, \"joints: %d\\n\", b2JointArray_ByteCount( &world->joints ) );\n\tfprintf( file, \"contacts: %d\\n\", b2ContactArray_ByteCount( &world->contacts ) );\n\tfprintf( file, \"islands: %d\\n\", b2IslandArray_ByteCount( &world->islands ) );\n\tfprintf( file, \"shapes: %d\\n\", b2ShapeArray_ByteCount( &world->shapes ) );\n\tfprintf( file, \"chains: %d\\n\", b2ChainShapeArray_ByteCount( &world->chainShapes ) );\n\tfprintf( file, \"\\n\" );\n\n\t// broad-phase\n\tfprintf( file, \"broad-phase\\n\" );\n\tfprintf( file, \"static tree: %d\\n\", b2DynamicTree_GetByteCount( world->broadPhase.trees + b2_staticBody ) );\n\tfprintf( file, \"kinematic tree: %d\\n\", b2DynamicTree_GetByteCount( world->broadPhase.trees + b2_kinematicBody ) );\n\tfprintf( file, \"dynamic tree: %d\\n\", b2DynamicTree_GetByteCount( world->broadPhase.trees + b2_dynamicBody ) );\n\tb2HashSet* moveSet = &world->broadPhase.moveSet;\n\tfprintf( file, \"moveSet: %d (%u, %u)\\n\", b2GetHashSetBytes( moveSet ), moveSet->count, moveSet->capacity );\n\tfprintf( file, \"moveArray: %d\\n\", b2IntArray_ByteCount( &world->broadPhase.moveArray ) );\n\tb2HashSet* pairSet = &world->broadPhase.pairSet;\n\tfprintf( file, \"pairSet: %d (%u, %u)\\n\", b2GetHashSetBytes( pairSet ), pairSet->count, pairSet->capacity );\n\tfprintf( file, \"\\n\" );\n\n\t// solver sets\n\tint bodySimCapacity = 0;\n\tint bodyStateCapacity = 0;\n\tint jointSimCapacity = 0;\n\tint contactSimCapacity = 0;\n\tint islandSimCapacity = 0;\n\tint solverSetCapacity = world->solverSets.count;\n\tfor ( int i = 0; i < solverSetCapacity; ++i )\n\t{\n\t\tb2SolverSet* set = world->solverSets.data + i;\n\t\tif ( set->setIndex == B2_NULL_INDEX )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tbodySimCapacity += set->bodySims.capacity;\n\t\tbodyStateCapacity += set->bodyStates.capacity;\n\t\tjointSimCapacity += set->jointSims.capacity;\n\t\tcontactSimCapacity += set->contactSims.capacity;\n\t\tislandSimCapacity += set->islandSims.capacity;\n\t}\n\n\tfprintf( file, \"solver sets\\n\" );\n\tfprintf( file, \"body sim: %d\\n\", bodySimCapacity * (int)sizeof( b2BodySim ) );\n\tfprintf( file, \"body state: %d\\n\", bodyStateCapacity * (int)sizeof( b2BodyState ) );\n\tfprintf( file, \"joint sim: %d\\n\", jointSimCapacity * (int)sizeof( b2JointSim ) );\n\tfprintf( file, \"contact sim: %d\\n\", contactSimCapacity * (int)sizeof( b2ContactSim ) );\n\tfprintf( file, \"island sim: %d\\n\", islandSimCapacity * (int)sizeof( islandSimCapacity ) );\n\tfprintf( file, \"\\n\" );\n\n\t// constraint graph\n\tint bodyBitSetBytes = 0;\n\tcontactSimCapacity = 0;\n\tjointSimCapacity = 0;\n\tfor ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )\n\t{\n\t\tb2GraphColor* c = world->constraintGraph.colors + i;\n\t\tbodyBitSetBytes += b2GetBitSetBytes( &c->bodySet );\n\t\tcontactSimCapacity += c->contactSims.capacity;\n\t\tjointSimCapacity += c->jointSims.capacity;\n\t}\n\n\tfprintf( file, \"constraint graph\\n\" );\n\tfprintf( file, \"body bit sets: %d\\n\", bodyBitSetBytes );\n\tfprintf( file, \"joint sim: %d\\n\", jointSimCapacity * (int)sizeof( b2JointSim ) );\n\tfprintf( file, \"contact sim: %d\\n\", contactSimCapacity * (int)sizeof( b2ContactSim ) );\n\tfprintf( file, \"\\n\" );\n\n\t// stack allocator\n\tfprintf( file, \"stack allocator: %d\\n\\n\", world->arena.capacity );\n\n\t// chain shapes\n\t// todo\n\n\tfclose( file );\n}\n\ntypedef struct WorldQueryContext\n{\n\tb2World* world;\n\tb2OverlapResultFcn* fcn;\n\tb2QueryFilter filter;\n\tvoid* userContext;\n} WorldQueryContext;\n\nstatic bool TreeQueryCallback( int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\n\tWorldQueryContext* worldContext = context;\n\tb2World* world = worldContext->world;\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\tif ( b2ShouldQueryCollide( shape->filter, worldContext->filter ) == false )\n\t{\n\t\treturn true;\n\t}\n\n\tb2ShapeId id = { shapeId + 1, world->worldId, shape->generation };\n\tbool result = worldContext->fcn( id, worldContext->userContext );\n\treturn result;\n}\n\nb2TreeStats b2World_OverlapAABB( b2WorldId worldId, b2AABB aabb, b2QueryFilter filter, b2OverlapResultFcn* fcn, void* context )\n{\n\tb2TreeStats treeStats = { 0 };\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn treeStats;\n\t}\n\n\tB2_ASSERT( b2IsValidAABB( aabb ) );\n\n\tWorldQueryContext worldContext = { world, fcn, filter, context };\n\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tb2TreeStats treeResult =\n\t\t\tb2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeQueryCallback, &worldContext );\n\n\t\ttreeStats.nodeVisits += treeResult.nodeVisits;\n\t\ttreeStats.leafVisits += treeResult.leafVisits;\n\t}\n\n\treturn treeStats;\n}\n\ntypedef struct WorldOverlapContext\n{\n\tb2World* world;\n\tb2OverlapResultFcn* fcn;\n\tb2QueryFilter filter;\n\tconst b2ShapeProxy* proxy;\n\tvoid* userContext;\n} WorldOverlapContext;\n\nstatic bool TreeOverlapCallback( int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\n\tWorldOverlapContext* worldContext = context;\n\tb2World* world = worldContext->world;\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\tif ( b2ShouldQueryCollide( shape->filter, worldContext->filter ) == false )\n\t{\n\t\treturn true;\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\tb2DistanceInput input;\n\tinput.proxyA = *worldContext->proxy;\n\tinput.proxyB = b2MakeShapeDistanceProxy( shape );\n\tinput.transformA = b2Transform_identity;\n\tinput.transformB = transform;\n\tinput.useRadii = true;\n\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput output = b2ShapeDistance( &input, &cache, NULL, 0 );\n\n\tfloat tolerance = 0.1f * B2_LINEAR_SLOP;\n\tif ( output.distance > tolerance )\n\t{\n\t\treturn true;\n\t}\n\n\tb2ShapeId id = { shape->id + 1, world->worldId, shape->generation };\n\tbool result = worldContext->fcn( id, worldContext->userContext );\n\treturn result;\n}\n\nb2TreeStats b2World_OverlapShape( b2WorldId worldId, const b2ShapeProxy* proxy, b2QueryFilter filter, b2OverlapResultFcn* fcn,\n\t\t\t\t\t\t\t\t  void* context )\n{\n\tb2TreeStats treeStats = { 0 };\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn treeStats;\n\t}\n\n\tb2AABB aabb = b2MakeAABB( proxy->points, proxy->count, proxy->radius );\n\tWorldOverlapContext worldContext = {\n\t\tworld, fcn, filter, proxy, context,\n\t};\n\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tb2TreeStats treeResult =\n\t\t\tb2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeOverlapCallback, &worldContext );\n\n\t\ttreeStats.nodeVisits += treeResult.nodeVisits;\n\t\ttreeStats.leafVisits += treeResult.leafVisits;\n\t}\n\n\treturn treeStats;\n}\n\ntypedef struct WorldRayCastContext\n{\n\tb2World* world;\n\tb2CastResultFcn* fcn;\n\tb2QueryFilter filter;\n\tfloat fraction;\n\tvoid* userContext;\n} WorldRayCastContext;\n\nstatic float RayCastCallback( const b2RayCastInput* input, int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\n\tWorldRayCastContext* worldContext = context;\n\tb2World* world = worldContext->world;\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\tif ( b2ShouldQueryCollide( shape->filter, worldContext->filter ) == false )\n\t{\n\t\treturn input->maxFraction;\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\tb2CastOutput output = b2RayCastShape( input, shape, transform );\n\n\tif ( output.hit )\n\t{\n\t\tb2ShapeId id = { shapeId + 1, world->worldId, shape->generation };\n\t\tfloat fraction = worldContext->fcn( id, output.point, output.normal, output.fraction, worldContext->userContext );\n\n\t\t// The user may return -1 to skip this shape\n\t\tif ( 0.0f <= fraction && fraction <= 1.0f )\n\t\t{\n\t\t\tworldContext->fraction = fraction;\n\t\t}\n\n\t\treturn fraction;\n\t}\n\n\treturn input->maxFraction;\n}\n\nb2TreeStats b2World_CastRay( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter, b2CastResultFcn* fcn,\n\t\t\t\t\t\t\t void* context )\n{\n\tb2TreeStats treeStats = { 0 };\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn treeStats;\n\t}\n\n\tB2_ASSERT( b2IsValidVec2( origin ) );\n\tB2_ASSERT( b2IsValidVec2( translation ) );\n\n\tb2RayCastInput input = { origin, translation, 1.0f };\n\n\tWorldRayCastContext worldContext = { world, fcn, filter, 1.0f, context };\n\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tb2TreeStats treeResult =\n\t\t\tb2DynamicTree_RayCast( world->broadPhase.trees + i, &input, filter.maskBits, RayCastCallback, &worldContext );\n\t\ttreeStats.nodeVisits += treeResult.nodeVisits;\n\t\ttreeStats.leafVisits += treeResult.leafVisits;\n\n\t\tif ( worldContext.fraction == 0.0f )\n\t\t{\n\t\t\treturn treeStats;\n\t\t}\n\n\t\tinput.maxFraction = worldContext.fraction;\n\t}\n\n\treturn treeStats;\n}\n\n// This callback finds the closest hit. This is the most common callback used in games.\nstatic float b2RayCastClosestFcn( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )\n{\n\t// Ignore initial overlap\n\tif ( fraction == 0.0f )\n\t{\n\t\treturn -1.0f;\n\t}\n\n\tb2RayResult* rayResult = (b2RayResult*)context;\n\trayResult->shapeId = shapeId;\n\trayResult->point = point;\n\trayResult->normal = normal;\n\trayResult->fraction = fraction;\n\trayResult->hit = true;\n\treturn fraction;\n}\n\nb2RayResult b2World_CastRayClosest( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter )\n{\n\tb2RayResult result = { 0 };\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn result;\n\t}\n\n\tB2_ASSERT( b2IsValidVec2( origin ) );\n\tB2_ASSERT( b2IsValidVec2( translation ) );\n\n\tb2RayCastInput input = { origin, translation, 1.0f };\n\tWorldRayCastContext worldContext = { world, b2RayCastClosestFcn, filter, 1.0f, &result };\n\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tb2TreeStats treeResult =\n\t\t\tb2DynamicTree_RayCast( world->broadPhase.trees + i, &input, filter.maskBits, RayCastCallback, &worldContext );\n\t\tresult.nodeVisits += treeResult.nodeVisits;\n\t\tresult.leafVisits += treeResult.leafVisits;\n\n\t\tif ( worldContext.fraction == 0.0f )\n\t\t{\n\t\t\treturn result;\n\t\t}\n\n\t\tinput.maxFraction = worldContext.fraction;\n\t}\n\n\treturn result;\n}\n\nstatic float ShapeCastCallback( const b2ShapeCastInput* input, int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\n\tWorldRayCastContext* worldContext = context;\n\tb2World* world = worldContext->world;\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\tif ( b2ShouldQueryCollide( shape->filter, worldContext->filter ) == false )\n\t{\n\t\treturn input->maxFraction;\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\tb2CastOutput output = b2ShapeCastShape( input, shape, transform );\n\n\tif ( output.hit )\n\t{\n\t\tb2ShapeId id = { shapeId + 1, world->worldId, shape->generation };\n\t\tfloat fraction = worldContext->fcn( id, output.point, output.normal, output.fraction, worldContext->userContext );\n\n\t\t// The user may return -1 to skip this shape\n\t\tif ( 0.0f <= fraction && fraction <= 1.0f )\n\t\t{\n\t\t\tworldContext->fraction = fraction;\n\t\t}\n\n\t\treturn fraction;\n\t}\n\n\treturn input->maxFraction;\n}\n\nb2TreeStats b2World_CastShape( b2WorldId worldId, const b2ShapeProxy* proxy, b2Vec2 translation, b2QueryFilter filter,\n\t\t\t\t\t\t\t   b2CastResultFcn* fcn, void* context )\n{\n\tb2TreeStats treeStats = { 0 };\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn treeStats;\n\t}\n\n\tB2_ASSERT( b2IsValidVec2( translation ) );\n\n\tb2ShapeCastInput input = { 0 };\n\tinput.proxy = *proxy;\n\tinput.translation = translation;\n\tinput.maxFraction = 1.0f;\n\n\tWorldRayCastContext worldContext = { world, fcn, filter, 1.0f, context };\n\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tb2TreeStats treeResult =\n\t\t\tb2DynamicTree_ShapeCast( world->broadPhase.trees + i, &input, filter.maskBits, ShapeCastCallback, &worldContext );\n\t\ttreeStats.nodeVisits += treeResult.nodeVisits;\n\t\ttreeStats.leafVisits += treeResult.leafVisits;\n\n\t\tif ( worldContext.fraction == 0.0f )\n\t\t{\n\t\t\treturn treeStats;\n\t\t}\n\n\t\tinput.maxFraction = worldContext.fraction;\n\t}\n\n\treturn treeStats;\n}\n\ntypedef struct b2MoverContext\n{\n\tb2World* world;\n\tb2QueryFilter filter;\n\tb2ShapeProxy proxy;\n\tb2Transform transform;\n\tvoid* userContext;\n} b2CharacterCallbackContext;\n\ntypedef struct WorldMoverCastContext\n{\n\tb2World* world;\n\tb2QueryFilter filter;\n\tfloat fraction;\n} WorldMoverCastContext;\n\nstatic float MoverCastCallback( const b2ShapeCastInput* input, int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\tWorldMoverCastContext* worldContext = context;\n\tb2World* world = worldContext->world;\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\tif ( b2ShouldQueryCollide( shape->filter, worldContext->filter ) == false )\n\t{\n\t\treturn worldContext->fraction;\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\tb2CastOutput output = b2ShapeCastShape( input, shape, transform );\n\tif ( output.fraction == 0.0f )\n\t{\n\t\t// Ignore overlapping shapes\n\t\treturn worldContext->fraction;\n\t}\n\n\tworldContext->fraction = output.fraction;\n\treturn output.fraction;\n}\n\nfloat b2World_CastMover( b2WorldId worldId, const b2Capsule* mover, b2Vec2 translation, b2QueryFilter filter )\n{\n\tB2_ASSERT( b2IsValidVec2( translation ) );\n\tB2_ASSERT( mover->radius > 2.0f * B2_LINEAR_SLOP );\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn 1.0f;\n\t}\n\n\tb2ShapeCastInput input = { 0 };\n\tinput.proxy.points[0] = mover->center1;\n\tinput.proxy.points[1] = mover->center2;\n\tinput.proxy.count = 2;\n\tinput.proxy.radius = mover->radius;\n\tinput.translation = translation;\n\tinput.maxFraction = 1.0f;\n\tinput.canEncroach = true;\n\n\tWorldMoverCastContext worldContext = { world, filter, 1.0f };\n\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tb2DynamicTree_ShapeCast( world->broadPhase.trees + i, &input, filter.maskBits, MoverCastCallback, &worldContext );\n\n\t\tif ( worldContext.fraction == 0.0f )\n\t\t{\n\t\t\treturn 0.0f;\n\t\t}\n\n\t\tinput.maxFraction = worldContext.fraction;\n\t}\n\n\treturn worldContext.fraction;\n}\n\ntypedef struct WorldMoverContext\n{\n\tb2World* world;\n\tb2PlaneResultFcn* fcn;\n\tb2QueryFilter filter;\n\tb2Capsule mover;\n\tvoid* userContext;\n} WorldMoverContext;\n\nstatic bool TreeCollideCallback( int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\tWorldMoverContext* worldContext = (WorldMoverContext*)context;\n\tb2World* world = worldContext->world;\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\tif ( b2ShouldQueryCollide( shape->filter, worldContext->filter ) == false )\n\t{\n\t\treturn true;\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\tb2PlaneResult result = b2CollideMover( &worldContext->mover, shape, transform );\n\n\t// todo handle deep overlap\n\tif ( result.hit && b2IsNormalized( result.plane.normal ) )\n\t{\n\t\tb2ShapeId id = { shape->id + 1, world->worldId, shape->generation };\n\t\treturn worldContext->fcn( id, &result, worldContext->userContext );\n\t}\n\n\treturn true;\n}\n\n// It is tempting to use a shape proxy for the mover, but this makes handling deep overlap difficult and the generality may\n// not be worth it.\nvoid b2World_CollideMover( b2WorldId worldId, const b2Capsule* mover, b2QueryFilter filter, b2PlaneResultFcn* fcn, void* context )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tb2Vec2 r = { mover->radius, mover->radius };\n\n\tb2AABB aabb;\n\taabb.lowerBound = b2Sub( b2Min( mover->center1, mover->center2 ), r );\n\taabb.upperBound = b2Add( b2Max( mover->center1, mover->center2 ), r );\n\n\tWorldMoverContext worldContext = {\n\t\tworld, fcn, filter, *mover, context,\n\t};\n\n\tfor ( int i = 0; i < b2_bodyTypeCount; ++i )\n\t{\n\t\tb2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeCollideCallback, &worldContext );\n\t}\n}\n\n#if 0\n\nvoid b2World_Dump()\n{\n\tif (m_locked)\n\t{\n\t\treturn;\n\t}\n\n\tb2OpenDump(\"box2d_dump.inl\");\n\n\tb2Dump(\"b2Vec2 g(%.9g, %.9g);\\n\", m_gravity.x, m_gravity.y);\n\tb2Dump(\"m_world->SetGravity(g);\\n\");\n\n\tb2Dump(\"b2Body** sims = (b2Body**)b2Alloc(%d * sizeof(b2Body*));\\n\", m_bodyCount);\n\tb2Dump(\"b2Joint** joints = (b2Joint**)b2Alloc(%d * sizeof(b2Joint*));\\n\", m_jointCount);\n\n\tint32 i = 0;\n\tfor (b2Body* b = m_bodyList; b; b = b->m_next)\n\t{\n\t\tb->m_islandIndex = i;\n\t\tb->Dump();\n\t\t++i;\n\t}\n\n\ti = 0;\n\tfor (b2Joint* j = m_jointList; j; j = j->m_next)\n\t{\n\t\tj->m_index = i;\n\t\t++i;\n\t}\n\n\t// First pass on joints, skip gear joints.\n\tfor (b2Joint* j = m_jointList; j; j = j->m_next)\n\t{\n\t\tif (j->m_type == e_gearJoint)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2Dump(\"{\\n\");\n\t\tj->Dump();\n\t\tb2Dump(\"}\\n\");\n\t}\n\n\t// Second pass on joints, only gear joints.\n\tfor (b2Joint* j = m_jointList; j; j = j->m_next)\n\t{\n\t\tif (j->m_type != e_gearJoint)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2Dump(\"{\\n\");\n\t\tj->Dump();\n\t\tb2Dump(\"}\\n\");\n\t}\n\n\tb2Dump(\"b2Free(joints);\\n\");\n\tb2Dump(\"b2Free(sims);\\n\");\n\tb2Dump(\"joints = nullptr;\\n\");\n\tb2Dump(\"sims = nullptr;\\n\");\n\n\tb2CloseDump();\n}\n#endif\n\nvoid b2World_SetCustomFilterCallback( b2WorldId worldId, b2CustomFilterFcn* fcn, void* context )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tworld->customFilterFcn = fcn;\n\tworld->customFilterContext = context;\n}\n\nvoid b2World_SetPreSolveCallback( b2WorldId worldId, b2PreSolveFcn* fcn, void* context )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tworld->preSolveFcn = fcn;\n\tworld->preSolveContext = context;\n}\n\nvoid b2World_SetGravity( b2WorldId worldId, b2Vec2 gravity )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tworld->gravity = gravity;\n}\n\nb2Vec2 b2World_GetGravity( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\treturn world->gravity;\n}\n\nstruct ExplosionContext\n{\n\tb2World* world;\n\tb2Vec2 position;\n\tfloat radius;\n\tfloat falloff;\n\tfloat impulsePerLength;\n};\n\nstatic bool ExplosionCallback( int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\n\tstruct ExplosionContext* explosionContext = context;\n\tb2World* world = explosionContext->world;\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\tB2_ASSERT( body->type == b2_dynamicBody );\n\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\tb2DistanceInput input;\n\tinput.proxyA = b2MakeShapeDistanceProxy( shape );\n\tinput.proxyB = b2MakeProxy( &explosionContext->position, 1, 0.0f );\n\tinput.transformA = transform;\n\tinput.transformB = b2Transform_identity;\n\tinput.useRadii = true;\n\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput output = b2ShapeDistance( &input, &cache, NULL, 0 );\n\n\tfloat radius = explosionContext->radius;\n\tfloat falloff = explosionContext->falloff;\n\tif ( output.distance > radius + falloff )\n\t{\n\t\treturn true;\n\t}\n\n\tb2WakeBody( world, body );\n\n\tif ( body->setIndex != b2_awakeSet )\n\t{\n\t\treturn true;\n\t}\n\n\tb2Vec2 closestPoint = output.pointA;\n\tif ( output.distance == 0.0f )\n\t{\n\t\tb2Vec2 localCentroid = b2GetShapeCentroid( shape );\n\t\tclosestPoint = b2TransformPoint( transform, localCentroid );\n\t}\n\n\tb2Vec2 direction = b2Sub( closestPoint, explosionContext->position );\n\tif ( b2LengthSquared( direction ) > 100.0f * FLT_EPSILON * FLT_EPSILON )\n\t{\n\t\tdirection = b2Normalize( direction );\n\t}\n\telse\n\t{\n\t\tdirection = (b2Vec2){ 1.0f, 0.0f };\n\t}\n\n\tb2Vec2 localLine = b2InvRotateVector( transform.q, b2LeftPerp( direction ) );\n\tfloat perimeter = b2GetShapeProjectedPerimeter( shape, localLine );\n\tfloat scale = 1.0f;\n\tif ( output.distance > radius && falloff > 0.0f )\n\t{\n\t\tscale = b2ClampFloat( ( radius + falloff - output.distance ) / falloff, 0.0f, 1.0f );\n\t}\n\n\tfloat magnitude = explosionContext->impulsePerLength * perimeter * scale;\n\tb2Vec2 impulse = b2MulSV( magnitude, direction );\n\n\tint localIndex = body->localIndex;\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tb2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex );\n\tb2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex );\n\tstate->linearVelocity = b2MulAdd( state->linearVelocity, bodySim->invMass, impulse );\n\tstate->angularVelocity += bodySim->invInertia * b2Cross( b2Sub( closestPoint, bodySim->center ), impulse );\n\n\treturn true;\n}\n\nvoid b2World_Explode( b2WorldId worldId, const b2ExplosionDef* explosionDef )\n{\n\tuint64_t maskBits = explosionDef->maskBits;\n\tb2Vec2 position = explosionDef->position;\n\tfloat radius = explosionDef->radius;\n\tfloat falloff = explosionDef->falloff;\n\tfloat impulsePerLength = explosionDef->impulsePerLength;\n\n\tB2_ASSERT( b2IsValidVec2( position ) );\n\tB2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( falloff ) && falloff >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( impulsePerLength ) );\n\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tstruct ExplosionContext explosionContext = { world, position, radius, falloff, impulsePerLength };\n\n\tb2AABB aabb;\n\taabb.lowerBound.x = position.x - ( radius + falloff );\n\taabb.lowerBound.y = position.y - ( radius + falloff );\n\taabb.upperBound.x = position.x + ( radius + falloff );\n\taabb.upperBound.y = position.y + ( radius + falloff );\n\n\tb2DynamicTree_Query( world->broadPhase.trees + b2_dynamicBody, aabb, maskBits, ExplosionCallback, &explosionContext );\n}\n\nvoid b2World_RebuildStaticTree( b2WorldId worldId )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tb2DynamicTree* staticTree = world->broadPhase.trees + b2_staticBody;\n\tb2DynamicTree_Rebuild( staticTree, true );\n}\n\nvoid b2World_EnableSpeculative( b2WorldId worldId, bool flag )\n{\n\tb2World* world = b2GetWorldFromId( worldId );\n\tworld->enableSpeculative = flag;\n}\n\n#if B2_VALIDATE\n// This validates island graph connectivity for each body\nvoid b2ValidateConnectivity( b2World* world )\n{\n\tb2Body* bodies = world->bodies.data;\n\tint bodyCapacity = world->bodies.count;\n\n\tfor ( int bodyIndex = 0; bodyIndex < bodyCapacity; ++bodyIndex )\n\t{\n\t\tb2Body* body = bodies + bodyIndex;\n\t\tif ( body->id == B2_NULL_INDEX )\n\t\t{\n\t\t\tb2ValidateFreeId( &world->bodyIdPool, bodyIndex );\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2ValidateUsedId( &world->bodyIdPool, bodyIndex );\n\n\t\tB2_ASSERT( bodyIndex == body->id );\n\n\t\t// Need to get the root island because islands are not merged until the next time step\n\t\tint bodyIslandId = body->islandId;\n\t\tint bodySetIndex = body->setIndex;\n\n\t\tint contactKey = body->headContactKey;\n\t\twhile ( contactKey != B2_NULL_INDEX )\n\t\t{\n\t\t\tint contactId = contactKey >> 1;\n\t\t\tint edgeIndex = contactKey & 1;\n\n\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\n\t\t\tbool touching = ( contact->flags & b2_contactTouchingFlag ) != 0;\n\t\t\tif ( touching )\n\t\t\t{\n\t\t\t\tif ( bodySetIndex != b2_staticSet )\n\t\t\t\t{\n\t\t\t\t\tint contactIslandId = contact->islandId;\n\t\t\t\t\tB2_ASSERT( contactIslandId == bodyIslandId );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tB2_ASSERT( contact->islandId == B2_NULL_INDEX );\n\t\t\t}\n\n\t\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\t\t}\n\n\t\tint jointKey = body->headJointKey;\n\t\twhile ( jointKey != B2_NULL_INDEX )\n\t\t{\n\t\t\tint jointId = jointKey >> 1;\n\t\t\tint edgeIndex = jointKey & 1;\n\n\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\n\t\t\tint otherEdgeIndex = edgeIndex ^ 1;\n\n\t\t\tb2Body* otherBody = b2BodyArray_Get( &world->bodies, joint->edges[otherEdgeIndex].bodyId );\n\n\t\t\tif ( bodySetIndex == b2_disabledSet || otherBody->setIndex == b2_disabledSet )\n\t\t\t{\n\t\t\t\tB2_ASSERT( joint->islandId == B2_NULL_INDEX );\n\t\t\t}\n\t\t\telse if ( bodySetIndex == b2_staticSet )\n\t\t\t{\n\t\t\t\t// Intentional nesting\n\t\t\t\tif ( otherBody->setIndex == b2_staticSet )\n\t\t\t\t{\n\t\t\t\t\tB2_ASSERT( joint->islandId == B2_NULL_INDEX );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( body->type != b2_dynamicBody && otherBody->type != b2_dynamicBody )\n\t\t\t{\n\t\t\t\tB2_ASSERT( joint->islandId == B2_NULL_INDEX );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint jointIslandId = joint->islandId;\n\t\t\t\tB2_ASSERT( jointIslandId == bodyIslandId );\n\t\t\t}\n\n\t\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\t\t}\n\t}\n}\n\n// Validates solver sets, but not island connectivity\nvoid b2ValidateSolverSets( b2World* world )\n{\n\tB2_ASSERT( b2GetIdCapacity( &world->bodyIdPool ) == world->bodies.count );\n\tB2_ASSERT( b2GetIdCapacity( &world->contactIdPool ) == world->contacts.count );\n\tB2_ASSERT( b2GetIdCapacity( &world->jointIdPool ) == world->joints.count );\n\tB2_ASSERT( b2GetIdCapacity( &world->islandIdPool ) == world->islands.count );\n\tB2_ASSERT( b2GetIdCapacity( &world->solverSetIdPool ) == world->solverSets.count );\n\n\tint activeSetCount = 0;\n\tint totalBodyCount = 0;\n\tint totalJointCount = 0;\n\tint totalContactCount = 0;\n\tint totalIslandCount = 0;\n\n\t// Validate all solver sets\n\tint setCount = world->solverSets.count;\n\tfor ( int setIndex = 0; setIndex < setCount; ++setIndex )\n\t{\n\t\tb2SolverSet* set = world->solverSets.data + setIndex;\n\t\tif ( set->setIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\tactiveSetCount += 1;\n\n\t\t\tif ( setIndex == b2_staticSet )\n\t\t\t{\n\t\t\t\tB2_ASSERT( set->contactSims.count == 0 );\n\t\t\t\tB2_ASSERT( set->islandSims.count == 0 );\n\t\t\t\tB2_ASSERT( set->bodyStates.count == 0 );\n\t\t\t}\n\t\t\telse if ( setIndex == b2_disabledSet )\n\t\t\t{\n\t\t\t\tB2_ASSERT( set->islandSims.count == 0 );\n\t\t\t\tB2_ASSERT( set->bodyStates.count == 0 );\n\t\t\t}\n\t\t\telse if ( setIndex == b2_awakeSet )\n\t\t\t{\n\t\t\t\tB2_ASSERT( set->bodySims.count == set->bodyStates.count );\n\t\t\t\tB2_ASSERT( set->jointSims.count == 0 );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tB2_ASSERT( set->bodyStates.count == 0 );\n\t\t\t}\n\n\t\t\t// Validate bodies\n\t\t\t{\n\t\t\t\tb2Body* bodies = world->bodies.data;\n\t\t\t\tB2_ASSERT( set->bodySims.count >= 0 );\n\t\t\t\ttotalBodyCount += set->bodySims.count;\n\t\t\t\tfor ( int i = 0; i < set->bodySims.count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2BodySim* bodySim = set->bodySims.data + i;\n\n\t\t\t\t\tint bodyId = bodySim->bodyId;\n\t\t\t\t\tB2_ASSERT( 0 <= bodyId && bodyId < world->bodies.count );\n\t\t\t\t\tb2Body* body = bodies + bodyId;\n\t\t\t\t\tB2_ASSERT( body->setIndex == setIndex );\n\t\t\t\t\tB2_ASSERT( body->localIndex == i );\n\n\t\t\t\t\tif ( body->type == b2_dynamicBody )\n\t\t\t\t\t{\n\t\t\t\t\t\tB2_ASSERT( body->flags & b2_dynamicFlag );\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( setIndex == b2_disabledSet )\n\t\t\t\t\t{\n\t\t\t\t\t\tB2_ASSERT( body->headContactKey == B2_NULL_INDEX );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Validate body shapes\n\t\t\t\t\tint prevShapeId = B2_NULL_INDEX;\n\t\t\t\t\tint shapeId = body->headShapeId;\n\t\t\t\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t\t\t\t{\n\t\t\t\t\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\t\t\t\t\tB2_ASSERT( shape->id == shapeId );\n\t\t\t\t\t\tB2_ASSERT( shape->prevShapeId == prevShapeId );\n\n\t\t\t\t\t\tif ( setIndex == b2_disabledSet )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tB2_ASSERT( shape->proxyKey == B2_NULL_INDEX );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ( setIndex == b2_staticSet )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tB2_ASSERT( B2_PROXY_TYPE( shape->proxyKey ) == b2_staticBody );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb2BodyType proxyType = B2_PROXY_TYPE( shape->proxyKey );\n\t\t\t\t\t\t\tB2_ASSERT( proxyType == b2_kinematicBody || proxyType == b2_dynamicBody );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tprevShapeId = shapeId;\n\t\t\t\t\t\tshapeId = shape->nextShapeId;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Validate body contacts\n\t\t\t\t\tint contactKey = body->headContactKey;\n\t\t\t\t\twhile ( contactKey != B2_NULL_INDEX )\n\t\t\t\t\t{\n\t\t\t\t\t\tint contactId = contactKey >> 1;\n\t\t\t\t\t\tint edgeIndex = contactKey & 1;\n\n\t\t\t\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\t\t\t\t\tB2_ASSERT( contact->setIndex != b2_staticSet );\n\t\t\t\t\t\tB2_ASSERT( contact->edges[0].bodyId == bodyId || contact->edges[1].bodyId == bodyId );\n\t\t\t\t\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Validate body joints\n\t\t\t\t\tint jointKey = body->headJointKey;\n\t\t\t\t\twhile ( jointKey != B2_NULL_INDEX )\n\t\t\t\t\t{\n\t\t\t\t\t\tint jointId = jointKey >> 1;\n\t\t\t\t\t\tint edgeIndex = jointKey & 1;\n\n\t\t\t\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\n\t\t\t\t\t\tint otherEdgeIndex = edgeIndex ^ 1;\n\n\t\t\t\t\t\tb2Body* otherBody = b2BodyArray_Get( &world->bodies, joint->edges[otherEdgeIndex].bodyId );\n\n\t\t\t\t\t\tif ( setIndex == b2_disabledSet || otherBody->setIndex == b2_disabledSet )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tB2_ASSERT( joint->setIndex == b2_disabledSet );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ( setIndex == b2_staticSet && otherBody->setIndex == b2_staticSet )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tB2_ASSERT( joint->setIndex == b2_staticSet );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ( body->type != b2_dynamicBody && otherBody->type != b2_dynamicBody )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tB2_ASSERT( joint->setIndex == b2_staticSet );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ( setIndex == b2_awakeSet )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tB2_ASSERT( joint->setIndex == b2_awakeSet );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ( setIndex >= b2_firstSleepingSet )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tB2_ASSERT( joint->setIndex == setIndex );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tb2JointSim* jointSim = b2GetJointSim( world, joint );\n\t\t\t\t\t\tB2_ASSERT( jointSim->jointId == jointId );\n\t\t\t\t\t\tB2_ASSERT( jointSim->bodyIdA == joint->edges[0].bodyId );\n\t\t\t\t\t\tB2_ASSERT( jointSim->bodyIdB == joint->edges[1].bodyId );\n\n\t\t\t\t\t\tjointKey = joint->edges[edgeIndex].nextKey;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Validate contacts\n\t\t\t{\n\t\t\t\tB2_ASSERT( set->contactSims.count >= 0 );\n\t\t\t\ttotalContactCount += set->contactSims.count;\n\t\t\t\tfor ( int i = 0; i < set->contactSims.count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2ContactSim* contactSim = set->contactSims.data + i;\n\t\t\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactSim->contactId );\n\t\t\t\t\tif ( setIndex == b2_awakeSet )\n\t\t\t\t\t{\n\t\t\t\t\t\t// contact should be non-touching if awake\n\t\t\t\t\t\t// or it could be this contact hasn't been transferred yet\n\t\t\t\t\t\tB2_ASSERT( contactSim->manifold.pointCount == 0 ||\n\t\t\t\t\t\t\t\t   ( contactSim->simFlags & b2_simStartedTouching ) != 0 );\n\t\t\t\t\t}\n\t\t\t\t\tB2_ASSERT( contact->setIndex == setIndex );\n\t\t\t\t\tB2_ASSERT( contact->colorIndex == B2_NULL_INDEX );\n\t\t\t\t\tB2_ASSERT( contact->localIndex == i );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Validate joints\n\t\t\t{\n\t\t\t\tB2_ASSERT( set->jointSims.count >= 0 );\n\t\t\t\ttotalJointCount += set->jointSims.count;\n\t\t\t\tfor ( int i = 0; i < set->jointSims.count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2JointSim* jointSim = set->jointSims.data + i;\n\t\t\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointSim->jointId );\n\t\t\t\t\tB2_ASSERT( joint->setIndex == setIndex );\n\t\t\t\t\tB2_ASSERT( joint->colorIndex == B2_NULL_INDEX );\n\t\t\t\t\tB2_ASSERT( joint->localIndex == i );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Validate islands\n\t\t\t{\n\t\t\t\tB2_ASSERT( set->islandSims.count >= 0 );\n\t\t\t\ttotalIslandCount += set->islandSims.count;\n\t\t\t\tfor ( int i = 0; i < set->islandSims.count; ++i )\n\t\t\t\t{\n\t\t\t\t\tb2IslandSim* islandSim = set->islandSims.data + i;\n\t\t\t\t\tb2Island* island = b2IslandArray_Get( &world->islands, islandSim->islandId );\n\t\t\t\t\tB2_ASSERT( island->setIndex == setIndex );\n\t\t\t\t\tB2_ASSERT( island->localIndex == i );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tB2_ASSERT( set->bodySims.count == 0 );\n\t\t\tB2_ASSERT( set->contactSims.count == 0 );\n\t\t\tB2_ASSERT( set->jointSims.count == 0 );\n\t\t\tB2_ASSERT( set->islandSims.count == 0 );\n\t\t\tB2_ASSERT( set->bodyStates.count == 0 );\n\t\t}\n\t}\n\n\tint setIdCount = b2GetIdCount( &world->solverSetIdPool );\n\tB2_ASSERT( activeSetCount == setIdCount );\n\n\tint bodyIdCount = b2GetIdCount( &world->bodyIdPool );\n\tB2_ASSERT( totalBodyCount == bodyIdCount );\n\n\tint islandIdCount = b2GetIdCount( &world->islandIdPool );\n\tB2_ASSERT( totalIslandCount == islandIdCount );\n\n\t// Validate constraint graph\n\tfor ( int colorIndex = 0; colorIndex < B2_GRAPH_COLOR_COUNT; ++colorIndex )\n\t{\n\t\tb2GraphColor* color = world->constraintGraph.colors + colorIndex;\n\t\tint bitCount = 0;\n\n\t\tB2_ASSERT( color->contactSims.count >= 0 );\n\t\ttotalContactCount += color->contactSims.count;\n\t\tfor ( int i = 0; i < color->contactSims.count; ++i )\n\t\t{\n\t\t\tb2ContactSim* contactSim = color->contactSims.data + i;\n\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactSim->contactId );\n\t\t\t// contact should be touching in the constraint graph or awaiting transfer to non-touching\n\t\t\tB2_ASSERT( contactSim->manifold.pointCount > 0 ||\n\t\t\t\t\t   ( contactSim->simFlags & ( b2_simStoppedTouching | b2_simDisjoint ) ) != 0 );\n\t\t\tB2_ASSERT( contact->setIndex == b2_awakeSet );\n\t\t\tB2_ASSERT( contact->colorIndex == colorIndex );\n\t\t\tB2_ASSERT( contact->localIndex == i );\n\n\t\t\tint bodyIdA = contact->edges[0].bodyId;\n\t\t\tint bodyIdB = contact->edges[1].bodyId;\n\n\t\t\tif ( colorIndex < B2_OVERFLOW_INDEX )\n\t\t\t{\n\t\t\t\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );\n\t\t\t\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );\n\t\t\t\tB2_ASSERT( b2GetBit( &color->bodySet, bodyIdA ) == ( bodyA->type == b2_dynamicBody ) );\n\t\t\t\tB2_ASSERT( b2GetBit( &color->bodySet, bodyIdB ) == ( bodyB->type == b2_dynamicBody ) );\n\n\t\t\t\tbitCount += bodyA->type == b2_dynamicBody ? 1 : 0;\n\t\t\t\tbitCount += bodyB->type == b2_dynamicBody ? 1 : 0;\n\t\t\t}\n\t\t}\n\n\t\tB2_ASSERT( color->jointSims.count >= 0 );\n\t\ttotalJointCount += color->jointSims.count;\n\t\tfor ( int i = 0; i < color->jointSims.count; ++i )\n\t\t{\n\t\t\tb2JointSim* jointSim = color->jointSims.data + i;\n\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointSim->jointId );\n\t\t\tB2_ASSERT( joint->setIndex == b2_awakeSet );\n\t\t\tB2_ASSERT( joint->colorIndex == colorIndex );\n\t\t\tB2_ASSERT( joint->localIndex == i );\n\n\t\t\tint bodyIdA = joint->edges[0].bodyId;\n\t\t\tint bodyIdB = joint->edges[1].bodyId;\n\n\t\t\tif ( colorIndex < B2_OVERFLOW_INDEX )\n\t\t\t{\n\t\t\t\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );\n\t\t\t\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );\n\t\t\t\tB2_ASSERT( b2GetBit( &color->bodySet, bodyIdA ) == ( bodyA->type == b2_dynamicBody ) );\n\t\t\t\tB2_ASSERT( b2GetBit( &color->bodySet, bodyIdB ) == ( bodyB->type == b2_dynamicBody ) );\n\n\t\t\t\tbitCount += bodyA->type == b2_dynamicBody ? 1 : 0;\n\t\t\t\tbitCount += bodyB->type == b2_dynamicBody ? 1 : 0;\n\t\t\t}\n\t\t}\n\n\t\t// Validate the bit population for this graph color\n\t\tB2_ASSERT( bitCount == b2CountSetBits( &color->bodySet ) );\n\t}\n\n\tint contactIdCount = b2GetIdCount( &world->contactIdPool );\n\tB2_ASSERT( totalContactCount == contactIdCount );\n\tB2_ASSERT( totalContactCount == (int)world->broadPhase.pairSet.count );\n\n\tint jointIdCount = b2GetIdCount( &world->jointIdPool );\n\tB2_ASSERT( totalJointCount == jointIdCount );\n\n// Validate shapes\n// This is very slow on compounds\n#if 0\n\tint shapeCapacity = b2Array(world->shapeArray).count;\n\tfor (int shapeIndex = 0; shapeIndex < shapeCapacity; shapeIndex += 1)\n\t{\n\t\tb2Shape* shape = world->shapeArray + shapeIndex;\n\t\tif (shape->id != shapeIndex)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tB2_ASSERT(0 <= shape->bodyId && shape->bodyId < b2Array(world->bodyArray).count);\n\n\t\tb2Body* body = world->bodyArray + shape->bodyId;\n\t\tB2_ASSERT(0 <= body->setIndex && body->setIndex < b2Array(world->solverSetArray).count);\n\n\t\tb2SolverSet* set = world->solverSetArray + body->setIndex;\n\t\tB2_ASSERT(0 <= body->localIndex && body->localIndex < set->sims.count);\n\n\t\tb2BodySim* bodySim = set->sims.data + body->localIndex;\n\t\tB2_ASSERT(bodySim->bodyId == shape->bodyId);\n\n\t\tbool found = false;\n\t\tint shapeCount = 0;\n\t\tint index = body->headShapeId;\n\t\twhile (index != B2_NULL_INDEX)\n\t\t{\n\t\t\tb2CheckId(world->shapeArray, index);\n\t\t\tb2Shape* s = world->shapeArray + index;\n\t\t\tif (index == shapeIndex)\n\t\t\t{\n\t\t\t\tfound = true;\n\t\t\t}\n\n\t\t\tindex = s->nextShapeId;\n\t\t\tshapeCount += 1;\n\t\t}\n\n\t\tB2_ASSERT(found);\n\t\tB2_ASSERT(shapeCount == body->shapeCount);\n\t}\n#endif\n}\n\n// Validate contact touching status.\nvoid b2ValidateContacts( b2World* world )\n{\n\tint contactCount = world->contacts.count;\n\tB2_ASSERT( contactCount == b2GetIdCapacity( &world->contactIdPool ) );\n\tint allocatedContactCount = 0;\n\n\tfor ( int contactIndex = 0; contactIndex < contactCount; ++contactIndex )\n\t{\n\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactIndex );\n\t\tif ( contact->contactId == B2_NULL_INDEX )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tB2_ASSERT( contact->contactId == contactIndex );\n\n\t\tallocatedContactCount += 1;\n\n\t\tbool touching = ( contact->flags & b2_contactTouchingFlag ) != 0;\n\n\t\tint setId = contact->setIndex;\n\n\t\tif ( setId == b2_awakeSet )\n\t\t{\n\t\t\tif ( touching )\n\t\t\t{\n\t\t\t\tB2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < B2_GRAPH_COLOR_COUNT );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tB2_ASSERT( contact->colorIndex == B2_NULL_INDEX );\n\t\t\t}\n\t\t}\n\t\telse if ( setId >= b2_firstSleepingSet )\n\t\t{\n\t\t\t// Only touching contacts allowed in a sleeping set\n\t\t\tB2_ASSERT( touching == true );\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Sleeping and non-touching contacts belong in the disabled set\n\t\t\tB2_ASSERT( touching == false && setId == b2_disabledSet );\n\t\t}\n\n\t\tb2ContactSim* contactSim = b2GetContactSim( world, contact );\n\t\tB2_ASSERT( contactSim->contactId == contactIndex );\n\t\tB2_ASSERT( contactSim->bodyIdA == contact->edges[0].bodyId );\n\t\tB2_ASSERT( contactSim->bodyIdB == contact->edges[1].bodyId );\n\n\t\tbool simTouching = ( contactSim->simFlags & b2_simTouchingFlag ) != 0;\n\t\tB2_ASSERT( touching == simTouching );\n\n\t\tB2_ASSERT( 0 <= contactSim->manifold.pointCount && contactSim->manifold.pointCount <= 2 );\n\t}\n\n\tint contactIdCount = b2GetIdCount( &world->contactIdPool );\n\tB2_ASSERT( allocatedContactCount == contactIdCount );\n}\n\n#else\n\nvoid b2ValidateConnectivity( b2World* world )\n{\n\tB2_UNUSED( world );\n}\n\nvoid b2ValidateSolverSets( b2World* world )\n{\n\tB2_UNUSED( world );\n}\n\nvoid b2ValidateContacts( b2World* world )\n{\n\tB2_UNUSED( world );\n}\n\n#endif\n"
  },
  {
    "path": "src/physics_world.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n#include \"bitset.h\"\n#include \"broad_phase.h\"\n#include \"constraint_graph.h\"\n#include \"id_pool.h\"\n#include \"arena_allocator.h\"\n\n#include \"box2d/types.h\"\n\n// Per thread task storage\ntypedef struct b2TaskContext\n{\n\t// Collect per thread sensor continuous hit events.\n\tb2SensorHitArray sensorHits;\n\n\t// These bits align with the contact id capacity and signal a change in contact status\n\tb2BitSet contactStateBitSet;\n\n\t// These bits align with the joint id capacity and signal a change in contact status\n\tb2BitSet jointStateBitSet;\n\n\t// Used to track bodies with shapes that have enlarged AABBs. This avoids having a bit array\n\t// that is very large when there are many static shapes.\n\tb2BitSet enlargedSimBitSet;\n\n\t// Used to put islands to sleep\n\tb2BitSet awakeIslandBitSet;\n\n\t// Per worker split island candidate\n\tfloat splitSleepTime;\n\tint splitIslandId;\n\n} b2TaskContext;\n\n// The world struct manages all physics entities, dynamic simulation,  and asynchronous queries.\n// The world also contains efficient memory management facilities.\ntypedef struct b2World\n{\n\tb2ArenaAllocator arena;\n\tb2BroadPhase broadPhase;\n\tb2ConstraintGraph constraintGraph;\n\n\t// The body id pool is used to allocate and recycle body ids. Body ids\n\t// provide a stable identifier for users, but incur caches misses when used\n\t// to access body data. Aligns with b2Body.\n\tb2IdPool bodyIdPool;\n\n\t// This is a sparse array that maps body ids to the body data\n\t// stored in solver sets. As sims move within a set or across set.\n\t// Indices come from id pool.\n\tb2BodyArray bodies;\n\n\t// Provides free list for solver sets.\n\tb2IdPool solverSetIdPool;\n\n\t// Solvers sets allow sims to be stored in contiguous arrays. The first\n\t// set is all static sims. The second set is active sims. The third set is disabled\n\t// sims. The remaining sets are sleeping islands.\n\tb2SolverSetArray solverSets;\n\n\t// Used to create stable ids for joints\n\tb2IdPool jointIdPool;\n\n\t// This is a sparse array that maps joint ids to the joint data stored in the constraint graph\n\t// or in the solver sets.\n\tb2JointArray joints;\n\n\t// Used to create stable ids for contacts\n\tb2IdPool contactIdPool;\n\n\t// This is a sparse array that maps contact ids to the contact data stored in the constraint graph\n\t// or in the solver sets.\n\tb2ContactArray contacts;\n\n\t// Used to create stable ids for islands\n\tb2IdPool islandIdPool;\n\n\t// This is a sparse array that maps island ids to the island data stored in the solver sets.\n\tb2IslandArray islands;\n\n\tb2IdPool shapeIdPool;\n\tb2IdPool chainIdPool;\n\n\t// These are sparse arrays that point into the pools above\n\tb2ShapeArray shapes;\n\tb2ChainShapeArray chainShapes;\n\n\t// This is a dense array of sensor data.\n\tb2SensorArray sensors;\n\n\t// Per thread storage\n\tb2TaskContextArray taskContexts;\n\tb2SensorTaskContextArray sensorTaskContexts;\n\n\tb2BodyMoveEventArray bodyMoveEvents;\n\tb2SensorBeginTouchEventArray sensorBeginEvents;\n\tb2ContactBeginTouchEventArray contactBeginEvents;\n\n\t// End events are double buffered so that the user doesn't need to flush events\n\tb2SensorEndTouchEventArray sensorEndEvents[2];\n\tb2ContactEndTouchEventArray contactEndEvents[2];\n\tint endEventArrayIndex;\n\n\tb2ContactHitEventArray contactHitEvents;\n\tb2JointEventArray jointEvents;\n\n\t// todo consider deferred waking and impulses to make it possible\n\t// to apply forces and impulses from multiple threads\n\t// impulses must be deferred because sleeping bodies have no velocity state\n\t// Problems:\n\t// - multiple forces applied to the same body from multiple threads\n\t// Deferred wake\n\t//b2BitSet bodyWakeSet;\n\t//b2ImpulseArray deferredImpulses;\n\n\t// Used to track debug draw\n\tb2BitSet debugBodySet;\n\tb2BitSet debugJointSet;\n\tb2BitSet debugContactSet;\n\tb2BitSet debugIslandSet;\n\n\t// Id that is incremented every time step\n\tuint64_t stepIndex;\n\n\t// Identify islands for splitting as follows:\n\t// - I want to split islands so smaller islands can sleep\n\t// - when a body comes to rest and its sleep timer trips, I can look at the island and flag it for splitting\n\t//   if it has removed constraints\n\t// - islands that have removed constraints must be put split first because I don't want to wake bodies incorrectly\n\t// - otherwise I can use the awake islands that have bodies wanting to sleep as the splitting candidates\n\t// - if no bodies want to sleep then there is no reason to perform island splitting\n\tint splitIslandId;\n\n\tb2Vec2 gravity;\n\tfloat hitEventThreshold;\n\tfloat restitutionThreshold;\n\tfloat maxLinearSpeed;\n\tfloat contactSpeed;\n\tfloat contactHertz;\n\tfloat contactDampingRatio;\n\n\tb2FrictionCallback* frictionCallback;\n\tb2RestitutionCallback* restitutionCallback;\n\n\tuint16_t generation;\n\n\tb2Profile profile;\n\n\tb2PreSolveFcn* preSolveFcn;\n\tvoid* preSolveContext;\n\n\tb2CustomFilterFcn* customFilterFcn;\n\tvoid* customFilterContext;\n\n\tint workerCount;\n\tb2EnqueueTaskCallback* enqueueTaskFcn;\n\tb2FinishTaskCallback* finishTaskFcn;\n\tvoid* userTaskContext;\n\tvoid* userTreeTask;\n\n\tvoid* userData;\n\n\t// Remember type step used for reporting forces and torques\n\t// inverse sub-step\n\tfloat inv_h;\n\n\t// inverse full-step\n\tfloat inv_dt;\n\n\tint activeTaskCount;\n\tint taskCount;\n\n\tuint16_t worldId;\n\n\tbool enableSleep;\n\tbool locked;\n\tbool enableWarmStarting;\n\tbool enableContactSoftening;\n\tbool enableContinuous;\n\tbool enableSpeculative;\n\tbool inUse;\n} b2World;\n\nb2World* b2GetWorldFromId( b2WorldId id );\nb2World* b2GetWorld( int index );\nb2World* b2GetWorldLocked( int index );\n\nvoid b2ValidateConnectivity( b2World* world );\nvoid b2ValidateSolverSets( b2World* world );\nvoid b2ValidateContacts( b2World* world );\n\nB2_ARRAY_INLINE( b2BodyMoveEvent, b2BodyMoveEvent )\nB2_ARRAY_INLINE( b2ContactBeginTouchEvent, b2ContactBeginTouchEvent )\nB2_ARRAY_INLINE( b2ContactEndTouchEvent, b2ContactEndTouchEvent )\nB2_ARRAY_INLINE( b2ContactHitEvent, b2ContactHitEvent )\nB2_ARRAY_INLINE( b2JointEvent, b2JointEvent )\nB2_ARRAY_INLINE( b2SensorBeginTouchEvent, b2SensorBeginTouchEvent )\nB2_ARRAY_INLINE( b2SensorEndTouchEvent, b2SensorEndTouchEvent )\nB2_ARRAY_INLINE( b2TaskContext, b2TaskContext )\n"
  },
  {
    "path": "src/prismatic_joint.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"body.h\"\n#include \"core.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"solver.h\"\n#include \"solver_set.h\"\n\n// needed for dll export\n#include \"box2d/box2d.h\"\n\n#include <stdio.h>\n\nvoid b2PrismaticJoint_EnableSpring( b2JointId jointId, bool enableSpring )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tif ( enableSpring != joint->prismaticJoint.enableSpring )\n\t{\n\t\tjoint->prismaticJoint.enableSpring = enableSpring;\n\t\tjoint->prismaticJoint.springImpulse = 0.0f;\n\t}\n}\n\nbool b2PrismaticJoint_IsSpringEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.enableSpring;\n}\n\nvoid b2PrismaticJoint_SetSpringHertz( b2JointId jointId, float hertz )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tjoint->prismaticJoint.hertz = hertz;\n}\n\nfloat b2PrismaticJoint_GetSpringHertz( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.hertz;\n}\n\nvoid b2PrismaticJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tjoint->prismaticJoint.dampingRatio = dampingRatio;\n}\n\nfloat b2PrismaticJoint_GetSpringDampingRatio( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.dampingRatio;\n}\n\nvoid b2PrismaticJoint_SetTargetTranslation( b2JointId jointId, float translation )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tjoint->prismaticJoint.targetTranslation = translation;\n}\n\nfloat b2PrismaticJoint_GetTargetTranslation( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.targetTranslation;\n}\n\nvoid b2PrismaticJoint_EnableLimit( b2JointId jointId, bool enableLimit )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tif ( enableLimit != joint->prismaticJoint.enableLimit )\n\t{\n\t\tjoint->prismaticJoint.enableLimit = enableLimit;\n\t\tjoint->prismaticJoint.lowerImpulse = 0.0f;\n\t\tjoint->prismaticJoint.upperImpulse = 0.0f;\n\t}\n}\n\nbool b2PrismaticJoint_IsLimitEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.enableLimit;\n}\n\nfloat b2PrismaticJoint_GetLowerLimit( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.lowerTranslation;\n}\n\nfloat b2PrismaticJoint_GetUpperLimit( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.upperTranslation;\n}\n\nvoid b2PrismaticJoint_SetLimits( b2JointId jointId, float lower, float upper )\n{\n\tB2_ASSERT( lower <= upper );\n\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tif ( lower != joint->prismaticJoint.lowerTranslation || upper != joint->prismaticJoint.upperTranslation )\n\t{\n\t\tjoint->prismaticJoint.lowerTranslation = b2MinFloat( lower, upper );\n\t\tjoint->prismaticJoint.upperTranslation = b2MaxFloat( lower, upper );\n\t\tjoint->prismaticJoint.lowerImpulse = 0.0f;\n\t\tjoint->prismaticJoint.upperImpulse = 0.0f;\n\t}\n}\n\nvoid b2PrismaticJoint_EnableMotor( b2JointId jointId, bool enableMotor )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tif ( enableMotor != joint->prismaticJoint.enableMotor )\n\t{\n\t\tjoint->prismaticJoint.enableMotor = enableMotor;\n\t\tjoint->prismaticJoint.motorImpulse = 0.0f;\n\t}\n}\n\nbool b2PrismaticJoint_IsMotorEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.enableMotor;\n}\n\nvoid b2PrismaticJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tjoint->prismaticJoint.motorSpeed = motorSpeed;\n}\n\nfloat b2PrismaticJoint_GetMotorSpeed( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.motorSpeed;\n}\n\nfloat b2PrismaticJoint_GetMotorForce( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2JointSim* base = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn world->inv_h * base->prismaticJoint.motorImpulse;\n}\n\nvoid b2PrismaticJoint_SetMaxMotorForce( b2JointId jointId, float force )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tjoint->prismaticJoint.maxMotorForce = force;\n}\n\nfloat b2PrismaticJoint_GetMaxMotorForce( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\treturn joint->prismaticJoint.maxMotorForce;\n}\n\nfloat b2PrismaticJoint_GetTranslation( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2JointSim* jointSim = b2GetJointSimCheckType( jointId, b2_prismaticJoint );\n\tb2Transform transformA = b2GetBodyTransform( world, jointSim->bodyIdA );\n\tb2Transform transformB = b2GetBodyTransform( world, jointSim->bodyIdB );\n\n\tb2Vec2 localAxisA = b2RotateVector( jointSim->localFrameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\tb2Vec2 axisA = b2RotateVector( transformA.q, localAxisA );\n\tb2Vec2 pA = b2TransformPoint( transformA, jointSim->localFrameA.p );\n\tb2Vec2 pB = b2TransformPoint( transformB, jointSim->localFrameB.p );\n\tb2Vec2 d = b2Sub( pB, pA );\n\tfloat translation = b2Dot( d, axisA );\n\treturn translation;\n}\n\nfloat b2PrismaticJoint_GetSpeed( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2Joint* joint = b2GetJointFullId( world, jointId );\n\tB2_ASSERT( joint->type == b2_prismaticJoint );\n\tb2JointSim* base = b2GetJointSim( world, joint );\n\tB2_ASSERT( base->type == b2_prismaticJoint );\n\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, base->bodyIdA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, base->bodyIdB );\n\tb2BodySim* bodySimA = b2GetBodySim( world, bodyA );\n\tb2BodySim* bodySimB = b2GetBodySim( world, bodyB );\n\tb2BodyState* bodyStateA = b2GetBodyState( world, bodyA );\n\tb2BodyState* bodyStateB = b2GetBodyState( world, bodyB );\n\n\tb2Transform transformA = bodySimA->transform;\n\tb2Transform transformB = bodySimB->transform;\n\n\tb2Vec2 localAxisA = b2RotateVector( base->localFrameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\tb2Vec2 axisA = b2RotateVector( transformA.q, localAxisA );\n\tb2Vec2 cA = bodySimA->center;\n\tb2Vec2 cB = bodySimB->center;\n\tb2Vec2 rA = b2RotateVector( transformA.q, b2Sub( base->localFrameA.p, bodySimA->localCenter ) );\n\tb2Vec2 rB = b2RotateVector( transformB.q, b2Sub( base->localFrameB.p, bodySimB->localCenter ) );\n\n\tb2Vec2 d = b2Add( b2Sub( cB, cA ), b2Sub( rB, rA ) );\n\n\tb2Vec2 vA = bodyStateA ? bodyStateA->linearVelocity : b2Vec2_zero;\n\tb2Vec2 vB = bodyStateB ? bodyStateB->linearVelocity : b2Vec2_zero;\n\tfloat wA = bodyStateA ? bodyStateA->angularVelocity : 0.0f;\n\tfloat wB = bodyStateB ? bodyStateB->angularVelocity : 0.0f;\n\n\tb2Vec2 vRel = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) );\n\tfloat speed = b2Dot( d, b2CrossSV( wA, axisA ) ) + b2Dot( axisA, vRel );\n\treturn speed;\n}\n\nb2Vec2 b2GetPrismaticJointForce( b2World* world, b2JointSim* base )\n{\n\tint idA = base->bodyIdA;\n\tb2Transform transformA = b2GetBodyTransform( world, idA );\n\n\tb2PrismaticJoint* joint = &base->prismaticJoint;\n\n\tb2Vec2 localAxisA = b2RotateVector( base->localFrameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\tb2Vec2 axisA = b2RotateVector( transformA.q, localAxisA );\n\tb2Vec2 perpA = b2LeftPerp( axisA );\n\n\tfloat inv_h = world->inv_h;\n\tfloat perpForce = inv_h * joint->impulse.x;\n\tfloat axialForce = inv_h * ( joint->motorImpulse + joint->lowerImpulse - joint->upperImpulse );\n\n\tb2Vec2 force = b2Add( b2MulSV( perpForce, perpA ), b2MulSV( axialForce, axisA ) );\n\treturn force;\n}\n\nfloat b2GetPrismaticJointTorque( b2World* world, b2JointSim* base )\n{\n\treturn world->inv_h * base->prismaticJoint.impulse.y;\n}\n\n// Linear constraint (point-to-line)\n// d = pB - pA = xB + rB - xA - rA\n// C = dot(perp, d)\n// Cdot = dot(d, cross(wA, perp)) + dot(perp, vB + cross(wB, rB) - vA - cross(wA, rA))\n//      = -dot(perp, vA) - dot(cross(rA + d, perp), wA) + dot(perp, vB) + dot(cross(rB, perp), vB)\n// J = [-perp, -cross(rA + d, perp), perp, cross(rB, perp)]\n//\n// Angular constraint\n// C = aB - aA + a_initial\n// Cdot = wB - wA\n// J = [0 0 -1 0 0 1]\n//\n// K = J * invM * JT\n//\n// J = [-a -sA a sB]\n//     [0  -1  0  1]\n// a = perp\n// sA = cross(rA + d, a) = cross(pB - xA, a)\n// sB = cross(rB, a) = cross(pB - xB, a)\n\n// Motor/Limit linear constraint\n// C = dot(axA, d)\n// Cdot = -dot(axA, vA) - dot(cross(rA + d, axA), wA) + dot(axA, vB) + dot(cross(rB, axA), vB)\n// J = [-axA -cross(rA + d, axA) axA cross(rB, ax1)]\n\n// Predictive limit is applied even when the limit is not active.\n// Prevents a constraint speed that can lead to a constraint error in one time step.\n// Want C2 = C1 + h * Cdot >= 0\n// Or:\n// Cdot + C1/h >= 0\n// I do not apply a negative constraint error because that is handled in position correction.\n// So:\n// Cdot + max(C1, 0)/h >= 0\n\n// Block Solver\n// We develop a block solver that includes the angular and linear constraints. This makes the limit stiffer.\n//\n// The Jacobian has 2 rows:\n// J = [-uT -s1 uT s2] // linear\n//     [0   -1   0  1] // angular\n//\n// u = perp\n// s1 = cross(d + r1, u), s2 = cross(r2, u)\n// a1 = cross(d + r1, v), a2 = cross(r2, v)\n\nvoid b2PreparePrismaticJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_prismaticJoint );\n\n\t// chase body id to the solver set where the body lives\n\tint idA = base->bodyIdA;\n\tint idB = base->bodyIdB;\n\n\tb2World* world = context->world;\n\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, idA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, idB );\n\n\tB2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet );\n\tb2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex );\n\tb2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex );\n\n\tint localIndexA = bodyA->localIndex;\n\tint localIndexB = bodyB->localIndex;\n\n\tb2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA );\n\tb2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB );\n\n\tfloat mA = bodySimA->invMass;\n\tfloat iA = bodySimA->invInertia;\n\tfloat mB = bodySimB->invMass;\n\tfloat iB = bodySimB->invInertia;\n\n\tbase->invMassA = mA;\n\tbase->invMassB = mB;\n\tbase->invIA = iA;\n\tbase->invIB = iB;\n\n\tb2PrismaticJoint* joint = &base->prismaticJoint;\n\tjoint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX;\n\tjoint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX;\n\n\t// Compute joint anchor frames with world space rotation, relative to center of mass\n\tjoint->frameA.q = b2MulRot( bodySimA->transform.q, base->localFrameA.q );\n\tjoint->frameA.p = b2RotateVector( bodySimA->transform.q, b2Sub( base->localFrameA.p, bodySimA->localCenter ) );\n\tjoint->frameB.q = b2MulRot( bodySimB->transform.q, base->localFrameB.q );\n\tjoint->frameB.p = b2RotateVector( bodySimB->transform.q, b2Sub( base->localFrameB.p, bodySimB->localCenter ) );\n\n\t// Compute the initial center delta. Incremental position updates are relative to this.\n\tjoint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center );\n\n\tjoint->springSoftness = b2MakeSoft( joint->hertz, joint->dampingRatio, context->h );\n\n\tif ( context->enableWarmStarting == false )\n\t{\n\t\tjoint->impulse = b2Vec2_zero;\n\t\tjoint->springImpulse = 0.0f;\n\t\tjoint->motorImpulse = 0.0f;\n\t\tjoint->lowerImpulse = 0.0f;\n\t\tjoint->upperImpulse = 0.0f;\n\t}\n}\n\nvoid b2WarmStartPrismaticJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_prismaticJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2PrismaticJoint* joint = &base->prismaticJoint;\n\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\tb2Vec2 d = b2Add( b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), joint->deltaCenter ), b2Sub( rB, rA ) );\n\n\tb2Vec2 axisA = b2RotateVector( joint->frameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\taxisA = b2RotateVector( stateA->deltaRotation, axisA );\n\n\t// impulse is applied at anchor point on body B\n\tfloat a1 = b2Cross( b2Add( rA, d ), axisA );\n\tfloat a2 = b2Cross( rB, axisA );\n\tfloat axialImpulse = joint->springImpulse + joint->motorImpulse + joint->lowerImpulse - joint->upperImpulse;\n\n\t// perpendicular constraint\n\tb2Vec2 perpA = b2LeftPerp( axisA );\n\tfloat s1 = b2Cross( b2Add( rA, d ), perpA );\n\tfloat s2 = b2Cross( rB, perpA );\n\tfloat perpImpulse = joint->impulse.x;\n\tfloat angleImpulse = joint->impulse.y;\n\n\tb2Vec2 P = b2Add( b2MulSV( axialImpulse, axisA ), b2MulSV( perpImpulse, perpA ) );\n\tfloat LA = axialImpulse * a1 + perpImpulse * s1 + angleImpulse;\n\tfloat LB = axialImpulse * a2 + perpImpulse * s2 + angleImpulse;\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, P );\n\t\tstateA->angularVelocity -= iA * LA;\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, P );\n\t\tstateB->angularVelocity += iB * LB;\n\t}\n}\n\nvoid b2SolvePrismaticJoint( b2JointSim* base, b2StepContext* context, bool useBias )\n{\n\tB2_ASSERT( base->type == b2_prismaticJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2PrismaticJoint* joint = &base->prismaticJoint;\n\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 vA = stateA->linearVelocity;\n\tfloat wA = stateA->angularVelocity;\n\tb2Vec2 vB = stateB->linearVelocity;\n\tfloat wB = stateB->angularVelocity;\n\n\tb2Rot qA = b2MulRot( stateA->deltaRotation, joint->frameA.q );\n\tb2Rot qB = b2MulRot( stateB->deltaRotation, joint->frameB.q );\n\tb2Rot relQ = b2InvMulRot( qA, qB );\n\n\t// current anchors\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\tb2Vec2 d = b2Add( b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), joint->deltaCenter ), b2Sub( rB, rA ) );\n\n\tb2Vec2 axisA = b2RotateVector( joint->frameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\taxisA = b2RotateVector( stateA->deltaRotation, axisA );\n\tfloat translation = b2Dot( axisA, d );\n\n\t// These scalars are for torques generated by axial forces\n\tfloat a1 = b2Cross( b2Add( rA, d ), axisA );\n\tfloat a2 = b2Cross( rB, axisA );\n\n\tfloat k = mA + mB + iA * a1 * a1 + iB * a2 * a2;\n\tfloat axialMass = k > 0.0f ? 1.0f / k : 0.0f;\n\n\tb2Softness softness = base->constraintSoftness;\n\n\t// spring constraint\n\tif ( joint->enableSpring )\n\t{\n\t\t// This is a real spring and should be applied even during relax\n\t\tfloat C = translation - joint->targetTranslation;\n\t\tfloat bias = joint->springSoftness.biasRate * C;\n\t\tfloat massScale = joint->springSoftness.massScale;\n\t\tfloat impulseScale = joint->springSoftness.impulseScale;\n\n\t\tfloat Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA;\n\t\tfloat deltaImpulse = -massScale * axialMass * ( Cdot + bias ) - impulseScale * joint->springImpulse;\n\t\tjoint->springImpulse += deltaImpulse;\n\n\t\tb2Vec2 P = b2MulSV( deltaImpulse, axisA );\n\t\tfloat LA = deltaImpulse * a1;\n\t\tfloat LB = deltaImpulse * a2;\n\n\t\tvA = b2MulSub( vA, mA, P );\n\t\twA -= iA * LA;\n\t\tvB = b2MulAdd( vB, mB, P );\n\t\twB += iB * LB;\n\t}\n\n\t// Solve motor constraint\n\tif ( joint->enableMotor )\n\t{\n\t\tfloat Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA;\n\t\tfloat impulse = axialMass * ( joint->motorSpeed - Cdot );\n\t\tfloat oldImpulse = joint->motorImpulse;\n\t\tfloat maxImpulse = context->h * joint->maxMotorForce;\n\t\tjoint->motorImpulse = b2ClampFloat( joint->motorImpulse + impulse, -maxImpulse, maxImpulse );\n\t\timpulse = joint->motorImpulse - oldImpulse;\n\n\t\tb2Vec2 P = b2MulSV( impulse, axisA );\n\t\tfloat LA = impulse * a1;\n\t\tfloat LB = impulse * a2;\n\n\t\tvA = b2MulSub( vA, mA, P );\n\t\twA -= iA * LA;\n\t\tvB = b2MulAdd( vB, mB, P );\n\t\twB += iB * LB;\n\t}\n\n\tif ( joint->enableLimit )\n\t{\n\t\t// Clamp the speculative distance to a reasonable value\n\t\tfloat speculativeDistance = 0.25f * ( joint->upperTranslation - joint->lowerTranslation );\n\n\t\t// Lower limit\n\t\t{\n\t\t\tfloat C = translation - joint->lowerTranslation;\n\n\t\t\tif ( C < speculativeDistance )\n\t\t\t{\n\t\t\t\tfloat bias = 0.0f;\n\t\t\t\tfloat massScale = 1.0f;\n\t\t\t\tfloat impulseScale = 0.0f;\n\n\t\t\t\tif ( C > 0.0f )\n\t\t\t\t{\n\t\t\t\t\t// speculation\n\t\t\t\t\tfloat safe = b2_lengthUnitsPerMeter;\n\t\t\t\t\tbias = b2MinFloat( C, safe ) * context->inv_h;\n\t\t\t\t}\n\t\t\t\telse if ( useBias )\n\t\t\t\t{\n\t\t\t\t\tbias = softness.biasRate * C;\n\t\t\t\t\tmassScale = softness.massScale;\n\t\t\t\t\timpulseScale = softness.impulseScale;\n\t\t\t\t}\n\n\t\t\t\tfloat oldImpulse = joint->lowerImpulse;\n\t\t\t\tfloat Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA;\n\t\t\t\tfloat deltaImpulse = -axialMass * massScale * ( Cdot + bias ) - impulseScale * oldImpulse;\n\t\t\t\tjoint->lowerImpulse = b2MaxFloat( oldImpulse + deltaImpulse, 0.0f );\n\t\t\t\tdeltaImpulse = joint->lowerImpulse - oldImpulse;\n\n\t\t\t\tb2Vec2 P = b2MulSV( deltaImpulse, axisA );\n\t\t\t\tfloat LA = deltaImpulse * a1;\n\t\t\t\tfloat LB = deltaImpulse * a2;\n\n\t\t\t\tvA = b2MulSub( vA, mA, P );\n\t\t\t\twA -= iA * LA;\n\t\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t\t\twB += iB * LB;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tjoint->lowerImpulse = 0.0f;\n\t\t\t}\n\t\t}\n\n\t\t// Upper limit\n\t\t// Note: signs are flipped to keep C positive when the constraint is satisfied.\n\t\t// This also keeps the impulse positive when the limit is active.\n\t\t{\n\t\t\t// sign flipped\n\t\t\tfloat C = joint->upperTranslation - translation;\n\n\t\t\tif ( C < speculativeDistance )\n\t\t\t{\n\t\t\t\tfloat bias = 0.0f;\n\t\t\t\tfloat massScale = 1.0f;\n\t\t\t\tfloat impulseScale = 0.0f;\n\n\t\t\t\tif ( C > 0.0f )\n\t\t\t\t{\n\t\t\t\t\t// speculation\n\t\t\t\t\tfloat safe = b2_lengthUnitsPerMeter;\n\t\t\t\t\tbias = b2MinFloat( C, safe ) * context->inv_h;\n\t\t\t\t}\n\t\t\t\telse if ( useBias )\n\t\t\t\t{\n\t\t\t\t\tbias = softness.biasRate * C;\n\t\t\t\t\tmassScale = softness.massScale;\n\t\t\t\t\timpulseScale = softness.impulseScale;\n\t\t\t\t}\n\n\t\t\t\tfloat oldImpulse = joint->upperImpulse;\n\n\t\t\t\t// sign flipped\n\t\t\t\tfloat Cdot = b2Dot( axisA, b2Sub( vA, vB ) ) + a1 * wA - a2 * wB;\n\t\t\t\tfloat deltaImpulse = -axialMass * massScale * ( Cdot + bias ) - impulseScale * oldImpulse;\n\t\t\t\tjoint->upperImpulse = b2MaxFloat( oldImpulse + deltaImpulse, 0.0f );\n\t\t\t\tdeltaImpulse = joint->upperImpulse - oldImpulse;\n\n\t\t\t\tb2Vec2 P = b2MulSV( deltaImpulse, axisA );\n\t\t\t\tfloat LA = deltaImpulse * a1;\n\t\t\t\tfloat LB = deltaImpulse * a2;\n\n\t\t\t\t// sign flipped\n\t\t\t\tvA = b2MulAdd( vA, mA, P );\n\t\t\t\twA += iA * LA;\n\t\t\t\tvB = b2MulSub( vB, mB, P );\n\t\t\t\twB -= iB * LB;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tjoint->upperImpulse = 0.0f;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Solve the prismatic constraint in block form\n\t{\n\t\tb2Vec2 perpA = b2LeftPerp( axisA );\n\n\t\t// These scalars are for torques generated by the perpendicular constraint force\n\t\tfloat s1 = b2Cross( b2Add( d, rA ), perpA );\n\t\tfloat s2 = b2Cross( rB, perpA );\n\n\t\tb2Vec2 Cdot;\n\t\tCdot.x = b2Dot( perpA, b2Sub( vB, vA ) ) + s2 * wB - s1 * wA;\n\t\tCdot.y = wB - wA;\n\n\t\tb2Vec2 bias = b2Vec2_zero;\n\t\tfloat massScale = 1.0f;\n\t\tfloat impulseScale = 0.0f;\n\t\tif ( useBias )\n\t\t{\n\t\t\tb2Vec2 C;\n\t\t\tC.x = b2Dot( perpA, d );\n\t\t\tC.y = b2Rot_GetAngle( relQ );\n\n\t\t\tbias = b2MulSV( softness.biasRate, C );\n\t\t\tmassScale = softness.massScale;\n\t\t\timpulseScale = softness.impulseScale;\n\t\t}\n\n\t\tfloat k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2;\n\t\tfloat k12 = iA * s1 + iB * s2;\n\t\tfloat k22 = iA + iB;\n\t\tif ( k22 == 0.0f )\n\t\t{\n\t\t\t// For bodies with fixed rotation.\n\t\t\tk22 = 1.0f;\n\t\t}\n\n\t\tb2Mat22 K = { { k11, k12 }, { k12, k22 } };\n\n\t\tb2Vec2 b = b2Solve22( K, b2Add( Cdot, bias ) );\n\t\tb2Vec2 deltaImpulse;\n\t\tdeltaImpulse.x = -massScale * b.x - impulseScale * joint->impulse.x;\n\t\tdeltaImpulse.y = -massScale * b.y - impulseScale * joint->impulse.y;\n\n\t\tjoint->impulse.x += deltaImpulse.x;\n\t\tjoint->impulse.y += deltaImpulse.y;\n\n\t\tb2Vec2 P = b2MulSV( deltaImpulse.x, perpA );\n\t\tfloat LA = deltaImpulse.x * s1 + deltaImpulse.y;\n\t\tfloat LB = deltaImpulse.x * s2 + deltaImpulse.y;\n\n\t\tvA = b2MulSub( vA, mA, P );\n\t\twA -= iA * LA;\n\t\tvB = b2MulAdd( vB, mB, P );\n\t\twB += iB * LB;\n\t}\n\n\tB2_ASSERT( b2IsValidVec2( vA ) );\n\tB2_ASSERT( b2IsValidFloat( wA ) );\n\tB2_ASSERT( b2IsValidVec2( vB ) );\n\tB2_ASSERT( b2IsValidFloat( wB ) );\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = vA;\n\t\tstateA->angularVelocity = wA;\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = vB;\n\t\tstateB->angularVelocity = wB;\n\t}\n}\n\n#if 0\nvoid b2PrismaticJoint::Dump()\n{\n\tint32 indexA = joint->bodyA->joint->islandIndex;\n\tint32 indexB = joint->bodyB->joint->islandIndex;\n\n\tb2Dump(\"  b2PrismaticJointDef jd;\\n\");\n\tb2Dump(\"  jd.bodyA = sims[%d];\\n\", indexA);\n\tb2Dump(\"  jd.bodyB = sims[%d];\\n\", indexB);\n\tb2Dump(\"  jd.collideConnected = bool(%d);\\n\", joint->collideConnected);\n\tb2Dump(\"  jd.localAnchorA.Set(%.9g, %.9g);\\n\", joint->localAnchorA.x, joint->localAnchorA.y);\n\tb2Dump(\"  jd.localAnchorB.Set(%.9g, %.9g);\\n\", joint->localAnchorB.x, joint->localAnchorB.y);\n\tb2Dump(\"  jd.referenceAngle = %.9g;\\n\", joint->referenceAngle);\n\tb2Dump(\"  jd.enableLimit = bool(%d);\\n\", joint->enableLimit);\n\tb2Dump(\"  jd.lowerAngle = %.9g;\\n\", joint->lowerAngle);\n\tb2Dump(\"  jd.upperAngle = %.9g;\\n\", joint->upperAngle);\n\tb2Dump(\"  jd.enableMotor = bool(%d);\\n\", joint->enableMotor);\n\tb2Dump(\"  jd.motorSpeed = %.9g;\\n\", joint->motorSpeed);\n\tb2Dump(\"  jd.maxMotorTorque = %.9g;\\n\", joint->maxMotorTorque);\n\tb2Dump(\"  joints[%d] = joint->world->CreateJoint(&jd);\\n\", joint->index);\n}\n#endif\n\nvoid b2DrawPrismaticJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, float drawScale )\n{\n\tB2_ASSERT( base->type == b2_prismaticJoint );\n\n\tb2PrismaticJoint* joint = &base->prismaticJoint;\n\n\tb2Transform frameA = b2MulTransforms( transformA, base->localFrameA );\n\tb2Transform frameB = b2MulTransforms( transformB, base->localFrameB );\n\tb2Vec2 axisA = b2RotateVector( frameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\n\tdraw->DrawLineFcn( frameA.p, frameB.p, b2_colorDimGray, draw->context );\n\n\tif ( joint->enableLimit )\n\t{\n\t\tfloat b = 0.25f * drawScale;\n\t\tb2Vec2 lower = b2MulAdd( frameA.p, joint->lowerTranslation, axisA );\n\t\tb2Vec2 upper = b2MulAdd( frameA.p, joint->upperTranslation, axisA );\n\t\tb2Vec2 perp = b2LeftPerp( axisA );\n\t\tdraw->DrawLineFcn( lower, upper, b2_colorGray, draw->context );\n\t\tdraw->DrawLineFcn( b2MulSub( lower, b, perp ), b2MulAdd( lower, b, perp ), b2_colorGreen, draw->context );\n\t\tdraw->DrawLineFcn( b2MulSub( upper, b, perp ), b2MulAdd( upper, b, perp ), b2_colorRed, draw->context );\n\t}\n\telse\n\t{\n\t\tdraw->DrawLineFcn( b2MulSub( frameA.p, 1.0f, axisA ), b2MulAdd( frameA.p, 1.0f, axisA ), b2_colorGray, draw->context );\n\t}\n\n\tif ( joint->enableSpring )\n\t{\n\t\tb2Vec2 p = b2MulAdd( frameA.p, joint->targetTranslation, axisA );\n\t\tdraw->DrawPointFcn( p, 8.0f, b2_colorViolet, draw->context );\n\t}\n\n\tdraw->DrawPointFcn( frameA.p, 5.0f, b2_colorGray, draw->context );\n\tdraw->DrawPointFcn( frameB.p, 5.0f, b2_colorBlue, draw->context );\n}\n"
  },
  {
    "path": "src/revolute_joint.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"body.h\"\n#include \"core.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"solver.h\"\n#include \"solver_set.h\"\n\n// needed for dll export\n#include \"box2d/box2d.h\"\n\n#include <stdio.h>\n\n// Point-to-point constraint\n// C = pB - pA\n// Cdot = vB - vA\n//      = vB + cross(wB, rB) - vA - cross(wA, rA)\n// J = [-E -skew(rA) E skew(rB) ]\n\n// Identity used:\n// w k % (rx i + ry j) = w * (-ry i + rx j)\n\n// Motor constraint\n// Cdot = wB - wA\n// J = [0 0 -1 0 0 1]\n// K = invIA + invIB\n\nvoid b2RevoluteJoint_EnableSpring( b2JointId jointId, bool enableSpring )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tif ( enableSpring != joint->revoluteJoint.enableSpring )\n\t{\n\t\tjoint->revoluteJoint.enableSpring = enableSpring;\n\t\tjoint->revoluteJoint.springImpulse = 0.0f;\n\t}\n}\n\nbool b2RevoluteJoint_IsSpringEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.enableSpring;\n}\n\nvoid b2RevoluteJoint_SetSpringHertz( b2JointId jointId, float hertz )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tjoint->revoluteJoint.hertz = hertz;\n}\n\nfloat b2RevoluteJoint_GetSpringHertz( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.hertz;\n}\n\nvoid b2RevoluteJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tjoint->revoluteJoint.dampingRatio = dampingRatio;\n}\n\nfloat b2RevoluteJoint_GetSpringDampingRatio( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.dampingRatio;\n}\n\nvoid b2RevoluteJoint_SetTargetAngle( b2JointId jointId, float angle )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tjoint->revoluteJoint.targetAngle = angle;\n}\n\nfloat b2RevoluteJoint_GetTargetAngle( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.targetAngle;\n}\n\nfloat b2RevoluteJoint_GetAngle( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2JointSim* jointSim = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tb2Transform transformA = b2GetBodyTransform( world, jointSim->bodyIdA );\n\tb2Transform transformB = b2GetBodyTransform( world, jointSim->bodyIdB );\n\tb2Rot qA = b2MulRot( transformA.q, jointSim->localFrameA.q );\n\tb2Rot qB = b2MulRot( transformB.q, jointSim->localFrameB.q );\n\n\tfloat angle = b2RelativeAngle( qA, qB );\n\treturn angle;\n}\n\nvoid b2RevoluteJoint_EnableLimit( b2JointId jointId, bool enableLimit )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tif ( enableLimit != joint->revoluteJoint.enableLimit )\n\t{\n\t\tjoint->revoluteJoint.enableLimit = enableLimit;\n\t\tjoint->revoluteJoint.lowerImpulse = 0.0f;\n\t\tjoint->revoluteJoint.upperImpulse = 0.0f;\n\t}\n}\n\nbool b2RevoluteJoint_IsLimitEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.enableLimit;\n}\n\nfloat b2RevoluteJoint_GetLowerLimit( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.lowerAngle;\n}\n\nfloat b2RevoluteJoint_GetUpperLimit( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.upperAngle;\n}\n\nvoid b2RevoluteJoint_SetLimits( b2JointId jointId, float lower, float upper )\n{\n\tB2_ASSERT( lower <= upper );\n\tB2_ASSERT( lower >= -0.99f * B2_PI );\n\tB2_ASSERT( upper <= 0.99f * B2_PI );\n\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tif ( lower != joint->revoluteJoint.lowerAngle || upper != joint->revoluteJoint.upperAngle )\n\t{\n\t\tjoint->revoluteJoint.lowerAngle = b2MinFloat( lower, upper );\n\t\tjoint->revoluteJoint.upperAngle = b2MaxFloat( lower, upper );\n\t\tjoint->revoluteJoint.lowerImpulse = 0.0f;\n\t\tjoint->revoluteJoint.upperImpulse = 0.0f;\n\t}\n}\n\nvoid b2RevoluteJoint_EnableMotor( b2JointId jointId, bool enableMotor )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tif ( enableMotor != joint->revoluteJoint.enableMotor )\n\t{\n\t\tjoint->revoluteJoint.enableMotor = enableMotor;\n\t\tjoint->revoluteJoint.motorImpulse = 0.0f;\n\t}\n}\n\nbool b2RevoluteJoint_IsMotorEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.enableMotor;\n}\n\nvoid b2RevoluteJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tjoint->revoluteJoint.motorSpeed = motorSpeed;\n}\n\nfloat b2RevoluteJoint_GetMotorSpeed( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.motorSpeed;\n}\n\nfloat b2RevoluteJoint_GetMotorTorque( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn world->inv_h * joint->revoluteJoint.motorImpulse;\n}\n\nvoid b2RevoluteJoint_SetMaxMotorTorque( b2JointId jointId, float torque )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\tjoint->revoluteJoint.maxMotorTorque = torque;\n}\n\nfloat b2RevoluteJoint_GetMaxMotorTorque( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint );\n\treturn joint->revoluteJoint.maxMotorTorque;\n}\n\nb2Vec2 b2GetRevoluteJointForce( b2World* world, b2JointSim* base )\n{\n\tb2Vec2 force = b2MulSV( world->inv_h, base->revoluteJoint.linearImpulse );\n\treturn force;\n}\n\nfloat b2GetRevoluteJointTorque( b2World* world, b2JointSim* base )\n{\n\tconst b2RevoluteJoint* revolute = &base->revoluteJoint;\n\tfloat torque = world->inv_h * ( revolute->motorImpulse + revolute->lowerImpulse - revolute->upperImpulse );\n\treturn torque;\n}\n\nvoid b2PrepareRevoluteJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_revoluteJoint );\n\n\t// chase body id to the solver set where the body lives\n\tint idA = base->bodyIdA;\n\tint idB = base->bodyIdB;\n\n\tb2World* world = context->world;\n\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, idA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, idB );\n\n\tB2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet );\n\tb2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex );\n\tb2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex );\n\n\tint localIndexA = bodyA->localIndex;\n\tint localIndexB = bodyB->localIndex;\n\n\tb2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA );\n\tb2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB );\n\n\tfloat mA = bodySimA->invMass;\n\tfloat iA = bodySimA->invInertia;\n\tfloat mB = bodySimB->invMass;\n\tfloat iB = bodySimB->invInertia;\n\n\tbase->invMassA = mA;\n\tbase->invMassB = mB;\n\tbase->invIA = iA;\n\tbase->invIB = iB;\n\n\tb2RevoluteJoint* joint = &base->revoluteJoint;\n\n\tjoint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX;\n\tjoint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX;\n\n\t// Compute joint anchor frames with world space rotation, relative to center of mass.\n\t// Avoid round-off here as much as possible.\n\t// b2Vec2 pf = (xf.p - c) + rot(xf.q, f.p)\n\t// pf = xf.p - (xf.p + rot(xf.q, lc)) + rot(xf.q, f.p)\n\t// pf = rot(xf.q, f.p - lc)\n\tjoint->frameA.q = b2MulRot( bodySimA->transform.q, base->localFrameA.q );\n\tjoint->frameA.p = b2RotateVector( bodySimA->transform.q, b2Sub( base->localFrameA.p, bodySimA->localCenter ) );\n\tjoint->frameB.q = b2MulRot( bodySimB->transform.q, base->localFrameB.q );\n\tjoint->frameB.p = b2RotateVector( bodySimB->transform.q, b2Sub( base->localFrameB.p, bodySimB->localCenter ) );\n\n\t// Compute the initial center delta. Incremental position updates are relative to this.\n\tjoint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center );\n\n\tfloat k = iA + iB;\n\tjoint->axialMass = k > 0.0f ? 1.0f / k : 0.0f;\n\n\tjoint->springSoftness = b2MakeSoft( joint->hertz, joint->dampingRatio, context->h );\n\n\tif ( context->enableWarmStarting == false )\n\t{\n\t\tjoint->linearImpulse = b2Vec2_zero;\n\t\tjoint->springImpulse = 0.0f;\n\t\tjoint->motorImpulse = 0.0f;\n\t\tjoint->lowerImpulse = 0.0f;\n\t\tjoint->upperImpulse = 0.0f;\n\t}\n}\n\nvoid b2WarmStartRevoluteJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_revoluteJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2RevoluteJoint* joint = &base->revoluteJoint;\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\tfloat axialImpulse = joint->springImpulse + joint->motorImpulse + joint->lowerImpulse - joint->upperImpulse;\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, joint->linearImpulse );\n\t\tstateA->angularVelocity -= iA * ( b2Cross( rA, joint->linearImpulse ) + axialImpulse );\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, joint->linearImpulse );\n\t\tstateB->angularVelocity += iB * ( b2Cross( rB, joint->linearImpulse ) + axialImpulse );\n\t}\n}\n\nvoid b2SolveRevoluteJoint( b2JointSim* base, b2StepContext* context, bool useBias )\n{\n\tB2_ASSERT( base->type == b2_revoluteJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2RevoluteJoint* joint = &base->revoluteJoint;\n\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 vA = stateA->linearVelocity;\n\tfloat wA = stateA->angularVelocity;\n\tb2Vec2 vB = stateB->linearVelocity;\n\tfloat wB = stateB->angularVelocity;\n\n\tb2Rot qA = b2MulRot( stateA->deltaRotation, joint->frameA.q );\n\tb2Rot qB = b2MulRot( stateB->deltaRotation, joint->frameB.q );\n\tb2Rot relQ = b2InvMulRot( qA, qB );\n\n\tbool fixedRotation = ( iA + iB == 0.0f );\n\n\t// Solve spring.\n\tif ( joint->enableSpring && fixedRotation == false )\n\t{\n\t\tfloat jointAngle = b2Rot_GetAngle( relQ );\n\t\tfloat jointAngleDelta = b2UnwindAngle( jointAngle - joint->targetAngle );\n\n\t\tfloat C = jointAngleDelta;\n\t\tfloat bias = joint->springSoftness.biasRate * C;\n\t\tfloat massScale = joint->springSoftness.massScale;\n\t\tfloat impulseScale = joint->springSoftness.impulseScale;\n\n\t\tfloat Cdot = wB - wA;\n\t\tfloat impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->springImpulse;\n\t\tjoint->springImpulse += impulse;\n\n\t\twA -= iA * impulse;\n\t\twB += iB * impulse;\n\t}\n\n\t// Solve motor constraint.\n\tif ( joint->enableMotor && fixedRotation == false )\n\t{\n\t\tfloat Cdot = wB - wA - joint->motorSpeed;\n\t\tfloat impulse = -joint->axialMass * Cdot;\n\t\tfloat oldImpulse = joint->motorImpulse;\n\t\tfloat maxImpulse = context->h * joint->maxMotorTorque;\n\t\tjoint->motorImpulse = b2ClampFloat( joint->motorImpulse + impulse, -maxImpulse, maxImpulse );\n\t\timpulse = joint->motorImpulse - oldImpulse;\n\n\t\twA -= iA * impulse;\n\t\twB += iB * impulse;\n\t}\n\n\tif ( joint->enableLimit && fixedRotation == false )\n\t{\n\t\tfloat jointAngle = b2Rot_GetAngle( relQ );\n\n\t\t// Lower limit\n\t\t{\n\t\t\tfloat C = jointAngle - joint->lowerAngle;\n\t\t\tfloat bias = 0.0f;\n\t\t\tfloat massScale = 1.0f;\n\t\t\tfloat impulseScale = 0.0f;\n\t\t\tif ( C > 0.0f )\n\t\t\t{\n\t\t\t\t// speculation\n\t\t\t\tbias = C * context->inv_h;\n\t\t\t}\n\t\t\telse if ( useBias )\n\t\t\t{\n\t\t\t\tbias = base->constraintSoftness.biasRate * C;\n\t\t\t\tmassScale = base->constraintSoftness.massScale;\n\t\t\t\timpulseScale = base->constraintSoftness.impulseScale;\n\t\t\t}\n\n\t\t\tfloat Cdot = wB - wA;\n\t\t\tfloat oldImpulse = joint->lowerImpulse;\n\t\t\tfloat impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * oldImpulse;\n\t\t\tjoint->lowerImpulse = b2MaxFloat( oldImpulse + impulse, 0.0f );\n\t\t\timpulse = joint->lowerImpulse - oldImpulse;\n\n\t\t\twA -= iA * impulse;\n\t\t\twB += iB * impulse;\n\t\t}\n\n\t\t// Upper limit\n\t\t// Note: signs are flipped to keep C positive when the constraint is satisfied.\n\t\t// This also keeps the impulse positive when the limit is active.\n\t\t{\n\t\t\tfloat C = joint->upperAngle - jointAngle;\n\t\t\tfloat bias = 0.0f;\n\t\t\tfloat massScale = 1.0f;\n\t\t\tfloat impulseScale = 0.0f;\n\t\t\tif ( C > 0.0f )\n\t\t\t{\n\t\t\t\t// speculation\n\t\t\t\tbias = C * context->inv_h;\n\t\t\t}\n\t\t\telse if ( useBias )\n\t\t\t{\n\t\t\t\tbias = base->constraintSoftness.biasRate * C;\n\t\t\t\tmassScale = base->constraintSoftness.massScale;\n\t\t\t\timpulseScale = base->constraintSoftness.impulseScale;\n\t\t\t}\n\n\t\t\t// sign flipped on Cdot\n\t\t\tfloat Cdot = wA - wB;\n\t\t\tfloat oldImpulse = joint->upperImpulse;\n\t\t\tfloat impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * oldImpulse;\n\t\t\tjoint->upperImpulse = b2MaxFloat( oldImpulse + impulse, 0.0f );\n\t\t\timpulse = joint->upperImpulse - oldImpulse;\n\n\t\t\t// sign flipped on applied impulse\n\t\t\twA += iA * impulse;\n\t\t\twB -= iB * impulse;\n\t\t}\n\t}\n\n\t// Solve point-to-point constraint\n\t{\n\t\t// J = [-I -r1_skew I r2_skew]\n\t\t// r_skew = [-ry; rx]\n\t\t// K = [ mA+r1y^2*iA+mB+r2y^2*iB,  -r1y*iA*r1x-r2y*iB*r2x]\n\t\t//     [  -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB]\n\n\t\t// current anchors\n\t\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\t\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\t\tb2Vec2 Cdot = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) );\n\n\t\tb2Vec2 bias = b2Vec2_zero;\n\t\tfloat massScale = 1.0f;\n\t\tfloat impulseScale = 0.0f;\n\t\tif ( useBias )\n\t\t{\n\t\t\tb2Vec2 dcA = stateA->deltaPosition;\n\t\t\tb2Vec2 dcB = stateB->deltaPosition;\n\n\t\t\tb2Vec2 separation = b2Add( b2Add( b2Sub( dcB, dcA ), b2Sub( rB, rA ) ), joint->deltaCenter );\n\t\t\tbias = b2MulSV( base->constraintSoftness.biasRate, separation );\n\t\t\tmassScale = base->constraintSoftness.massScale;\n\t\t\timpulseScale = base->constraintSoftness.impulseScale;\n\t\t}\n\n\t\tb2Mat22 K;\n\t\tK.cx.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB;\n\t\tK.cy.x = -rA.y * rA.x * iA - rB.y * rB.x * iB;\n\t\tK.cx.y = K.cy.x;\n\t\tK.cy.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB;\n\t\tb2Vec2 b = b2Solve22( K, b2Add( Cdot, bias ) );\n\n\t\tb2Vec2 impulse;\n\t\timpulse.x = -massScale * b.x - impulseScale * joint->linearImpulse.x;\n\t\timpulse.y = -massScale * b.y - impulseScale * joint->linearImpulse.y;\n\t\tjoint->linearImpulse.x += impulse.x;\n\t\tjoint->linearImpulse.y += impulse.y;\n\n\t\tvA = b2MulSub( vA, mA, impulse );\n\t\twA -= iA * b2Cross( rA, impulse );\n\t\tvB = b2MulAdd( vB, mB, impulse );\n\t\twB += iB * b2Cross( rB, impulse );\n\t}\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = vA;\n\t\tstateA->angularVelocity = wA;\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = vB;\n\t\tstateB->angularVelocity = wB;\n\t}\n}\n\n#if 0\nvoid b2RevoluteJoint::Dump()\n{\n\tint32 indexA = joint->bodyA->joint->islandIndex;\n\tint32 indexB = joint->bodyB->joint->islandIndex;\n\n\tb2Dump(\"  b2RevoluteJointDef jd;\\n\");\n\tb2Dump(\"  jd.bodyA = bodies[%d];\\n\", indexA);\n\tb2Dump(\"  jd.bodyB = bodies[%d];\\n\", indexB);\n\tb2Dump(\"  jd.collideConnected = bool(%d);\\n\", joint->collideConnected);\n\tb2Dump(\"  jd.localAnchorA.Set(%.9g, %.9g);\\n\", joint->localAnchorA.x, joint->localAnchorA.y);\n\tb2Dump(\"  jd.localAnchorB.Set(%.9g, %.9g);\\n\", joint->localAnchorB.x, joint->localAnchorB.y);\n\tb2Dump(\"  jd.referenceAngle = %.9g;\\n\", joint->referenceAngle);\n\tb2Dump(\"  jd.enableLimit = bool(%d);\\n\", joint->enableLimit);\n\tb2Dump(\"  jd.lowerAngle = %.9g;\\n\", joint->lowerAngle);\n\tb2Dump(\"  jd.upperAngle = %.9g;\\n\", joint->upperAngle);\n\tb2Dump(\"  jd.enableMotor = bool(%d);\\n\", joint->enableMotor);\n\tb2Dump(\"  jd.motorSpeed = %.9g;\\n\", joint->motorSpeed);\n\tb2Dump(\"  jd.maxMotorTorque = %.9g;\\n\", joint->maxMotorTorque);\n\tb2Dump(\"  joints[%d] = joint->world->CreateJoint(&jd);\\n\", joint->index);\n}\n#endif\n\nvoid b2DrawRevoluteJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, float drawScale )\n{\n\tB2_ASSERT( base->type == b2_revoluteJoint );\n\n\tb2RevoluteJoint* joint = &base->revoluteJoint;\n\n\tb2Transform frameA = b2MulTransforms( transformA, base->localFrameA );\n\tb2Transform frameB = b2MulTransforms( transformB, base->localFrameB );\n\n\tconst float radius = 0.25f * drawScale;\n\tdraw->DrawCircleFcn( frameB.p, radius, b2_colorGray, draw->context );\n\n\tb2Vec2 rx = { radius, 0.0f };\n\tb2Vec2 r = b2RotateVector( frameA.q, rx );\n\tdraw->DrawLineFcn( frameA.p, b2Add( frameA.p, r ), b2_colorGray, draw->context );\n\n\tr = b2RotateVector( frameB.q, rx );\n\tdraw->DrawLineFcn( frameB.p, b2Add( frameB.p, r ), b2_colorBlue, draw->context );\n\n\tif ( draw->drawJointExtras )\n\t{\n\t\tfloat jointAngle = b2RelativeAngle( frameA.q, frameB.q );\n\t\tchar buffer[32];\n\t\tsnprintf( buffer, 32, \" %.1f deg\", 180.0f * jointAngle / B2_PI );\n\t\tdraw->DrawStringFcn( b2Add( frameA.p, r ), buffer, b2_colorWhite, draw->context );\n\t}\n\n\tfloat lowerAngle = joint->lowerAngle;\n\tfloat upperAngle = joint->upperAngle;\n\n\tif ( joint->enableLimit )\n\t{\n\t\tb2Rot rotLo = b2MulRot( frameA.q, b2MakeRot( lowerAngle ) );\n\t\tb2Vec2 rlo = b2RotateVector( rotLo, rx );\n\n\t\tb2Rot rotHi = b2MulRot( frameA.q, b2MakeRot( upperAngle ) );\n\t\tb2Vec2 rhi = b2RotateVector( rotHi, rx );\n\n\t\tdraw->DrawLineFcn( frameB.p, b2Add( frameB.p, rlo ), b2_colorGreen, draw->context );\n\t\tdraw->DrawLineFcn( frameB.p, b2Add( frameB.p, rhi ), b2_colorRed, draw->context );\n\t}\n\n\tif ( joint->enableSpring )\n\t{\n\t\tb2Rot q = b2MulRot( frameA.q, b2MakeRot( joint->targetAngle ) );\n\t\tb2Vec2 v = b2RotateVector( q, rx );\n\t\tdraw->DrawLineFcn( frameB.p, b2Add( frameB.p, v ), b2_colorViolet, draw->context );\n\t}\n\n\tb2HexColor color = b2_colorGold;\n\tdraw->DrawLineFcn( transformA.p, frameA.p, color, draw->context );\n\tdraw->DrawLineFcn( frameA.p, frameB.p, color, draw->context );\n\tdraw->DrawLineFcn( transformB.p, frameB.p, color, draw->context );\n\n\t// char buffer[32];\n\t// sprintf(buffer, \"%.1f\", b2Length(joint->impulse));\n\t// draw->DrawString(pA, buffer, draw->context);\n}\n"
  },
  {
    "path": "src/sensor.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"sensor.h\"\n\n#include \"array.h\"\n#include \"body.h\"\n#include \"contact.h\"\n#include \"ctz.h\"\n#include \"physics_world.h\"\n#include \"shape.h\"\n#include \"solver_set.h\"\n\n#include \"box2d/collision.h\"\n\n#include <stddef.h>\n#include <stdlib.h>\n\nB2_ARRAY_SOURCE( b2Visitor, b2Visitor )\nB2_ARRAY_SOURCE( b2Sensor, b2Sensor )\nB2_ARRAY_SOURCE( b2SensorTaskContext, b2SensorTaskContext )\nB2_ARRAY_SOURCE( b2SensorHit, b2SensorHit )\n\nstruct b2SensorQueryContext\n{\n\tb2World* world;\n\tb2SensorTaskContext* taskContext;\n\tb2Sensor* sensor;\n\tb2Shape* sensorShape;\n\tb2Transform transform;\n};\n\n// Sensor shapes need to\n// - detect begin and end overlap events\n// - events must be reported in deterministic order\n// - maintain an active list of overlaps for query\n\n// Assumption\n// - sensors don't detect shapes on the same body\n\n// Algorithm\n// Query all sensors for overlaps\n// Check against previous overlaps\n\n// Data structures\n// Each sensor has an double buffered array of overlaps\n// These overlaps use a shape reference with index and generation\n\nstatic bool b2SensorQueryCallback( int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\n\tstruct b2SensorQueryContext* queryContext = context;\n\tb2Shape* sensorShape = queryContext->sensorShape;\n\tint sensorShapeId = sensorShape->id;\n\n\tif ( shapeId == sensorShapeId )\n\t{\n\t\treturn true;\n\t}\n\n\tb2World* world = queryContext->world;\n\tb2Shape* otherShape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\t// Are sensor events enabled on the other shape?\n\tif ( otherShape->enableSensorEvents == false )\n\t{\n\t\treturn true;\n\t}\n\n\t// Skip shapes on the same body\n\tif ( otherShape->bodyId == sensorShape->bodyId )\n\t{\n\t\treturn true;\n\t}\n\n\t// Check filter\n\tif ( b2ShouldShapesCollide( sensorShape->filter, otherShape->filter ) == false )\n\t{\n\t\treturn true;\n\t}\n\n\t// Custom user filter\n\tif ( sensorShape->enableCustomFiltering || otherShape->enableCustomFiltering )\n\t{\n\t\tb2CustomFilterFcn* customFilterFcn = queryContext->world->customFilterFcn;\n\t\tif ( customFilterFcn != NULL )\n\t\t{\n\t\t\tb2ShapeId idA = { sensorShapeId + 1, world->worldId, sensorShape->generation };\n\t\t\tb2ShapeId idB = { shapeId + 1, world->worldId, otherShape->generation };\n\t\t\tbool shouldCollide = customFilterFcn( idA, idB, queryContext->world->customFilterContext );\n\t\t\tif ( shouldCollide == false )\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\tb2Transform otherTransform = b2GetBodyTransform( world, otherShape->bodyId );\n\n\tb2DistanceInput input;\n\tinput.proxyA = b2MakeShapeDistanceProxy( sensorShape );\n\tinput.proxyB = b2MakeShapeDistanceProxy( otherShape );\n\tinput.transformA = queryContext->transform;\n\tinput.transformB = otherTransform;\n\tinput.useRadii = true;\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput output = b2ShapeDistance( &input, &cache, NULL, 0 );\n\n\tbool overlaps = output.distance < 10.0f * FLT_EPSILON;\n\tif ( overlaps == false )\n\t{\n\t\treturn true;\n\t}\n\n\t// Record the overlap\n\tb2Sensor* sensor = queryContext->sensor;\n\tb2Visitor* shapeRef = b2VisitorArray_Add( &sensor->overlaps2 );\n\tshapeRef->shapeId = shapeId;\n\tshapeRef->generation = otherShape->generation;\n\n\treturn true;\n}\n\nstatic int b2CompareVisitors( const void* a, const void* b )\n{\n\tconst b2Visitor* sa = a;\n\tconst b2Visitor* sb = b;\n\n\tif ( sa->shapeId < sb->shapeId )\n\t{\n\t\treturn -1;\n\t}\n\n\treturn 1;\n}\n\nstatic void b2SensorTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )\n{\n\tb2TracyCZoneNC( sensor_task, \"Overlap\", b2_colorBrown, true );\n\n\tb2World* world = context;\n\tB2_ASSERT( (int)threadIndex < world->workerCount );\n\tb2SensorTaskContext* taskContext = world->sensorTaskContexts.data + threadIndex;\n\n\tB2_ASSERT( startIndex < endIndex );\n\n\tb2DynamicTree* trees = world->broadPhase.trees;\n\tfor ( int sensorIndex = startIndex; sensorIndex < endIndex; ++sensorIndex )\n\t{\n\t\tb2Sensor* sensor = b2SensorArray_Get( &world->sensors, sensorIndex );\n\t\tb2Shape* sensorShape = b2ShapeArray_Get( &world->shapes, sensor->shapeId );\n\n\t\t// Swap overlap arrays\n\t\tb2VisitorArray temp = sensor->overlaps1;\n\t\tsensor->overlaps1 = sensor->overlaps2;\n\t\tsensor->overlaps2 = temp;\n\t\tb2VisitorArray_Clear( &sensor->overlaps2 );\n\n\t\t// Append sensor hits\n\t\tint hitCount = sensor->hits.count;\n\t\tfor ( int i = 0; i < hitCount; ++i )\n\t\t{\n\t\t\tb2VisitorArray_Push( &sensor->overlaps2, sensor->hits.data[i] );\n\t\t}\n\n\t\t// Clear the hits\n\t\tb2VisitorArray_Clear( &sensor->hits );\n\n\t\tb2Body* body = b2BodyArray_Get( &world->bodies, sensorShape->bodyId );\n\t\tif ( body->setIndex == b2_disabledSet || sensorShape->enableSensorEvents == false )\n\t\t{\n\t\t\tif ( sensor->overlaps1.count != 0 )\n\t\t\t{\n\t\t\t\t// This sensor is dropping all overlaps because it has been disabled.\n\t\t\t\tb2SetBit( &taskContext->eventBits, sensorIndex );\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\t\tstruct b2SensorQueryContext queryContext = {\n\t\t\t.world = world,\n\t\t\t.taskContext = taskContext,\n\t\t\t.sensor = sensor,\n\t\t\t.sensorShape = sensorShape,\n\t\t\t.transform = transform,\n\t\t};\n\n\t\tB2_ASSERT( sensorShape->sensorIndex == sensorIndex );\n\t\tb2AABB queryBounds = sensorShape->aabb;\n\n\t\t// Query all trees\n\t\tb2DynamicTree_Query( trees + 0, queryBounds, sensorShape->filter.maskBits, b2SensorQueryCallback, &queryContext );\n\t\tb2DynamicTree_Query( trees + 1, queryBounds, sensorShape->filter.maskBits, b2SensorQueryCallback, &queryContext );\n\t\tb2DynamicTree_Query( trees + 2, queryBounds, sensorShape->filter.maskBits, b2SensorQueryCallback, &queryContext );\n\n\t\t// Sort the overlaps to enable finding begin and end events.\n\t\tqsort( sensor->overlaps2.data, sensor->overlaps2.count, sizeof( b2Visitor ), b2CompareVisitors );\n\n\t\t// Remove duplicates from overlaps2 (sorted). Duplicates are possible due to the hit events appended earlier.\n\t\tint uniqueCount = 0;\n\t\tint overlapCount = sensor->overlaps2.count;\n\t\tb2Visitor* overlapData = sensor->overlaps2.data;\n\t\tfor ( int i = 0; i < overlapCount; ++i )\n\t\t{\n\t\t\tif ( uniqueCount == 0 || overlapData[i].shapeId != overlapData[uniqueCount - 1].shapeId )\n\t\t\t{\n\t\t\t\toverlapData[uniqueCount] = overlapData[i];\n\t\t\t\tuniqueCount += 1;\n\t\t\t}\n\t\t}\n\t\tsensor->overlaps2.count = uniqueCount;\n\n\t\tint count1 = sensor->overlaps1.count;\n\t\tint count2 = sensor->overlaps2.count;\n\t\tif ( count1 != count2 )\n\t\t{\n\t\t\t// something changed\n\t\t\tb2SetBit( &taskContext->eventBits, sensorIndex );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor ( int i = 0; i < count1; ++i )\n\t\t\t{\n\t\t\t\tb2Visitor* s1 = sensor->overlaps1.data + i;\n\t\t\t\tb2Visitor* s2 = sensor->overlaps2.data + i;\n\n\t\t\t\tif ( s1->shapeId != s2->shapeId || s1->generation != s2->generation )\n\t\t\t\t{\n\t\t\t\t\t// something changed\n\t\t\t\t\tb2SetBit( &taskContext->eventBits, sensorIndex );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( sensor_task );\n}\n\nvoid b2OverlapSensors( b2World* world )\n{\n\tint sensorCount = world->sensors.count;\n\tif ( sensorCount == 0 )\n\t{\n\t\treturn;\n\t}\n\n\tB2_ASSERT( world->workerCount > 0 );\n\n\tb2TracyCZoneNC( overlap_sensors, \"Sensors\", b2_colorMediumPurple, true );\n\n\tfor ( int i = 0; i < world->workerCount; ++i )\n\t{\n\t\tb2SetBitCountAndClear( &world->sensorTaskContexts.data[i].eventBits, sensorCount );\n\t}\n\n\t// Parallel-for sensors overlaps\n\tint minRange = 16;\n\tvoid* userSensorTask = world->enqueueTaskFcn( &b2SensorTask, sensorCount, minRange, world, world->userTaskContext );\n\tworld->taskCount += 1;\n\tif ( userSensorTask != NULL )\n\t{\n\t\tworld->finishTaskFcn( userSensorTask, world->userTaskContext );\n\t}\n\n\tb2TracyCZoneNC( sensor_state, \"Events\", b2_colorLightSlateGray, true );\n\n\tb2BitSet* bitSet = &world->sensorTaskContexts.data[0].eventBits;\n\tfor ( int i = 1; i < world->workerCount; ++i )\n\t{\n\t\tb2InPlaceUnion( bitSet, &world->sensorTaskContexts.data[i].eventBits );\n\t}\n\n\t// Iterate sensors bits and publish events\n\t// Process sensor state changes. Iterate over set bits\n\tuint64_t* bits = bitSet->bits;\n\tuint32_t blockCount = bitSet->blockCount;\n\n\tfor ( uint32_t k = 0; k < blockCount; ++k )\n\t{\n\t\tuint64_t word = bits[k];\n\t\twhile ( word != 0 )\n\t\t{\n\t\t\tuint32_t ctz = b2CTZ64( word );\n\t\t\tint sensorIndex = (int)( 64 * k + ctz );\n\n\t\t\tb2Sensor* sensor = b2SensorArray_Get( &world->sensors, sensorIndex );\n\t\t\tb2Shape* sensorShape = b2ShapeArray_Get( &world->shapes, sensor->shapeId );\n\t\t\tb2ShapeId sensorId = { sensor->shapeId + 1, world->worldId, sensorShape->generation };\n\n\t\t\tint count1 = sensor->overlaps1.count;\n\t\t\tint count2 = sensor->overlaps2.count;\n\t\t\tconst b2Visitor* refs1 = sensor->overlaps1.data;\n\t\t\tconst b2Visitor* refs2 = sensor->overlaps2.data;\n\n\t\t\t// overlaps1 can have overlaps that end\n\t\t\t// overlaps2 can have overlaps that begin\n\t\t\tint index1 = 0, index2 = 0;\n\t\t\twhile ( index1 < count1 && index2 < count2 )\n\t\t\t{\n\t\t\t\tconst b2Visitor* r1 = refs1 + index1;\n\t\t\t\tconst b2Visitor* r2 = refs2 + index2;\n\t\t\t\tif ( r1->shapeId == r2->shapeId )\n\t\t\t\t{\n\t\t\t\t\tif ( r1->generation < r2->generation )\n\t\t\t\t\t{\n\t\t\t\t\t\t// end\n\t\t\t\t\t\tb2ShapeId visitorId = { r1->shapeId + 1, world->worldId, r1->generation };\n\t\t\t\t\t\tb2SensorEndTouchEvent event = {\n\t\t\t\t\t\t\t.sensorShapeId = sensorId,\n\t\t\t\t\t\t\t.visitorShapeId = visitorId,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tb2SensorEndTouchEventArray_Push( &world->sensorEndEvents[world->endEventArrayIndex], event );\n\t\t\t\t\t\tindex1 += 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if ( r1->generation > r2->generation )\n\t\t\t\t\t{\n\t\t\t\t\t\t// begin\n\t\t\t\t\t\tb2ShapeId visitorId = { r2->shapeId + 1, world->worldId, r2->generation };\n\t\t\t\t\t\tb2SensorBeginTouchEvent event = { sensorId, visitorId };\n\t\t\t\t\t\tb2SensorBeginTouchEventArray_Push( &world->sensorBeginEvents, event );\n\t\t\t\t\t\tindex2 += 1;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// persisted\n\t\t\t\t\t\tindex1 += 1;\n\t\t\t\t\t\tindex2 += 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ( r1->shapeId < r2->shapeId )\n\t\t\t\t{\n\t\t\t\t\t// end\n\t\t\t\t\tb2ShapeId visitorId = { r1->shapeId + 1, world->worldId, r1->generation };\n\t\t\t\t\tb2SensorEndTouchEvent event = { sensorId, visitorId };\n\t\t\t\t\tb2SensorEndTouchEventArray_Push( &world->sensorEndEvents[world->endEventArrayIndex], event );\n\t\t\t\t\tindex1 += 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// begin\n\t\t\t\t\tb2ShapeId visitorId = { r2->shapeId + 1, world->worldId, r2->generation };\n\t\t\t\t\tb2SensorBeginTouchEvent event = { sensorId, visitorId };\n\t\t\t\t\tb2SensorBeginTouchEventArray_Push( &world->sensorBeginEvents, event );\n\t\t\t\t\tindex2 += 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\twhile ( index1 < count1 )\n\t\t\t{\n\t\t\t\t// end\n\t\t\t\tconst b2Visitor* r1 = refs1 + index1;\n\t\t\t\tb2ShapeId visitorId = { r1->shapeId + 1, world->worldId, r1->generation };\n\t\t\t\tb2SensorEndTouchEvent event = { sensorId, visitorId };\n\t\t\t\tb2SensorEndTouchEventArray_Push( &world->sensorEndEvents[world->endEventArrayIndex], event );\n\t\t\t\tindex1 += 1;\n\t\t\t}\n\n\t\t\twhile ( index2 < count2 )\n\t\t\t{\n\t\t\t\t// begin\n\t\t\t\tconst b2Visitor* r2 = refs2 + index2;\n\t\t\t\tb2ShapeId visitorId = { r2->shapeId + 1, world->worldId, r2->generation };\n\t\t\t\tb2SensorBeginTouchEvent event = { sensorId, visitorId };\n\t\t\t\tb2SensorBeginTouchEventArray_Push( &world->sensorBeginEvents, event );\n\t\t\t\tindex2 += 1;\n\t\t\t}\n\n\t\t\t// Clear the smallest set bit\n\t\t\tword = word & ( word - 1 );\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( sensor_state );\n\tb2TracyCZoneEnd( overlap_sensors );\n}\n\nvoid b2DestroySensor( b2World* world, b2Shape* sensorShape )\n{\n\tb2Sensor* sensor = b2SensorArray_Get( &world->sensors, sensorShape->sensorIndex );\n\tfor ( int i = 0; i < sensor->overlaps2.count; ++i )\n\t{\n\t\tb2Visitor* ref = sensor->overlaps2.data + i;\n\t\tb2SensorEndTouchEvent event = {\n\t\t\t.sensorShapeId =\n\t\t\t\t{\n\t\t\t\t\t.index1 = sensorShape->id + 1,\n\t\t\t\t\t.world0 = world->worldId,\n\t\t\t\t\t.generation = sensorShape->generation,\n\t\t\t\t},\n\t\t\t.visitorShapeId =\n\t\t\t\t{\n\t\t\t\t\t.index1 = ref->shapeId + 1,\n\t\t\t\t\t.world0 = world->worldId,\n\t\t\t\t\t.generation = ref->generation,\n\t\t\t\t},\n\t\t};\n\n\t\tb2SensorEndTouchEventArray_Push( world->sensorEndEvents + world->endEventArrayIndex, event );\n\t}\n\n\t// Destroy sensor\n\tb2VisitorArray_Destroy( &sensor->hits );\n\tb2VisitorArray_Destroy( &sensor->overlaps1 );\n\tb2VisitorArray_Destroy( &sensor->overlaps2 );\n\n\tint movedIndex = b2SensorArray_RemoveSwap( &world->sensors, sensorShape->sensorIndex );\n\tif ( movedIndex != B2_NULL_INDEX )\n\t{\n\t\t// Fixup moved sensor\n\t\tb2Sensor* movedSensor = b2SensorArray_Get( &world->sensors, sensorShape->sensorIndex );\n\t\tb2Shape* otherSensorShape = b2ShapeArray_Get( &world->shapes, movedSensor->shapeId );\n\t\totherSensorShape->sensorIndex = sensorShape->sensorIndex;\n\t}\n}\n"
  },
  {
    "path": "src/sensor.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n#include \"bitset.h\"\n\ntypedef struct b2Shape b2Shape;\ntypedef struct b2World b2World;\n\n// Used to track shapes that hit sensors using time of impact\ntypedef struct b2SensorHit\n{\n\tint sensorId;\n\tint visitorId;\n} b2SensorHit;\n\ntypedef struct b2Visitor\n{\n\tint shapeId;\n\tuint16_t generation;\n} b2Visitor;\n\ntypedef struct b2Sensor\n{\n\t// todo find a way to pool these\n\tb2VisitorArray hits;\n\tb2VisitorArray overlaps1;\n\tb2VisitorArray overlaps2;\n\tint shapeId;\n} b2Sensor;\n\ntypedef struct b2SensorTaskContext\n{\n\tb2BitSet eventBits;\n} b2SensorTaskContext;\n\nvoid b2OverlapSensors( b2World* world );\n\nvoid b2DestroySensor( b2World* world, b2Shape* sensorShape );\n\nB2_ARRAY_INLINE( b2Sensor, b2Sensor )\nB2_ARRAY_INLINE( b2SensorHit, b2SensorHit )\nB2_ARRAY_INLINE( b2SensorTaskContext, b2SensorTaskContext )\nB2_ARRAY_INLINE( b2Visitor, b2Visitor )\n"
  },
  {
    "path": "src/shape.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"shape.h\"\n\n#include \"body.h\"\n#include \"broad_phase.h\"\n#include \"contact.h\"\n#include \"physics_world.h\"\n#include \"sensor.h\"\n\n// needed for dll export\n#include \"solver_set.h\"\n#include \"box2d/box2d.h\"\n\n#include <stddef.h>\n\nB2_ARRAY_SOURCE( b2ChainShape, b2ChainShape )\nB2_ARRAY_SOURCE( b2Shape, b2Shape )\n\nstatic b2Shape* b2GetShape( b2World* world, b2ShapeId shapeId )\n{\n\tint id = shapeId.index1 - 1;\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, id );\n\tB2_ASSERT( shape->id == id && shape->generation == shapeId.generation );\n\treturn shape;\n}\n\nstatic b2ChainShape* b2GetChainShape( b2World* world, b2ChainId chainId )\n{\n\tint id = chainId.index1 - 1;\n\tb2ChainShape* chain = b2ChainShapeArray_Get( &world->chainShapes, id );\n\tB2_ASSERT( chain->id == id && chain->generation == chainId.generation );\n\treturn chain;\n}\n\nstatic void b2UpdateShapeAABBs( b2Shape* shape, b2Transform transform, b2BodyType proxyType )\n{\n\t// Compute a bounding box with a speculative margin\n\tconst float speculativeDistance = B2_SPECULATIVE_DISTANCE;\n\tconst float aabbMargin = B2_AABB_MARGIN;\n\n\tb2AABB aabb = b2ComputeShapeAABB( shape, transform );\n\taabb.lowerBound.x -= speculativeDistance;\n\taabb.lowerBound.y -= speculativeDistance;\n\taabb.upperBound.x += speculativeDistance;\n\taabb.upperBound.y += speculativeDistance;\n\tshape->aabb = aabb;\n\n\t// Smaller margin for static bodies. Cannot be zero due to TOI tolerance.\n\tfloat margin = proxyType == b2_staticBody ? speculativeDistance : aabbMargin;\n\tb2AABB fatAABB;\n\tfatAABB.lowerBound.x = aabb.lowerBound.x - margin;\n\tfatAABB.lowerBound.y = aabb.lowerBound.y - margin;\n\tfatAABB.upperBound.x = aabb.upperBound.x + margin;\n\tfatAABB.upperBound.y = aabb.upperBound.y + margin;\n\tshape->fatAABB = fatAABB;\n}\n\nstatic b2Shape* b2CreateShapeInternal( b2World* world, b2Body* body, b2Transform transform, const b2ShapeDef* def,\n\t\t\t\t\t\t\t\t\t   const void* geometry, b2ShapeType shapeType )\n{\n\tint shapeId = b2AllocId( &world->shapeIdPool );\n\n\tif ( shapeId == world->shapes.count )\n\t{\n\t\tb2ShapeArray_Push( &world->shapes, (b2Shape){ 0 } );\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( world->shapes.data[shapeId].id == B2_NULL_INDEX );\n\t}\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\tswitch ( shapeType )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\tshape->capsule = *(const b2Capsule*)geometry;\n\t\t\tbreak;\n\n\t\tcase b2_circleShape:\n\t\t\tshape->circle = *(const b2Circle*)geometry;\n\t\t\tbreak;\n\n\t\tcase b2_polygonShape:\n\t\t\tshape->polygon = *(const b2Polygon*)geometry;\n\t\t\tbreak;\n\n\t\tcase b2_segmentShape:\n\t\t\tshape->segment = *(const b2Segment*)geometry;\n\t\t\tbreak;\n\n\t\tcase b2_chainSegmentShape:\n\t\t\tshape->chainSegment = *(const b2ChainSegment*)geometry;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t\t\tbreak;\n\t}\n\n\tshape->id = shapeId;\n\tshape->bodyId = body->id;\n\tshape->type = shapeType;\n\tshape->density = def->density;\n\tshape->material = def->material;\n\tshape->filter = def->filter;\n\tshape->userData = def->userData;\n\tshape->enlargedAABB = false;\n\tshape->enableSensorEvents = def->enableSensorEvents;\n\tshape->enableContactEvents = def->enableContactEvents;\n\tshape->enableCustomFiltering = def->enableCustomFiltering;\n\tshape->enableHitEvents = def->enableHitEvents;\n\tshape->enablePreSolveEvents = def->enablePreSolveEvents;\n\tshape->proxyKey = B2_NULL_INDEX;\n\tshape->localCentroid = b2GetShapeCentroid( shape );\n\tshape->aabb = (b2AABB){ b2Vec2_zero, b2Vec2_zero };\n\tshape->fatAABB = (b2AABB){ b2Vec2_zero, b2Vec2_zero };\n\tshape->generation += 1;\n\n\tif ( body->setIndex != b2_disabledSet )\n\t{\n\t\tb2BodyType proxyType = body->type;\n\t\tb2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, def->invokeContactCreation || def->isSensor );\n\t}\n\n\t// Add to shape doubly linked list\n\tif ( body->headShapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* headShape = b2ShapeArray_Get( &world->shapes, body->headShapeId );\n\t\theadShape->prevShapeId = shapeId;\n\t}\n\n\tshape->prevShapeId = B2_NULL_INDEX;\n\tshape->nextShapeId = body->headShapeId;\n\tbody->headShapeId = shapeId;\n\tbody->shapeCount += 1;\n\n\tif ( def->isSensor )\n\t{\n\t\tshape->sensorIndex = world->sensors.count;\n\t\tb2Sensor sensor = {\n\t\t\t.hits = b2VisitorArray_Create( 4 ),\n\t\t\t.overlaps1 = b2VisitorArray_Create( 16 ),\n\t\t\t.overlaps2 = b2VisitorArray_Create( 16 ),\n\t\t\t.shapeId = shapeId,\n\t\t};\n\t\tb2SensorArray_Push( &world->sensors, sensor );\n\t}\n\telse\n\t{\n\t\tshape->sensorIndex = B2_NULL_INDEX;\n\t}\n\n\tb2ValidateSolverSets( world );\n\n\treturn shape;\n}\n\nstatic b2ShapeId b2CreateShape( b2BodyId bodyId, const b2ShapeDef* def, const void* geometry, b2ShapeType shapeType )\n{\n\tB2_CHECK_DEF( def );\n\tB2_ASSERT( b2IsValidFloat( def->density ) && def->density >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( def->material.friction ) && def->material.friction >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( def->material.restitution ) && def->material.restitution >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( def->material.rollingResistance ) && def->material.rollingResistance >= 0.0f );\n\tB2_ASSERT( b2IsValidFloat( def->material.tangentSpeed ) );\n\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn (b2ShapeId){ 0 };\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\tb2Shape* shape = b2CreateShapeInternal( world, body, transform, def, geometry, shapeType );\n\n\tif ( def->updateBodyMass == true )\n\t{\n\t\tb2UpdateBodyMassData( world, body );\n\t}\n\telse\n\t{\n\t\tbody->flags |= b2_dirtyMass;\n\t}\n\n\tb2ValidateSolverSets( world );\n\n\tb2ShapeId id = { shape->id + 1, bodyId.world0, shape->generation };\n\treturn id;\n}\n\nb2ShapeId b2CreateCircleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Circle* circle )\n{\n\treturn b2CreateShape( bodyId, def, circle, b2_circleShape );\n}\n\nb2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Capsule* capsule )\n{\n\tfloat lengthSqr = b2DistanceSquared( capsule->center1, capsule->center2 );\n\tif ( lengthSqr <= B2_LINEAR_SLOP * B2_LINEAR_SLOP )\n\t{\n\t\treturn b2_nullShapeId;\n\t}\n\n\treturn b2CreateShape( bodyId, def, capsule, b2_capsuleShape );\n}\n\nb2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon )\n{\n\tB2_ASSERT( b2IsValidFloat( polygon->radius ) && polygon->radius >= 0.0f );\n\treturn b2CreateShape( bodyId, def, polygon, b2_polygonShape );\n}\n\nb2ShapeId b2CreateSegmentShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Segment* segment )\n{\n\tfloat lengthSqr = b2DistanceSquared( segment->point1, segment->point2 );\n\tif ( lengthSqr <= B2_LINEAR_SLOP * B2_LINEAR_SLOP )\n\t{\n\t\tB2_ASSERT( false );\n\t\treturn b2_nullShapeId;\n\t}\n\n\treturn b2CreateShape( bodyId, def, segment, b2_segmentShape );\n}\n\n// Destroy a shape on a body. This doesn't need to be called when destroying a body.\nstatic void b2DestroyShapeInternal( b2World* world, b2Shape* shape, b2Body* body, bool wakeBodies )\n{\n\tint shapeId = shape->id;\n\n\t// Remove the shape from the body's doubly linked list.\n\tif ( shape->prevShapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* prevShape = b2ShapeArray_Get( &world->shapes, shape->prevShapeId );\n\t\tprevShape->nextShapeId = shape->nextShapeId;\n\t}\n\n\tif ( shape->nextShapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* nextShape = b2ShapeArray_Get( &world->shapes, shape->nextShapeId );\n\t\tnextShape->prevShapeId = shape->prevShapeId;\n\t}\n\n\tif ( shapeId == body->headShapeId )\n\t{\n\t\tbody->headShapeId = shape->nextShapeId;\n\t}\n\n\tbody->shapeCount -= 1;\n\n\t// Remove from broad-phase.\n\tb2DestroyShapeProxy( shape, &world->broadPhase );\n\n\t// Destroy any contacts associated with the shape.\n\tint contactKey = body->headContactKey;\n\twhile ( contactKey != B2_NULL_INDEX )\n\t{\n\t\tint contactId = contactKey >> 1;\n\t\tint edgeIndex = contactKey & 1;\n\n\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\n\t\tif ( contact->shapeIdA == shapeId || contact->shapeIdB == shapeId )\n\t\t{\n\t\t\tb2DestroyContact( world, contact, wakeBodies );\n\t\t}\n\t}\n\n\tif ( shape->sensorIndex != B2_NULL_INDEX )\n\t{\n\t\tb2Sensor* sensor = b2SensorArray_Get( &world->sensors, shape->sensorIndex );\n\t\tfor ( int i = 0; i < sensor->overlaps2.count; ++i )\n\t\t{\n\t\t\tb2Visitor* ref = sensor->overlaps2.data + i;\n\t\t\tb2SensorEndTouchEvent event = {\n\t\t\t\t.sensorShapeId =\n\t\t\t\t\t{\n\t\t\t\t\t\t.index1 = shapeId + 1,\n\t\t\t\t\t\t.world0 = world->worldId,\n\t\t\t\t\t\t.generation = shape->generation,\n\t\t\t\t\t},\n\t\t\t\t.visitorShapeId =\n\t\t\t\t\t{\n\t\t\t\t\t\t.index1 = ref->shapeId + 1,\n\t\t\t\t\t\t.world0 = world->worldId,\n\t\t\t\t\t\t.generation = ref->generation,\n\t\t\t\t\t},\n\t\t\t};\n\n\t\t\tb2SensorEndTouchEventArray_Push( world->sensorEndEvents + world->endEventArrayIndex, event );\n\t\t}\n\n\t\t// Destroy sensor\n\t\tb2VisitorArray_Destroy( &sensor->hits );\n\t\tb2VisitorArray_Destroy( &sensor->overlaps1 );\n\t\tb2VisitorArray_Destroy( &sensor->overlaps2 );\n\n\t\tint movedIndex = b2SensorArray_RemoveSwap( &world->sensors, shape->sensorIndex );\n\t\tif ( movedIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\t// Fixup moved sensor\n\t\t\tb2Sensor* movedSensor = b2SensorArray_Get( &world->sensors, shape->sensorIndex );\n\t\t\tb2Shape* otherSensorShape = b2ShapeArray_Get( &world->shapes, movedSensor->shapeId );\n\t\t\totherSensorShape->sensorIndex = shape->sensorIndex;\n\t\t}\n\t}\n\n\t// Return shape to free list.\n\tb2FreeId( &world->shapeIdPool, shapeId );\n\tshape->id = B2_NULL_INDEX;\n\n\tb2ValidateSolverSets( world );\n}\n\nvoid b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\n\t// need to wake bodies because this might be a static body\n\tbool wakeBodies = true;\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\tb2DestroyShapeInternal( world, shape, body, wakeBodies );\n\n\tif ( updateBodyMass == true )\n\t{\n\t\tb2UpdateBodyMassData( world, body );\n\t}\n}\n\nb2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def )\n{\n\tB2_CHECK_DEF( def );\n\tB2_ASSERT( def->count >= 4 );\n\tB2_ASSERT( def->materialCount == 1 || def->materialCount == def->count );\n\n\tb2World* world = b2GetWorldLocked( bodyId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn (b2ChainId){ 0 };\n\t}\n\n\tb2Body* body = b2GetBodyFullId( world, bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\tint chainId = b2AllocId( &world->chainIdPool );\n\n\tif ( chainId == world->chainShapes.count )\n\t{\n\t\tb2ChainShapeArray_Push( &world->chainShapes, (b2ChainShape){ 0 } );\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( world->chainShapes.data[chainId].id == B2_NULL_INDEX );\n\t}\n\n\tb2ChainShape* chainShape = b2ChainShapeArray_Get( &world->chainShapes, chainId );\n\n\tchainShape->id = chainId;\n\tchainShape->bodyId = body->id;\n\tchainShape->nextChainId = body->headChainId;\n\tchainShape->generation += 1;\n\n\tint materialCount = def->materialCount;\n\tchainShape->materialCount = materialCount;\n\tchainShape->materials = b2Alloc( materialCount * sizeof( b2SurfaceMaterial ) );\n\n\tfor ( int i = 0; i < materialCount; ++i )\n\t{\n\t\tconst b2SurfaceMaterial* material = def->materials + i;\n\t\tB2_ASSERT( b2IsValidFloat( material->friction ) && material->friction >= 0.0f );\n\t\tB2_ASSERT( b2IsValidFloat( material->restitution ) && material->restitution >= 0.0f );\n\t\tB2_ASSERT( b2IsValidFloat( material->rollingResistance ) && material->rollingResistance >= 0.0f );\n\t\tB2_ASSERT( b2IsValidFloat( material->tangentSpeed ) );\n\n\t\tchainShape->materials[i] = *material;\n\t}\n\n\tbody->headChainId = chainId;\n\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.userData = def->userData;\n\tshapeDef.filter = def->filter;\n\tshapeDef.enableSensorEvents = def->enableSensorEvents;\n\tshapeDef.enableContactEvents = false;\n\tshapeDef.enableHitEvents = false;\n\n\tconst b2Vec2* points = def->points;\n\tint n = def->count;\n\n\tif ( def->isLoop )\n\t{\n\t\tchainShape->count = n;\n\t\tchainShape->shapeIndices = b2Alloc( chainShape->count * sizeof( int ) );\n\n\t\tb2ChainSegment chainSegment;\n\n\t\tint prevIndex = n - 1;\n\t\tfor ( int i = 0; i < n - 2; ++i )\n\t\t{\n\t\t\tchainSegment.ghost1 = points[prevIndex];\n\t\t\tchainSegment.segment.point1 = points[i];\n\t\t\tchainSegment.segment.point2 = points[i + 1];\n\t\t\tchainSegment.ghost2 = points[i + 2];\n\t\t\tchainSegment.chainId = chainId;\n\t\t\tprevIndex = i;\n\n\t\t\tint materialIndex = materialCount == 1 ? 0 : i;\n\t\t\tshapeDef.material = def->materials[materialIndex];\n\n\t\t\tb2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape );\n\t\t\tchainShape->shapeIndices[i] = shape->id;\n\t\t}\n\n\t\t{\n\t\t\tchainSegment.ghost1 = points[n - 3];\n\t\t\tchainSegment.segment.point1 = points[n - 2];\n\t\t\tchainSegment.segment.point2 = points[n - 1];\n\t\t\tchainSegment.ghost2 = points[0];\n\t\t\tchainSegment.chainId = chainId;\n\n\t\t\tint materialIndex = materialCount == 1 ? 0 : n - 2;\n\t\t\tshapeDef.material = def->materials[materialIndex];\n\n\t\t\tb2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape );\n\t\t\tchainShape->shapeIndices[n - 2] = shape->id;\n\t\t}\n\n\t\t{\n\t\t\tchainSegment.ghost1 = points[n - 2];\n\t\t\tchainSegment.segment.point1 = points[n - 1];\n\t\t\tchainSegment.segment.point2 = points[0];\n\t\t\tchainSegment.ghost2 = points[1];\n\t\t\tchainSegment.chainId = chainId;\n\n\t\t\tint materialIndex = materialCount == 1 ? 0 : n - 1;\n\t\t\tshapeDef.material = def->materials[materialIndex];\n\n\t\t\tb2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape );\n\t\t\tchainShape->shapeIndices[n - 1] = shape->id;\n\t\t}\n\t}\n\telse\n\t{\n\t\tchainShape->count = n - 3;\n\t\tchainShape->shapeIndices = b2Alloc( chainShape->count * sizeof( int ) );\n\n\t\tb2ChainSegment chainSegment;\n\n\t\tfor ( int i = 0; i < n - 3; ++i )\n\t\t{\n\t\t\tchainSegment.ghost1 = points[i];\n\t\t\tchainSegment.segment.point1 = points[i + 1];\n\t\t\tchainSegment.segment.point2 = points[i + 2];\n\t\t\tchainSegment.ghost2 = points[i + 3];\n\t\t\tchainSegment.chainId = chainId;\n\n\t\t\t// Material is associated with leading point of solid segment\n\t\t\tint materialIndex = materialCount == 1 ? 0 : i + 1;\n\t\t\tshapeDef.material = def->materials[materialIndex];\n\n\t\t\tb2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape );\n\t\t\tchainShape->shapeIndices[i] = shape->id;\n\t\t}\n\t}\n\n\tb2ChainId id = { chainId + 1, world->worldId, chainShape->generation };\n\treturn id;\n}\n\nvoid b2FreeChainData( b2ChainShape* chain )\n{\n\tb2Free( chain->shapeIndices, chain->count * sizeof( int ) );\n\tchain->shapeIndices = NULL;\n\n\tb2Free( chain->materials, chain->materialCount * sizeof( b2SurfaceMaterial ) );\n\tchain->materials = NULL;\n}\n\nvoid b2DestroyChain( b2ChainId chainId )\n{\n\tb2World* world = b2GetWorldLocked( chainId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2ChainShape* chain = b2GetChainShape( world, chainId );\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, chain->bodyId );\n\n\t// Remove the chain from the body's singly linked list.\n\tint* chainIdPtr = &body->headChainId;\n\tbool found = false;\n\twhile ( *chainIdPtr != B2_NULL_INDEX )\n\t{\n\t\tif ( *chainIdPtr == chain->id )\n\t\t{\n\t\t\t*chainIdPtr = chain->nextChainId;\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\n\t\tchainIdPtr = &( world->chainShapes.data[*chainIdPtr].nextChainId );\n\t}\n\n\tB2_ASSERT( found == true );\n\tif ( found == false )\n\t{\n\t\treturn;\n\t}\n\n\tint count = chain->count;\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tint shapeId = chain->shapeIndices[i];\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tbool wakeBodies = true;\n\t\tb2DestroyShapeInternal( world, shape, body, wakeBodies );\n\t}\n\n\tb2FreeChainData( chain );\n\n\t// Return chain to free list.\n\tb2FreeId( &world->chainIdPool, chain->id );\n\tchain->id = B2_NULL_INDEX;\n\n\tb2ValidateSolverSets( world );\n}\n\nb2WorldId b2Chain_GetWorld( b2ChainId chainId )\n{\n\tb2World* world = b2GetWorld( chainId.world0 );\n\treturn (b2WorldId){ chainId.world0 + 1, world->generation };\n}\n\nint b2Chain_GetSegmentCount( b2ChainId chainId )\n{\n\tb2World* world = b2GetWorldLocked( chainId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2ChainShape* chain = b2GetChainShape( world, chainId );\n\treturn chain->count;\n}\n\nint b2Chain_GetSegments( b2ChainId chainId, b2ShapeId* segmentArray, int capacity )\n{\n\tb2World* world = b2GetWorldLocked( chainId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2ChainShape* chain = b2GetChainShape( world, chainId );\n\n\tint count = b2MinInt( chain->count, capacity );\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tint shapeId = chain->shapeIndices[i];\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tsegmentArray[i] = (b2ShapeId){ shapeId + 1, chainId.world0, shape->generation };\n\t}\n\n\treturn count;\n}\n\nb2AABB b2ComputeShapeAABB( const b2Shape* shape, b2Transform xf )\n{\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\treturn b2ComputeCapsuleAABB( &shape->capsule, xf );\n\t\tcase b2_circleShape:\n\t\t\treturn b2ComputeCircleAABB( &shape->circle, xf );\n\t\tcase b2_polygonShape:\n\t\t\treturn b2ComputePolygonAABB( &shape->polygon, xf );\n\t\tcase b2_segmentShape:\n\t\t\treturn b2ComputeSegmentAABB( &shape->segment, xf );\n\t\tcase b2_chainSegmentShape:\n\t\t\treturn b2ComputeSegmentAABB( &shape->chainSegment.segment, xf );\n\t\tdefault:\n\t\t{\n\t\t\tB2_ASSERT( false );\n\t\t\tb2AABB empty = { xf.p, xf.p };\n\t\t\treturn empty;\n\t\t}\n\t}\n}\n\nb2Vec2 b2GetShapeCentroid( const b2Shape* shape )\n{\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\treturn b2Lerp( shape->capsule.center1, shape->capsule.center2, 0.5f );\n\t\tcase b2_circleShape:\n\t\t\treturn shape->circle.center;\n\t\tcase b2_polygonShape:\n\t\t\treturn shape->polygon.centroid;\n\t\tcase b2_segmentShape:\n\t\t\treturn b2Lerp( shape->segment.point1, shape->segment.point2, 0.5f );\n\t\tcase b2_chainSegmentShape:\n\t\t\treturn b2Lerp( shape->chainSegment.segment.point1, shape->chainSegment.segment.point2, 0.5f );\n\t\tdefault:\n\t\t\treturn b2Vec2_zero;\n\t}\n}\n\n// todo_erin maybe compute this on shape creation\nfloat b2GetShapePerimeter( const b2Shape* shape )\n{\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\treturn 2.0f * b2Length( b2Sub( shape->capsule.center1, shape->capsule.center2 ) ) +\n\t\t\t\t   2.0f * B2_PI * shape->capsule.radius;\n\t\tcase b2_circleShape:\n\t\t\treturn 2.0f * B2_PI * shape->circle.radius;\n\t\tcase b2_polygonShape:\n\t\t{\n\t\t\tconst b2Vec2* points = shape->polygon.vertices;\n\t\t\tint count = shape->polygon.count;\n\t\t\tfloat perimeter = 2.0f * B2_PI * shape->polygon.radius;\n\t\t\tB2_ASSERT( count > 0 );\n\t\t\tb2Vec2 prev = points[count - 1];\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 next = points[i];\n\t\t\t\tperimeter += b2Length( b2Sub( next, prev ) );\n\t\t\t\tprev = next;\n\t\t\t}\n\n\t\t\treturn perimeter;\n\t\t}\n\t\tcase b2_segmentShape:\n\t\t\treturn 2.0f * b2Length( b2Sub( shape->segment.point1, shape->segment.point2 ) );\n\t\tcase b2_chainSegmentShape:\n\t\t\treturn 2.0f * b2Length( b2Sub( shape->chainSegment.segment.point1, shape->chainSegment.segment.point2 ) );\n\t\tdefault:\n\t\t\treturn 0.0f;\n\t}\n}\n\n// This projects the shape perimeter onto an infinite line\nfloat b2GetShapeProjectedPerimeter( const b2Shape* shape, b2Vec2 line )\n{\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t{\n\t\t\tb2Vec2 axis = b2Sub( shape->capsule.center2, shape->capsule.center1 );\n\t\t\tfloat projectedLength = b2AbsFloat( b2Dot( axis, line ) );\n\t\t\treturn projectedLength + 2.0f * shape->capsule.radius;\n\t\t}\n\n\t\tcase b2_circleShape:\n\t\t\treturn 2.0f * shape->circle.radius;\n\n\t\tcase b2_polygonShape:\n\t\t{\n\t\t\tconst b2Vec2* points = shape->polygon.vertices;\n\t\t\tint count = shape->polygon.count;\n\t\t\tB2_ASSERT( count > 0 );\n\t\t\tfloat value = b2Dot( points[0], line );\n\t\t\tfloat lower = value;\n\t\t\tfloat upper = value;\n\t\t\tfor ( int i = 1; i < count; ++i )\n\t\t\t{\n\t\t\t\tvalue = b2Dot( points[i], line );\n\t\t\t\tlower = b2MinFloat( lower, value );\n\t\t\t\tupper = b2MaxFloat( upper, value );\n\t\t\t}\n\n\t\t\treturn ( upper - lower ) + 2.0f * shape->polygon.radius;\n\t\t}\n\n\t\tcase b2_segmentShape:\n\t\t{\n\t\t\tfloat value1 = b2Dot( shape->segment.point1, line );\n\t\t\tfloat value2 = b2Dot( shape->segment.point2, line );\n\t\t\treturn b2AbsFloat( value2 - value1 );\n\t\t}\n\n\t\tcase b2_chainSegmentShape:\n\t\t{\n\t\t\tfloat value1 = b2Dot( shape->chainSegment.segment.point1, line );\n\t\t\tfloat value2 = b2Dot( shape->chainSegment.segment.point2, line );\n\t\t\treturn b2AbsFloat( value2 - value1 );\n\t\t}\n\n\t\tdefault:\n\t\t\treturn 0.0f;\n\t}\n}\n\nb2MassData b2ComputeShapeMass( const b2Shape* shape )\n{\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\treturn b2ComputeCapsuleMass( &shape->capsule, shape->density );\n\t\tcase b2_circleShape:\n\t\t\treturn b2ComputeCircleMass( &shape->circle, shape->density );\n\t\tcase b2_polygonShape:\n\t\t\treturn b2ComputePolygonMass( &shape->polygon, shape->density );\n\t\tdefault:\n\t\t\treturn (b2MassData){ 0 };\n\t}\n}\n\nb2ShapeExtent b2ComputeShapeExtent( const b2Shape* shape, b2Vec2 localCenter )\n{\n\tb2ShapeExtent extent = { 0 };\n\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t{\n\t\t\tfloat radius = shape->capsule.radius;\n\t\t\textent.minExtent = radius;\n\t\t\tb2Vec2 c1 = b2Sub( shape->capsule.center1, localCenter );\n\t\t\tb2Vec2 c2 = b2Sub( shape->capsule.center2, localCenter );\n\t\t\textent.maxExtent = sqrtf( b2MaxFloat( b2LengthSquared( c1 ), b2LengthSquared( c2 ) ) ) + radius;\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_circleShape:\n\t\t{\n\t\t\tfloat radius = shape->circle.radius;\n\t\t\textent.minExtent = radius;\n\t\t\textent.maxExtent = b2Length( b2Sub( shape->circle.center, localCenter ) ) + radius;\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_polygonShape:\n\t\t{\n\t\t\tconst b2Polygon* poly = &shape->polygon;\n\t\t\tfloat minExtent = B2_HUGE;\n\t\t\tfloat maxExtentSqr = 0.0f;\n\t\t\tint count = poly->count;\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 v = poly->vertices[i];\n\t\t\t\tfloat planeOffset = b2Dot( poly->normals[i], b2Sub( v, poly->centroid ) );\n\t\t\t\tminExtent = b2MinFloat( minExtent, planeOffset );\n\n\t\t\t\tfloat distanceSqr = b2LengthSquared( b2Sub( v, localCenter ) );\n\t\t\t\tmaxExtentSqr = b2MaxFloat( maxExtentSqr, distanceSqr );\n\t\t\t}\n\n\t\t\textent.minExtent = minExtent + poly->radius;\n\t\t\textent.maxExtent = sqrtf( maxExtentSqr ) + poly->radius;\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_segmentShape:\n\t\t{\n\t\t\textent.minExtent = 0.0f;\n\t\t\tb2Vec2 c1 = b2Sub( shape->segment.point1, localCenter );\n\t\t\tb2Vec2 c2 = b2Sub( shape->segment.point2, localCenter );\n\t\t\textent.maxExtent = sqrtf( b2MaxFloat( b2LengthSquared( c1 ), b2LengthSquared( c2 ) ) );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_chainSegmentShape:\n\t\t{\n\t\t\textent.minExtent = 0.0f;\n\t\t\tb2Vec2 c1 = b2Sub( shape->chainSegment.segment.point1, localCenter );\n\t\t\tb2Vec2 c2 = b2Sub( shape->chainSegment.segment.point2, localCenter );\n\t\t\textent.maxExtent = sqrtf( b2MaxFloat( b2LengthSquared( c1 ), b2LengthSquared( c2 ) ) );\n\t\t}\n\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\treturn extent;\n}\n\nb2CastOutput b2RayCastShape( const b2RayCastInput* input, const b2Shape* shape, b2Transform transform )\n{\n\tb2RayCastInput localInput = *input;\n\tlocalInput.origin = b2InvTransformPoint( transform, input->origin );\n\tlocalInput.translation = b2InvRotateVector( transform.q, input->translation );\n\n\tb2CastOutput output = { 0 };\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\toutput = b2RayCastCapsule( &shape->capsule, &localInput );\n\t\t\tbreak;\n\t\tcase b2_circleShape:\n\t\t\toutput = b2RayCastCircle( &shape->circle, &localInput );\n\t\t\tbreak;\n\t\tcase b2_polygonShape:\n\t\t\toutput = b2RayCastPolygon( &shape->polygon, &localInput );\n\t\t\tbreak;\n\t\tcase b2_segmentShape:\n\t\t\toutput = b2RayCastSegment( &shape->segment, &localInput, false );\n\t\t\tbreak;\n\t\tcase b2_chainSegmentShape:\n\t\t\toutput = b2RayCastSegment( &shape->chainSegment.segment, &localInput, true );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn output;\n\t}\n\n\toutput.point = b2TransformPoint( transform, output.point );\n\toutput.normal = b2RotateVector( transform.q, output.normal );\n\treturn output;\n}\n\nb2CastOutput b2ShapeCastShape( const b2ShapeCastInput* input, const b2Shape* shape, b2Transform transform )\n{\n\tb2CastOutput output = { 0 };\n\n\tif ( input->proxy.count == 0 )\n\t{\n\t\treturn output;\n\t}\n\n\tb2ShapeCastInput localInput = *input;\n\n\tfor ( int i = 0; i < localInput.proxy.count; ++i )\n\t{\n\t\tlocalInput.proxy.points[i] = b2InvTransformPoint( transform, input->proxy.points[i] );\n\t}\n\n\tlocalInput.translation = b2InvRotateVector( transform.q, input->translation );\n\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\toutput = b2ShapeCastCapsule( &shape->capsule, &localInput );\n\t\t\tbreak;\n\t\tcase b2_circleShape:\n\t\t\toutput = b2ShapeCastCircle( &shape->circle, &localInput );\n\t\t\tbreak;\n\t\tcase b2_polygonShape:\n\t\t\toutput = b2ShapeCastPolygon( &shape->polygon, &localInput );\n\t\t\tbreak;\n\t\tcase b2_segmentShape:\n\t\t\toutput = b2ShapeCastSegment( &shape->segment, &localInput );\n\t\t\tbreak;\n\t\tcase b2_chainSegmentShape:\n\t\t{\n\t\t\t// Check for back side collision\n\t\t\tb2Vec2 approximateCentroid = localInput.proxy.points[0];\n\t\t\tfor ( int i = 1; i < localInput.proxy.count; ++i )\n\t\t\t{\n\t\t\t\tapproximateCentroid = b2Add( approximateCentroid, localInput.proxy.points[i] );\n\t\t\t}\n\n\t\t\tapproximateCentroid = b2MulSV( 1.0f / localInput.proxy.count, approximateCentroid );\n\n\t\t\tb2Vec2 edge = b2Sub( shape->chainSegment.segment.point2, shape->chainSegment.segment.point1 );\n\t\t\tb2Vec2 r = b2Sub( approximateCentroid, shape->chainSegment.segment.point1 );\n\n\t\t\tif ( b2Cross( r, edge ) < 0.0f )\n\t\t\t{\n\t\t\t\t// Shape cast starts behind\n\t\t\t\treturn output;\n\t\t\t}\n\n\t\t\toutput = b2ShapeCastSegment( &shape->chainSegment.segment, &localInput );\n\t\t}\n\t\tbreak;\n\t\tdefault:\n\t\t\treturn output;\n\t}\n\n\toutput.point = b2TransformPoint( transform, output.point );\n\toutput.normal = b2RotateVector( transform.q, output.normal );\n\treturn output;\n}\n\nb2PlaneResult b2CollideMover( const b2Capsule* mover, const b2Shape* shape, b2Transform transform )\n{\n\tb2Capsule localMover;\n\tlocalMover.center1 = b2InvTransformPoint( transform, mover->center1 );\n\tlocalMover.center2 = b2InvTransformPoint( transform, mover->center2 );\n\tlocalMover.radius = mover->radius;\n\n\tb2PlaneResult result = { 0 };\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\tresult = b2CollideMoverAndCapsule( &localMover, &shape->capsule );\n\t\t\tbreak;\n\t\tcase b2_circleShape:\n\t\t\tresult = b2CollideMoverAndCircle( &localMover, &shape->circle );\n\t\t\tbreak;\n\t\tcase b2_polygonShape:\n\t\t\tresult = b2CollideMoverAndPolygon( &localMover, &shape->polygon );\n\t\t\tbreak;\n\t\tcase b2_segmentShape:\n\t\t\tresult = b2CollideMoverAndSegment( &localMover, &shape->segment );\n\t\t\tbreak;\n\t\tcase b2_chainSegmentShape:\n\t\t\tresult = b2CollideMoverAndSegment( &localMover, &shape->chainSegment.segment );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn result;\n\t}\n\n\tif ( result.hit == false )\n\t{\n\t\treturn result;\n\t}\n\n\tresult.plane.normal = b2RotateVector( transform.q, result.plane.normal );\n\treturn result;\n}\n\nvoid b2CreateShapeProxy( b2Shape* shape, b2BroadPhase* bp, b2BodyType type, b2Transform transform, bool forcePairCreation )\n{\n\tB2_ASSERT( shape->proxyKey == B2_NULL_INDEX );\n\n\tb2UpdateShapeAABBs( shape, transform, type );\n\n\t// Create proxies in the broad-phase.\n\tshape->proxyKey =\n\t\tb2BroadPhase_CreateProxy( bp, type, shape->fatAABB, shape->filter.categoryBits, shape->id, forcePairCreation );\n\tB2_ASSERT( B2_PROXY_TYPE( shape->proxyKey ) < b2_bodyTypeCount );\n}\n\nvoid b2DestroyShapeProxy( b2Shape* shape, b2BroadPhase* bp )\n{\n\tif ( shape->proxyKey != B2_NULL_INDEX )\n\t{\n\t\tb2BroadPhase_DestroyProxy( bp, shape->proxyKey );\n\t\tshape->proxyKey = B2_NULL_INDEX;\n\t}\n}\n\nb2ShapeProxy b2MakeShapeDistanceProxy( const b2Shape* shape )\n{\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\treturn b2MakeProxy( &shape->capsule.center1, 2, shape->capsule.radius );\n\t\tcase b2_circleShape:\n\t\t\treturn b2MakeProxy( &shape->circle.center, 1, shape->circle.radius );\n\t\tcase b2_polygonShape:\n\t\t\treturn b2MakeProxy( shape->polygon.vertices, shape->polygon.count, shape->polygon.radius );\n\t\tcase b2_segmentShape:\n\t\t\treturn b2MakeProxy( &shape->segment.point1, 2, 0.0f );\n\t\tcase b2_chainSegmentShape:\n\t\t\treturn b2MakeProxy( &shape->chainSegment.segment.point1, 2, 0.0f );\n\t\tdefault:\n\t\t{\n\t\t\tB2_ASSERT( false );\n\t\t\tb2ShapeProxy empty = { 0 };\n\t\t\treturn empty;\n\t\t}\n\t}\n}\n\nb2BodyId b2Shape_GetBody( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn b2MakeBodyId( world, shape->bodyId );\n}\n\nb2WorldId b2Shape_GetWorld( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\treturn (b2WorldId){ shapeId.world0 + 1, world->generation };\n}\n\nvoid b2Shape_SetUserData( b2ShapeId shapeId, void* userData )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->userData = userData;\n}\n\nvoid* b2Shape_GetUserData( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->userData;\n}\n\nbool b2Shape_IsSensor( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->sensorIndex != B2_NULL_INDEX;\n}\n\nbool b2Shape_TestPoint( b2ShapeId shapeId, b2Vec2 point )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\n\tb2Transform transform = b2GetBodyTransform( world, shape->bodyId );\n\tb2Vec2 localPoint = b2InvTransformPoint( transform, point );\n\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\treturn b2PointInCapsule( &shape->capsule, localPoint );\n\n\t\tcase b2_circleShape:\n\t\t\treturn b2PointInCircle( &shape->circle, localPoint );\n\n\t\tcase b2_polygonShape:\n\t\t\treturn b2PointInPolygon( &shape->polygon, localPoint );\n\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n// todo_erin untested\nb2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\n\tb2Transform transform = b2GetBodyTransform( world, shape->bodyId );\n\n\t// input in local coordinates\n\tb2RayCastInput localInput;\n\tlocalInput.origin = b2InvTransformPoint( transform, input->origin );\n\tlocalInput.translation = b2InvRotateVector( transform.q, input->translation );\n\tlocalInput.maxFraction = input->maxFraction;\n\n\tb2CastOutput output = { 0 };\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\toutput = b2RayCastCapsule( &shape->capsule, &localInput );\n\t\t\tbreak;\n\n\t\tcase b2_circleShape:\n\t\t\toutput = b2RayCastCircle( &shape->circle, &localInput );\n\t\t\tbreak;\n\n\t\tcase b2_segmentShape:\n\t\t\toutput = b2RayCastSegment( &shape->segment, &localInput, false );\n\t\t\tbreak;\n\n\t\tcase b2_polygonShape:\n\t\t\toutput = b2RayCastPolygon( &shape->polygon, &localInput );\n\t\t\tbreak;\n\n\t\tcase b2_chainSegmentShape:\n\t\t\toutput = b2RayCastSegment( &shape->chainSegment.segment, &localInput, true );\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tB2_ASSERT( false );\n\t\t\treturn output;\n\t}\n\n\tif ( output.hit )\n\t{\n\t\t// convert to world coordinates\n\t\toutput.normal = b2RotateVector( transform.q, output.normal );\n\t\toutput.point = b2TransformPoint( transform, output.point );\n\t}\n\n\treturn output;\n}\n\nvoid b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass )\n{\n\tB2_ASSERT( b2IsValidFloat( density ) && density >= 0.0f );\n\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tif ( density == shape->density )\n\t{\n\t\t// early return to avoid expensive function\n\t\treturn;\n\t}\n\n\tshape->density = density;\n\n\tif ( updateBodyMass == true )\n\t{\n\t\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\t\tb2UpdateBodyMassData( world, body );\n\t}\n}\n\nfloat b2Shape_GetDensity( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->density;\n}\n\nvoid b2Shape_SetFriction( b2ShapeId shapeId, float friction )\n{\n\tB2_ASSERT( b2IsValidFloat( friction ) && friction >= 0.0f );\n\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->material.friction = friction;\n}\n\nfloat b2Shape_GetFriction( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->material.friction;\n}\n\nvoid b2Shape_SetRestitution( b2ShapeId shapeId, float restitution )\n{\n\tB2_ASSERT( b2IsValidFloat( restitution ) && restitution >= 0.0f );\n\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->material.restitution = restitution;\n}\n\nfloat b2Shape_GetRestitution( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->material.restitution;\n}\n\nvoid b2Shape_SetUserMaterial( b2ShapeId shapeId, uint64_t material )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tB2_ASSERT( world->locked == false );\n\tif ( world->locked )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->material.userMaterialId = material;\n}\n\nuint64_t b2Shape_GetUserMaterial( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->material.userMaterialId;\n}\n\nb2SurfaceMaterial b2Shape_GetSurfaceMaterial( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->material;\n}\n\nvoid b2Shape_SetSurfaceMaterial( b2ShapeId shapeId, const b2SurfaceMaterial* surfaceMaterial )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->material = *surfaceMaterial;\n}\n\nb2Filter b2Shape_GetFilter( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->filter;\n}\n\nstatic void b2ResetProxy( b2World* world, b2Shape* shape, bool wakeBodies, bool destroyProxy )\n{\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\n\tint shapeId = shape->id;\n\n\t// destroy all contacts associated with this shape\n\tint contactKey = body->headContactKey;\n\twhile ( contactKey != B2_NULL_INDEX )\n\t{\n\t\tint contactId = contactKey >> 1;\n\t\tint edgeIndex = contactKey & 1;\n\n\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\n\t\tif ( contact->shapeIdA == shapeId || contact->shapeIdB == shapeId )\n\t\t{\n\t\t\tb2DestroyContact( world, contact, wakeBodies );\n\t\t}\n\t}\n\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\tif ( shape->proxyKey != B2_NULL_INDEX )\n\t{\n\t\tb2BodyType proxyType = B2_PROXY_TYPE( shape->proxyKey );\n\t\tb2UpdateShapeAABBs( shape, transform, proxyType );\n\n\t\tif ( destroyProxy )\n\t\t{\n\t\t\tb2BroadPhase_DestroyProxy( &world->broadPhase, shape->proxyKey );\n\n\t\t\tbool forcePairCreation = true;\n\t\t\tshape->proxyKey = b2BroadPhase_CreateProxy( &world->broadPhase, proxyType, shape->fatAABB, shape->filter.categoryBits,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshapeId, forcePairCreation );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb2BroadPhase_MoveProxy( &world->broadPhase, shape->proxyKey, shape->fatAABB );\n\t\t}\n\t}\n\telse\n\t{\n\t\tb2BodyType proxyType = body->type;\n\t\tb2UpdateShapeAABBs( shape, transform, proxyType );\n\t}\n\n\tb2ValidateSolverSets( world );\n}\n\nvoid b2Shape_SetFilter( b2ShapeId shapeId, b2Filter filter )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tif ( filter.maskBits == shape->filter.maskBits && filter.categoryBits == shape->filter.categoryBits &&\n\t\t filter.groupIndex == shape->filter.groupIndex )\n\t{\n\t\treturn;\n\t}\n\n\t// If the category bits change, I need to destroy the proxy because it affects the tree sorting.\n\tbool destroyProxy = filter.categoryBits != shape->filter.categoryBits;\n\n\tshape->filter = filter;\n\n\t// need to wake bodies because a filter change may destroy contacts\n\tbool wakeBodies = true;\n\tb2ResetProxy( world, shape, wakeBodies, destroyProxy );\n\n\t// note: this does not immediately update sensor overlaps. Instead sensor\n\t// overlaps are updated the next time step\n}\n\nvoid b2Shape_EnableSensorEvents( b2ShapeId shapeId, bool flag )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->enableSensorEvents = flag;\n}\n\nbool b2Shape_AreSensorEventsEnabled( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->enableSensorEvents;\n}\n\nvoid b2Shape_EnableContactEvents( b2ShapeId shapeId, bool flag )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->enableContactEvents = flag;\n}\n\nbool b2Shape_AreContactEventsEnabled( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->enableContactEvents;\n}\n\nvoid b2Shape_EnablePreSolveEvents( b2ShapeId shapeId, bool flag )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->enablePreSolveEvents = flag;\n}\n\nbool b2Shape_ArePreSolveEventsEnabled( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->enablePreSolveEvents;\n}\n\nvoid b2Shape_EnableHitEvents( b2ShapeId shapeId, bool flag )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->enableHitEvents = flag;\n}\n\nbool b2Shape_AreHitEventsEnabled( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->enableHitEvents;\n}\n\nb2ShapeType b2Shape_GetType( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->type;\n}\n\nb2Circle b2Shape_GetCircle( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tB2_ASSERT( shape->type == b2_circleShape );\n\treturn shape->circle;\n}\n\nb2Segment b2Shape_GetSegment( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tB2_ASSERT( shape->type == b2_segmentShape );\n\treturn shape->segment;\n}\n\nb2ChainSegment b2Shape_GetChainSegment( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tB2_ASSERT( shape->type == b2_chainSegmentShape );\n\treturn shape->chainSegment;\n}\n\nb2Capsule b2Shape_GetCapsule( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tB2_ASSERT( shape->type == b2_capsuleShape );\n\treturn shape->capsule;\n}\n\nb2Polygon b2Shape_GetPolygon( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tB2_ASSERT( shape->type == b2_polygonShape );\n\treturn shape->polygon;\n}\n\nvoid b2Shape_SetCircle( b2ShapeId shapeId, const b2Circle* circle )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->circle = *circle;\n\tshape->type = b2_circleShape;\n\n\t// need to wake bodies so they can react to the shape change\n\tbool wakeBodies = true;\n\tbool destroyProxy = true;\n\tb2ResetProxy( world, shape, wakeBodies, destroyProxy );\n}\n\nvoid b2Shape_SetCapsule( b2ShapeId shapeId, const b2Capsule* capsule )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tfloat lengthSqr = b2DistanceSquared( capsule->center1, capsule->center2 );\n\tif ( lengthSqr <= B2_LINEAR_SLOP * B2_LINEAR_SLOP )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->capsule = *capsule;\n\tshape->type = b2_capsuleShape;\n\n\t// need to wake bodies so they can react to the shape change\n\tbool wakeBodies = true;\n\tbool destroyProxy = true;\n\tb2ResetProxy( world, shape, wakeBodies, destroyProxy );\n}\n\nvoid b2Shape_SetSegment( b2ShapeId shapeId, const b2Segment* segment )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->segment = *segment;\n\tshape->type = b2_segmentShape;\n\n\t// need to wake bodies so they can react to the shape change\n\tbool wakeBodies = true;\n\tbool destroyProxy = true;\n\tb2ResetProxy( world, shape, wakeBodies, destroyProxy );\n}\n\nvoid b2Shape_SetPolygon( b2ShapeId shapeId, const b2Polygon* polygon )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tshape->polygon = *polygon;\n\tshape->type = b2_polygonShape;\n\n\t// need to wake bodies so they can react to the shape change\n\tbool wakeBodies = true;\n\tbool destroyProxy = true;\n\tb2ResetProxy( world, shape, wakeBodies, destroyProxy );\n}\n\nb2ChainId b2Shape_GetParentChain( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tif ( shape->type == b2_chainSegmentShape )\n\t{\n\t\tint chainId = shape->chainSegment.chainId;\n\t\tif ( chainId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2ChainShape* chain = b2ChainShapeArray_Get( &world->chainShapes, chainId );\n\t\t\tb2ChainId id = { chainId + 1, shapeId.world0, chain->generation };\n\t\t\treturn id;\n\t\t}\n\t}\n\n\treturn (b2ChainId){ 0 };\n}\n\nint b2Chain_GetSurfaceMaterialCount( b2ChainId chainId )\n{\n\tb2World* world = b2GetWorld( chainId.world0 );\n\tb2ChainShape* chainShape = b2GetChainShape( world, chainId );\n\treturn chainShape->materialCount;\n}\n\nvoid b2Chain_SetSurfaceMaterial( b2ChainId chainId, const b2SurfaceMaterial* material, int materialIndex )\n{\n\tb2World* world = b2GetWorldLocked( chainId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2ChainShape* chainShape = b2GetChainShape( world, chainId );\n\tB2_ASSERT( 0 <= materialIndex && materialIndex < chainShape->materialCount );\n\tchainShape->materials[materialIndex] = *material;\n\n\tB2_ASSERT( chainShape->materialCount == 1 || chainShape->materialCount == chainShape->count );\n\tint count = chainShape->count;\n\n\tif ( chainShape->materialCount == 1 )\n\t{\n\t\tfor ( int i = 0; i < count; ++i )\n\t\t{\n\t\t\tint shapeId = chainShape->shapeIndices[i];\n\t\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\t\tshape->material = *material;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint shapeId = chainShape->shapeIndices[materialIndex];\n\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tshape->material = *material;\n\t}\n}\n\nb2SurfaceMaterial b2Chain_GetSurfaceMaterial( b2ChainId chainId, int segmentIndex )\n{\n\tb2World* world = b2GetWorld( chainId.world0 );\n\tb2ChainShape* chainShape = b2GetChainShape( world, chainId );\n\tB2_ASSERT( 0 <= segmentIndex && segmentIndex < chainShape->count );\n\treturn chainShape->materials[segmentIndex];\n}\n\nint b2Shape_GetContactCapacity( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tif ( shape->sensorIndex != B2_NULL_INDEX )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\n\t// Conservative and fast\n\treturn body->contactCount;\n}\n\nint b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int capacity )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tif ( shape->sensorIndex != B2_NULL_INDEX )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\tint contactKey = body->headContactKey;\n\tint index = 0;\n\twhile ( contactKey != B2_NULL_INDEX && index < capacity )\n\t{\n\t\tint contactId = contactKey >> 1;\n\t\tint edgeIndex = contactKey & 1;\n\n\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\n\t\t// Does contact involve this shape and is it touching?\n\t\tif ( ( contact->shapeIdA == shapeId.index1 - 1 || contact->shapeIdB == shapeId.index1 - 1 ) &&\n\t\t\t ( contact->flags & b2_contactTouchingFlag ) != 0 )\n\t\t{\n\t\t\tb2Shape* shapeA = world->shapes.data + contact->shapeIdA;\n\t\t\tb2Shape* shapeB = world->shapes.data + contact->shapeIdB;\n\n\t\t\tcontactData[index].contactId = (b2ContactId){ contact->contactId + 1, shapeId.world0, 0, contact->generation };\n\t\t\tcontactData[index].shapeIdA = (b2ShapeId){ shapeA->id + 1, shapeId.world0, shapeA->generation };\n\t\t\tcontactData[index].shapeIdB = (b2ShapeId){ shapeB->id + 1, shapeId.world0, shapeB->generation };\n\n\t\t\tb2ContactSim* contactSim = b2GetContactSim( world, contact );\n\t\t\tcontactData[index].manifold = contactSim->manifold;\n\t\t\tindex += 1;\n\t\t}\n\n\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\t}\n\n\tB2_ASSERT( index <= capacity );\n\n\treturn index;\n}\n\nint b2Shape_GetSensorCapacity( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tif ( shape->sensorIndex == B2_NULL_INDEX )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Sensor* sensor = b2SensorArray_Get( &world->sensors, shape->sensorIndex );\n\treturn sensor->overlaps2.count;\n}\n\nint b2Shape_GetSensorData( b2ShapeId shapeId, b2ShapeId* visitorIds, int capacity )\n{\n\tb2World* world = b2GetWorldLocked( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tif ( shape->sensorIndex == B2_NULL_INDEX )\n\t{\n\t\treturn 0;\n\t}\n\n\tb2Sensor* sensor = b2SensorArray_Get( &world->sensors, shape->sensorIndex );\n\n\tint count = b2MinInt( sensor->overlaps2.count, capacity );\n\tb2Visitor* refs = sensor->overlaps2.data;\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tb2ShapeId visitorId = {\n\t\t\t.index1 = refs[i].shapeId + 1,\n\t\t\t.world0 = shapeId.world0,\n\t\t\t.generation = refs[i].generation,\n\t\t};\n\n\t\tvisitorIds[i] = visitorId;\n\t}\n\n\treturn count;\n}\n\nb2AABB b2Shape_GetAABB( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn (b2AABB){ 0 };\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn shape->aabb;\n}\n\nb2MassData b2Shape_ComputeMassData( b2ShapeId shapeId )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn (b2MassData){ 0 };\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\treturn b2ComputeShapeMass( shape );\n}\n\nb2Vec2 b2Shape_GetClosestPoint( b2ShapeId shapeId, b2Vec2 target )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn (b2Vec2){ 0 };\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\tb2Transform transform = b2GetBodyTransformQuick( world, body );\n\n\tb2DistanceInput input;\n\tinput.proxyA = b2MakeShapeDistanceProxy( shape );\n\tinput.proxyB = b2MakeProxy( &target, 1, 0.0f );\n\tinput.transformA = transform;\n\tinput.transformB = b2Transform_identity;\n\tinput.useRadii = true;\n\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput output = b2ShapeDistance( &input, &cache, NULL, 0 );\n\n\treturn output.pointA;\n}\n\n// https://en.wikipedia.org/wiki/Density_of_air\n// https://www.engineeringtoolbox.com/wind-load-d_1775.html\n// force = 0.5 * air_density * velocity^2 * area\n// https://en.wikipedia.org/wiki/Lift_(force)\nvoid b2Shape_ApplyWind( b2ShapeId shapeId, b2Vec2 wind, float drag, float lift, bool wake )\n{\n\tb2World* world = b2GetWorld( shapeId.world0 );\n\tif ( world == NULL )\n\t{\n\t\treturn;\n\t}\n\n\tb2Shape* shape = b2GetShape( world, shapeId );\n\n\tb2ShapeType shapeType = shape->type;\n\tif ( shapeType != b2_circleShape && shapeType != b2_capsuleShape && shapeType != b2_polygonShape )\n\t{\n\t\treturn;\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\n\tif ( body->type != b2_dynamicBody )\n\t{\n\t\treturn;\n\t}\n\n\tif ( body->setIndex >= b2_firstSleepingSet && wake == false )\n\t{\n\t\treturn;\n\t}\n\n\tb2BodySim* sim = b2GetBodySim( world, body );\n\n\tif ( body->setIndex != b2_awakeSet )\n\t{\n\t\t// Must wake for state to exist\n\t\tb2WakeBody( world, body );\n\t}\n\n\tB2_ASSERT( body->setIndex == b2_awakeSet );\n\n\tb2BodyState* state = b2GetBodyState( world, body );\n\tb2Transform transform = sim->transform;\n\n\tfloat lengthUnits = b2_lengthUnitsPerMeter;\n\tfloat volumeUnits = lengthUnits * lengthUnits * lengthUnits;\n\n\t// In 2D I'm assuming unit depth\n\tfloat airDensity = 1.2250f / ( volumeUnits );\n\n\tb2Vec2 force = { 0 };\n\tfloat torque = 0.0f;\n\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_circleShape:\n\t\t{\n\t\t\tfloat radius = shape->circle.radius;\n\t\t\tb2Vec2 centroid = shape->localCentroid;\n\t\t\tb2Vec2 lever = b2RotateVector( transform.q, b2Sub( centroid, sim->localCenter ) );\n\t\t\tb2Vec2 shapeVelocity = b2Add( state->linearVelocity, b2CrossSV( state->angularVelocity, lever ) );\n\t\t\tb2Vec2 relativeVelocity = b2MulSub( wind, drag, shapeVelocity );\n\t\t\tfloat speed;\n\t\t\tb2Vec2 direction = b2GetLengthAndNormalize( &speed, relativeVelocity );\n\t\t\tfloat projectedArea = 2.0f * radius;\n\t\t\tforce = b2MulSV( 0.5f * airDensity * projectedArea * speed * speed, direction );\n\t\t\ttorque = b2Cross( lever, force );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_capsuleShape:\n\t\t{\n\t\t\tb2Vec2 centroid = shape->localCentroid;\n\t\t\tb2Vec2 lever = b2RotateVector( transform.q, b2Sub( centroid, sim->localCenter ) );\n\t\t\tb2Vec2 shapeVelocity = b2Add( state->linearVelocity, b2CrossSV( state->angularVelocity, lever ) );\n\t\t\tb2Vec2 relativeVelocity = b2MulSub( wind, drag, shapeVelocity );\n\t\t\tfloat speed;\n\t\t\tb2Vec2 direction = b2GetLengthAndNormalize( &speed, relativeVelocity );\n\n\t\t\tb2Vec2 d = b2Sub( shape->capsule.center2, shape->capsule.center1 );\n\t\t\td = b2RotateVector( transform.q, d );\n\n\t\t\tfloat radius = shape->capsule.radius;\n\t\t\tfloat projectedArea = 2.0f * radius + b2AbsFloat( b2Cross(d, direction) );\n\n\t\t\t// Normal that opposes the wind\n\t\t\tb2Vec2 normal = b2LeftPerp( b2Normalize(d) );\n\t\t\tnormal = b2Dot( normal, direction ) > 0.0f ? b2Neg( normal ) : normal;\n\n\t\t\t// portion of wind that is perpendicular to surface\n\t\t\tb2Vec2 liftDirection = b2CrossSV( b2Cross( normal, direction ), direction );\n\n\t\t\tfloat forceMagnitude = 0.5f * airDensity * projectedArea * speed * speed;\n\t\t\tforce = b2MulSV( forceMagnitude, b2MulAdd( direction, lift, liftDirection ) );\n\n\t\t\tb2Vec2 edgeLever = b2MulAdd( lever, radius, normal );\n\t\t\ttorque = b2Cross( edgeLever, force );\n\t\t}\n\t\tbreak;\n\n\t\tcase b2_polygonShape:\n\t\t{\n\t\t\tb2Vec2 centroid = shape->localCentroid;\n\t\t\tb2Vec2 lever = b2RotateVector( transform.q, b2Sub( centroid, sim->localCenter ) );\n\t\t\tb2Vec2 shapeVelocity = b2Add( state->linearVelocity, b2CrossSV( state->angularVelocity, lever ) );\n\t\t\tb2Vec2 relativeVelocity = b2MulSub( wind, drag, shapeVelocity );\n\t\t\tfloat speed;\n\t\t\tb2Vec2 direction = b2GetLengthAndNormalize( &speed, relativeVelocity );\n\n\t\t\t// polygon radius is ignored for simplicity\n\t\t\tint count = shape->polygon.count;\n\t\t\tb2Vec2* vertices = shape->polygon.vertices;\n\n\t\t\tb2Vec2 v1 = vertices[count - 1];\n\t\t\tfor ( int i = 0; i < count; ++i )\n\t\t\t{\n\t\t\t\tb2Vec2 v2 = vertices[i];\n\t\t\t\tb2Vec2 d = b2Sub( v2, v1 );\n\t\t\t\tb2Vec2 edgeCenter = b2Lerp( v1, v2, 0.5f );\n\t\t\t\tv1 = v2;\n\n\t\t\t\td = b2RotateVector( transform.q, d );\n\n\t\t\t\tfloat projectedArea = b2Cross( d, direction );\n\t\t\t\tif ( projectedArea <= 0.0f )\n\t\t\t\t{\n\t\t\t\t\t// back facing\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tb2Vec2 normal = b2RightPerp( b2Normalize( d ) );\n\n\t\t\t\t// portion of wind that is perpendicular to surface\n\t\t\t\tb2Vec2 liftDirection = b2CrossSV( b2Cross( normal, direction ), direction );\n\n\t\t\t\tfloat forceMagnitude = 0.5f * airDensity * projectedArea * speed * speed;\n\t\t\t\tb2Vec2 f = b2MulSV( forceMagnitude, b2MulAdd( direction, lift, liftDirection ) );\n\n\t\t\t\tb2Vec2 edgeLever = b2RotateVector( transform.q, b2Sub( edgeCenter, sim->localCenter ) );\n\n\t\t\t\tforce = b2Add( force, f );\n\t\t\t\ttorque += b2Cross( edgeLever, f );\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\tsim->force = b2Add( sim->force, force );\n\tsim->torque += torque;\n}\n"
  },
  {
    "path": "src/shape.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n\n#include \"box2d/types.h\"\n\ntypedef struct b2BroadPhase b2BroadPhase;\ntypedef struct b2World b2World;\n\ntypedef struct b2Shape\n{\n\tint id;\n\tint bodyId;\n\tint prevShapeId;\n\tint nextShapeId;\n\tint sensorIndex;\n\tb2ShapeType type;\n\tb2SurfaceMaterial material;\n\tfloat density;\n\n\tb2AABB aabb;\n\tb2AABB fatAABB;\n\tb2Vec2 localCentroid;\n\tint proxyKey;\n\n\tb2Filter filter;\n\tvoid* userData;\n\n\tunion\n\t{\n\t\tb2Capsule capsule;\n\t\tb2Circle circle;\n\t\tb2Polygon polygon;\n\t\tb2Segment segment;\n\t\tb2ChainSegment chainSegment;\n\t};\n\n\tuint16_t generation;\n\tbool enableSensorEvents;\n\tbool enableContactEvents;\n\tbool enableCustomFiltering;\n\tbool enableHitEvents;\n\tbool enablePreSolveEvents;\n\tbool enlargedAABB;\n} b2Shape;\n\ntypedef struct b2ChainShape\n{\n\tint id;\n\tint bodyId;\n\tint nextChainId;\n\tint count;\n\tint materialCount;\n\tint* shapeIndices;\n\tb2SurfaceMaterial* materials;\n\tuint16_t generation;\n} b2ChainShape;\n\ntypedef struct b2ShapeExtent\n{\n\tfloat minExtent;\n\tfloat maxExtent;\n} b2ShapeExtent;\n\n// Sensors are shapes that live in the broad-phase but never have contacts.\n// At the end of the time step all sensors are queried for overlap with any other shapes.\n// Sensors ignore body type and sleeping.\n// Sensors generate events when there is a new overlap or and overlap disappears.\n// The sensor overlaps don't get cleared until the next time step regardless of the overlapped\n// shapes being destroyed.\n// When a sensor is destroyed.\ntypedef struct\n{\n\tb2IntArray overlaps;\n} b2SensorOverlaps;\n\nvoid b2CreateShapeProxy( b2Shape* shape, b2BroadPhase* bp, b2BodyType type, b2Transform transform, bool forcePairCreation );\nvoid b2DestroyShapeProxy( b2Shape* shape, b2BroadPhase* bp );\n\nvoid b2FreeChainData( b2ChainShape* chain );\n\nb2MassData b2ComputeShapeMass( const b2Shape* shape );\nb2ShapeExtent b2ComputeShapeExtent( const b2Shape* shape, b2Vec2 localCenter );\nb2AABB b2ComputeShapeAABB( const b2Shape* shape, b2Transform transform );\nb2Vec2 b2GetShapeCentroid( const b2Shape* shape );\nfloat b2GetShapePerimeter( const b2Shape* shape );\nfloat b2GetShapeProjectedPerimeter( const b2Shape* shape, b2Vec2 line );\n\nb2ShapeProxy b2MakeShapeDistanceProxy( const b2Shape* shape );\n\nb2CastOutput b2RayCastShape( const b2RayCastInput* input, const b2Shape* shape, b2Transform transform );\nb2CastOutput b2ShapeCastShape( const b2ShapeCastInput* input, const b2Shape* shape, b2Transform transform );\n\nb2PlaneResult b2CollideMoverAndCircle( const b2Capsule* mover, const b2Circle* shape );\nb2PlaneResult b2CollideMoverAndCapsule( const b2Capsule* mover, const b2Capsule* shape );\nb2PlaneResult b2CollideMoverAndPolygon( const b2Capsule* mover, const b2Polygon* shape );\nb2PlaneResult b2CollideMoverAndSegment( const b2Capsule* mover, const b2Segment* shape );\nb2PlaneResult b2CollideMover( const b2Capsule* mover, const b2Shape* shape, b2Transform transform );\n\nstatic inline float b2GetShapeRadius( const b2Shape* shape )\n{\n\tswitch ( shape->type )\n\t{\n\t\tcase b2_capsuleShape:\n\t\t\treturn shape->capsule.radius;\n\t\tcase b2_circleShape:\n\t\t\treturn shape->circle.radius;\n\t\tcase b2_polygonShape:\n\t\t\treturn shape->polygon.radius;\n\t\tdefault:\n\t\t\treturn 0.0f;\n\t}\n}\n\nstatic inline bool b2ShouldShapesCollide( b2Filter filterA, b2Filter filterB )\n{\n\tif ( filterA.groupIndex == filterB.groupIndex && filterA.groupIndex != 0 )\n\t{\n\t\treturn filterA.groupIndex > 0;\n\t}\n\n\treturn ( filterA.maskBits & filterB.categoryBits ) != 0 && ( filterA.categoryBits & filterB.maskBits ) != 0;\n}\n\nstatic inline bool b2ShouldQueryCollide( b2Filter shapeFilter, b2QueryFilter queryFilter )\n{\n\treturn ( shapeFilter.categoryBits & queryFilter.maskBits ) != 0 && ( shapeFilter.maskBits & queryFilter.categoryBits ) != 0;\n}\n\nB2_ARRAY_INLINE( b2ChainShape, b2ChainShape )\nB2_ARRAY_INLINE( b2Shape, b2Shape )\n"
  },
  {
    "path": "src/solver.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"solver.h\"\n\n#include \"arena_allocator.h\"\n#include \"array.h\"\n#include \"atomic.h\"\n#include \"bitset.h\"\n#include \"body.h\"\n#include \"contact.h\"\n#include \"contact_solver.h\"\n#include \"core.h\"\n#include \"ctz.h\"\n#include \"island.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"sensor.h\"\n#include \"shape.h\"\n#include \"solver_set.h\"\n\n#include <limits.h>\n#include <stdbool.h>\n#include <stddef.h>\n\n// todo testing\n#define ITERATIONS 1\n#define RELAX_ITERATIONS 1\n\n// Compare to SDL_CPUPauseInstruction\n#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __i386__ ) || defined( __x86_64__ ) )\nstatic inline void b2Pause( void )\n{\n\t__asm__ __volatile__( \"pause\\n\" );\n}\n#elif ( defined( __arm__ ) && defined( __ARM_ARCH ) && __ARM_ARCH >= 7 ) || defined( __aarch64__ )\nstatic inline void b2Pause( void )\n{\n\t__asm__ __volatile__( \"yield\" ::: \"memory\" );\n}\n#elif defined( _MSC_VER ) && ( defined( _M_IX86 ) || defined( _M_X64 ) )\nstatic inline void b2Pause( void )\n{\n\t_mm_pause();\n}\n#elif defined( _MSC_VER ) && ( defined( _M_ARM ) || defined( _M_ARM64 ) )\nstatic inline void b2Pause( void )\n{\n\t__yield();\n}\n#else\nstatic inline void b2Pause( void )\n{\n}\n#endif\n\ntypedef struct b2WorkerContext\n{\n\tb2StepContext* context;\n\tint workerIndex;\n\tvoid* userTask;\n} b2WorkerContext;\n\n// Integrate velocities and apply damping\nstatic void b2IntegrateVelocitiesTask( int startIndex, int endIndex, b2StepContext* context )\n{\n\tb2TracyCZoneNC( integrate_velocity, \"IntVel\", b2_colorDeepPink, true );\n\n\tb2BodyState* states = context->states;\n\tb2BodySim* sims = context->sims;\n\n\tb2Vec2 gravity = context->world->gravity;\n\tfloat h = context->h;\n\tfloat maxLinearSpeed = context->maxLinearVelocity;\n\tfloat maxAngularSpeed = B2_MAX_ROTATION * context->inv_dt;\n\tfloat maxLinearSpeedSquared = maxLinearSpeed * maxLinearSpeed;\n\tfloat maxAngularSpeedSquared = maxAngularSpeed * maxAngularSpeed;\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tb2BodySim* sim = sims + i;\n\t\tb2BodyState* state = states + i;\n\n\t\tb2Vec2 v = state->linearVelocity;\n\t\tfloat w = state->angularVelocity;\n\n\t\t// Apply forces, torque, gravity, and damping\n\t\t// Apply damping.\n\t\t// Differential equation: dv/dt + c * v = 0\n\t\t// Solution: v(t) = v0 * exp(-c * t)\n\t\t// Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v(t) * exp(-c * dt)\n\t\t// v2 = exp(-c * dt) * v1\n\t\t// Pade approximation:\n\t\t// v2 = v1 * 1 / (1 + c * dt)\n\t\tfloat linearDamping = 1.0f / ( 1.0f + h * sim->linearDamping );\n\t\tfloat angularDamping = 1.0f / ( 1.0f + h * sim->angularDamping );\n\n\t\t// Gravity scale will be zero for kinematic bodies\n\t\tfloat gravityScale = sim->invMass > 0.0f ? sim->gravityScale : 0.0f;\n\n\t\t// lvd = h * im * f + h * g\n\t\tb2Vec2 linearVelocityDelta = b2Add( b2MulSV( h * sim->invMass, sim->force ), b2MulSV( h * gravityScale, gravity ) );\n\t\tfloat angularVelocityDelta = h * sim->invInertia * sim->torque;\n\n\t\tv = b2MulAdd( linearVelocityDelta, linearDamping, v );\n\t\tw = angularVelocityDelta + angularDamping * w;\n\n\t\t// Clamp to max linear speed\n\t\tif ( b2Dot( v, v ) > maxLinearSpeedSquared )\n\t\t{\n\t\t\tfloat ratio = maxLinearSpeed / b2Length( v );\n\t\t\tv = b2MulSV( ratio, v );\n\t\t\tsim->flags |= b2_isSpeedCapped;\n\t\t}\n\n\t\t// Clamp to max angular speed\n\t\tif ( w * w > maxAngularSpeedSquared && ( sim->flags & b2_allowFastRotation ) == 0 )\n\t\t{\n\t\t\tfloat ratio = maxAngularSpeed / b2AbsFloat( w );\n\t\t\tw *= ratio;\n\t\t\tsim->flags |= b2_isSpeedCapped;\n\t\t}\n\n\t\tif ( state->flags & b2_lockLinearX )\n\t\t{\n\t\t\tv.x = 0.0f;\n\t\t}\n\n\t\tif ( state->flags & b2_lockLinearY )\n\t\t{\n\t\t\tv.y = 0.0f;\n\t\t}\n\n\t\tif ( state->flags & b2_lockAngularZ )\n\t\t{\n\t\t\tw = 0.0f;\n\t\t}\n\n\t\tstate->linearVelocity = v;\n\t\tstate->angularVelocity = w;\n\t}\n\n\tb2TracyCZoneEnd( integrate_velocity );\n}\n\nstatic void b2PrepareJointsTask( int startIndex, int endIndex, b2StepContext* context )\n{\n\tb2TracyCZoneNC( prepare_joints, \"PrepJoints\", b2_colorOldLace, true );\n\n\tb2JointSim** joints = context->joints;\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tb2JointSim* joint = joints[i];\n\t\tb2PrepareJoint( joint, context );\n\t}\n\n\tb2TracyCZoneEnd( prepare_joints );\n}\n\nstatic void b2WarmStartJointsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex )\n{\n\tb2TracyCZoneNC( warm_joints, \"WarmJoints\", b2_colorGold, true );\n\n\tb2GraphColor* color = context->graph->colors + colorIndex;\n\tb2JointSim* joints = color->jointSims.data;\n\tB2_ASSERT( 0 <= startIndex && startIndex < color->jointSims.count );\n\tB2_ASSERT( startIndex <= endIndex && endIndex <= color->jointSims.count );\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tb2JointSim* joint = joints + i;\n\t\tb2WarmStartJoint( joint, context );\n\t}\n\n\tb2TracyCZoneEnd( warm_joints );\n}\n\nstatic void b2SolveJointsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex, bool useBias,\n\t\t\t\t\t\t\t   int workerIndex )\n{\n\tb2TracyCZoneNC( solve_joints, \"SolveJoints\", b2_colorLemonChiffon, true );\n\n\tb2GraphColor* color = context->graph->colors + colorIndex;\n\tb2JointSim* joints = color->jointSims.data;\n\tB2_ASSERT( 0 <= startIndex && startIndex < color->jointSims.count );\n\tB2_ASSERT( startIndex <= endIndex && endIndex <= color->jointSims.count );\n\n\tb2BitSet* jointStateBitSet = &context->world->taskContexts.data[workerIndex].jointStateBitSet;\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tb2JointSim* joint = joints + i;\n\t\tb2SolveJoint( joint, context, useBias );\n\n\t\tif ( useBias && ( joint->forceThreshold < FLT_MAX || joint->torqueThreshold < FLT_MAX ) &&\n\t\t\t b2GetBit( jointStateBitSet, joint->jointId ) == false )\n\t\t{\n\t\t\tfloat force, torque;\n\t\t\tb2GetJointReaction( joint, context->inv_h, &force, &torque );\n\n\t\t\t// Check thresholds. A zero threshold means all awake joints get reported.\n\t\t\tif ( force >= joint->forceThreshold || torque >= joint->torqueThreshold )\n\t\t\t{\n\t\t\t\t// Flag this joint for processing.\n\t\t\t\tb2SetBit( jointStateBitSet, joint->jointId );\n\t\t\t}\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( solve_joints );\n}\n\nstatic void b2IntegratePositionsTask( int startIndex, int endIndex, b2StepContext* context )\n{\n\tb2TracyCZoneNC( integrate_positions, \"IntPos\", b2_colorDarkSeaGreen, true );\n\n\tb2BodyState* states = context->states;\n\tfloat h = context->h;\n\n\tB2_ASSERT( startIndex <= endIndex );\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tb2BodyState* state = states + i;\n\n\t\tif ( state->flags & b2_lockLinearX )\n\t\t{\n\t\t\tstate->linearVelocity.x = 0.0f;\n\t\t}\n\n\t\tif ( state->flags & b2_lockLinearY )\n\t\t{\n\t\t\tstate->linearVelocity.y = 0.0f;\n\t\t}\n\n\t\tif ( state->flags & b2_lockAngularZ )\n\t\t{\n\t\t\tstate->angularVelocity = 0.0f;\n\t\t}\n\n\t\tstate->deltaPosition = b2MulAdd( state->deltaPosition, h, state->linearVelocity );\n\t\tstate->deltaRotation = b2IntegrateRotation( state->deltaRotation, h * state->angularVelocity );\n\t}\n\n\tb2TracyCZoneEnd( integrate_positions );\n}\n\n#define B2_MAX_CONTINUOUS_SENSOR_HITS 8\n\nstruct b2ContinuousContext\n{\n\tb2World* world;\n\tb2BodySim* fastBodySim;\n\tb2Shape* fastShape;\n\tb2Vec2 centroid1, centroid2;\n\tb2Sweep sweep;\n\tfloat fraction;\n\tb2SensorHit sensorHits[B2_MAX_CONTINUOUS_SENSOR_HITS];\n\tfloat sensorFractions[B2_MAX_CONTINUOUS_SENSOR_HITS];\n\tint sensorCount;\n};\n\n#define B2_CORE_FRACTION 0.25f\n\n// This is called from b2DynamicTree_Query for continuous collision\nstatic bool b2ContinuousQueryCallback( int proxyId, uint64_t userData, void* context )\n{\n\tB2_UNUSED( proxyId );\n\n\tint shapeId = (int)userData;\n\n\tstruct b2ContinuousContext* continuousContext = context;\n\tb2Shape* fastShape = continuousContext->fastShape;\n\tb2BodySim* fastBodySim = continuousContext->fastBodySim;\n\n\tB2_ASSERT( fastShape->sensorIndex == B2_NULL_INDEX );\n\n\t// Skip same shape\n\tif ( shapeId == fastShape->id )\n\t{\n\t\treturn true;\n\t}\n\n\tb2World* world = continuousContext->world;\n\n\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\t// Skip same body\n\tif ( shape->bodyId == fastShape->bodyId )\n\t{\n\t\treturn true;\n\t}\n\n\tbool isSensor = shape->sensorIndex != B2_NULL_INDEX;\n\n\t// Skip sensors unless the shapes want sensor events\n\tif ( isSensor && ( shape->enableSensorEvents == false || fastShape->enableSensorEvents == false ) )\n\t{\n\t\treturn true;\n\t}\n\n\t// Skip filtered shapes\n\tbool canCollide = b2ShouldShapesCollide( fastShape->filter, shape->filter );\n\tif ( canCollide == false )\n\t{\n\t\treturn true;\n\t}\n\n\tb2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );\n\n\tb2BodySim* bodySim = b2GetBodySim( world, body );\n\tB2_ASSERT( body->type == b2_staticBody || ( fastBodySim->flags & b2_isBullet ) );\n\n\t// Skip bullets\n\tif ( bodySim->flags & b2_isBullet )\n\t{\n\t\treturn true;\n\t}\n\n\t// Skip filtered bodies\n\tb2Body* fastBody = b2BodyArray_Get( &world->bodies, fastBodySim->bodyId );\n\tcanCollide = b2ShouldBodiesCollide( world, fastBody, body );\n\tif ( canCollide == false )\n\t{\n\t\treturn true;\n\t}\n\n\t// Custom user filtering\n\tif ( shape->enableCustomFiltering || fastShape->enableCustomFiltering )\n\t{\n\t\tb2CustomFilterFcn* customFilterFcn = world->customFilterFcn;\n\t\tif ( customFilterFcn != NULL )\n\t\t{\n\t\t\tb2ShapeId idA = { shape->id + 1, world->worldId, shape->generation };\n\t\t\tb2ShapeId idB = { fastShape->id + 1, world->worldId, fastShape->generation };\n\t\t\tcanCollide = customFilterFcn( idA, idB, world->customFilterContext );\n\t\t\tif ( canCollide == false )\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Early out on fast parallel movement over a chain shape.\n\tif ( shape->type == b2_chainSegmentShape )\n\t{\n\t\tb2Transform transform = bodySim->transform;\n\t\tb2Vec2 p1 = b2TransformPoint( transform, shape->chainSegment.segment.point1 );\n\t\tb2Vec2 p2 = b2TransformPoint( transform, shape->chainSegment.segment.point2 );\n\t\tb2Vec2 e = b2Sub( p2, p1 );\n\t\tfloat length;\n\t\te = b2GetLengthAndNormalize( &length, e );\n\t\tif ( length > B2_LINEAR_SLOP )\n\t\t{\n\t\t\tb2Vec2 c1 = continuousContext->centroid1;\n\t\t\tfloat separation1 = b2Cross( b2Sub( c1, p1 ), e );\n\t\t\tb2Vec2 c2 = continuousContext->centroid2;\n\t\t\tfloat separation2 = b2Cross( b2Sub( c2, p1 ), e );\n\n\t\t\tfloat coreDistance = B2_CORE_FRACTION * fastBodySim->minExtent;\n\n\t\t\tif ( separation1 < 0.0f || ( separation1 - separation2 < coreDistance && separation2 > coreDistance ) )\n\t\t\t{\n\t\t\t\t// Minimal clipping\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// todo_erin testing early out for segments\n#if 0\n\tif ( shape->type == b2_segmentShape )\n\t{\n\t\tb2Transform transform = bodySim->transform;\n\t\tb2Vec2 p1 = b2TransformPoint( transform, shape->segment.point1 );\n\t\tb2Vec2 p2 = b2TransformPoint( transform, shape->segment.point2 );\n\t\tb2Vec2 e = b2Sub( p2, p1 );\n\t\tb2Vec2 c1 = continuousContext->centroid1;\n\t\tb2Vec2 c2 = continuousContext->centroid2;\n\t\tfloat offset1 = b2Cross( b2Sub( c1, p1 ), e );\n\t\tfloat offset2 = b2Cross( b2Sub( c2, p1 ), e );\n\n\t\tif ( offset1 > 0.0f && offset2 > 0.0f )\n\t\t{\n\t\t\t// Started behind or finished in front\n\t\t\treturn true;\n\t\t}\n\n\t\tif ( offset1 < 0.0f && offset2 < 0.0f )\n\t\t{\n\t\t\t// Started behind or finished in front\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n\n\tb2TOIInput input;\n\tinput.proxyA = b2MakeShapeDistanceProxy( shape );\n\tinput.proxyB = b2MakeShapeDistanceProxy( fastShape );\n\tinput.sweepA = b2MakeSweep( bodySim );\n\tinput.sweepB = continuousContext->sweep;\n\tinput.maxFraction = continuousContext->fraction;\n\n\tb2TOIOutput output = b2TimeOfImpact( &input );\n\tif ( isSensor )\n\t{\n\t\t// Only accept a sensor hit that is sooner than the current solid hit.\n\t\tif ( output.fraction <= continuousContext->fraction && continuousContext->sensorCount < B2_MAX_CONTINUOUS_SENSOR_HITS )\n\t\t{\n\t\t\tint index = continuousContext->sensorCount;\n\n\t\t\t// The hit shape is a sensor\n\t\t\tb2SensorHit sensorHit = {\n\t\t\t\t.sensorId = shape->id,\n\t\t\t\t.visitorId = fastShape->id,\n\t\t\t};\n\n\t\t\tcontinuousContext->sensorHits[index] = sensorHit;\n\t\t\tcontinuousContext->sensorFractions[index] = output.fraction;\n\t\t\tcontinuousContext->sensorCount += 1;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfloat hitFraction = continuousContext->fraction;\n\t\tbool didHit = false;\n\n\t\tif ( 0.0f < output.fraction && output.fraction < continuousContext->fraction )\n\t\t{\n\t\t\thitFraction = output.fraction;\n\t\t\tdidHit = true;\n\t\t}\n\t\telse if ( 0.0f == output.fraction )\n\t\t{\n\t\t\t// fallback to TOI of a small circle around the fast shape centroid\n\t\t\tb2Vec2 centroid = b2GetShapeCentroid( fastShape );\n\t\t\tb2ShapeExtent extent = b2ComputeShapeExtent( fastShape, centroid );\n\t\t\tfloat radius = B2_CORE_FRACTION * extent.minExtent;\n\t\t\tinput.proxyB = b2MakeProxy( &centroid, 1, radius );\n\t\t\toutput = b2TimeOfImpact( &input );\n\t\t\tif ( 0.0f < output.fraction && output.fraction < continuousContext->fraction )\n\t\t\t{\n\t\t\t\thitFraction = output.fraction;\n\t\t\t\tdidHit = true;\n\t\t\t}\n\t\t}\n\n\t\tif ( didHit && ( shape->enablePreSolveEvents || fastShape->enablePreSolveEvents ) && world->preSolveFcn != NULL )\n\t\t{\n\t\t\tb2ShapeId shapeIdA = { shape->id + 1, world->worldId, shape->generation };\n\t\t\tb2ShapeId shapeIdB = { fastShape->id + 1, world->worldId, fastShape->generation };\n\t\t\tdidHit = world->preSolveFcn( shapeIdA, shapeIdB, output.point, output.normal, world->preSolveContext );\n\t\t}\n\n\t\tif ( didHit )\n\t\t{\n\t\t\tfastBodySim->flags |= b2_hadTimeOfImpact;\n\t\t\tcontinuousContext->fraction = hitFraction;\n\t\t}\n\t}\n\n\t// Continue query\n\treturn true;\n}\n\nstatic void b2SolveContinuous( b2World* world, int bodySimIndex, b2TaskContext* taskContext )\n{\n\tb2TracyCZoneNC( ccd, \"CCD\", b2_colorDarkGoldenRod, true );\n\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tb2BodySim* fastBodySim = b2BodySimArray_Get( &awakeSet->bodySims, bodySimIndex );\n\tB2_ASSERT( fastBodySim->flags & b2_isFast );\n\n\tb2Sweep sweep = b2MakeSweep( fastBodySim );\n\n\tb2Transform xf1;\n\txf1.q = sweep.q1;\n\txf1.p = b2Sub( sweep.c1, b2RotateVector( sweep.q1, sweep.localCenter ) );\n\n\tb2Transform xf2;\n\txf2.q = sweep.q2;\n\txf2.p = b2Sub( sweep.c2, b2RotateVector( sweep.q2, sweep.localCenter ) );\n\n\tb2DynamicTree* staticTree = world->broadPhase.trees + b2_staticBody;\n\tb2DynamicTree* kinematicTree = world->broadPhase.trees + b2_kinematicBody;\n\tb2DynamicTree* dynamicTree = world->broadPhase.trees + b2_dynamicBody;\n\tb2Body* fastBody = b2BodyArray_Get( &world->bodies, fastBodySim->bodyId );\n\n\tstruct b2ContinuousContext context = { 0 };\n\tcontext.world = world;\n\tcontext.sweep = sweep;\n\tcontext.fastBodySim = fastBodySim;\n\tcontext.fraction = 1.0f;\n\n\tbool isBullet = ( fastBodySim->flags & b2_isBullet ) != 0;\n\n\tint shapeId = fastBody->headShapeId;\n\twhile ( shapeId != B2_NULL_INDEX )\n\t{\n\t\tb2Shape* fastShape = b2ShapeArray_Get( &world->shapes, shapeId );\n\t\tshapeId = fastShape->nextShapeId;\n\n\t\tcontext.fastShape = fastShape;\n\t\tcontext.centroid1 = b2TransformPoint( xf1, fastShape->localCentroid );\n\t\tcontext.centroid2 = b2TransformPoint( xf2, fastShape->localCentroid );\n\n\t\tb2AABB box1 = fastShape->aabb;\n\t\tb2AABB box2 = b2ComputeShapeAABB( fastShape, xf2 );\n\n\t\t// Store this to avoid double computation in the case there is no impact event\n\t\tfastShape->aabb = box2;\n\n\t\t// No continuous collision for sensors (but still need the updated bounds)\n\t\tif ( fastShape->sensorIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tb2AABB sweptBox = b2AABB_Union( box1, box2 );\n\n\t\tb2DynamicTree_Query( staticTree, sweptBox, B2_DEFAULT_MASK_BITS, b2ContinuousQueryCallback, &context );\n\n\t\tif ( isBullet )\n\t\t{\n\t\t\tb2DynamicTree_Query( kinematicTree, sweptBox, B2_DEFAULT_MASK_BITS, b2ContinuousQueryCallback, &context );\n\t\t\tb2DynamicTree_Query( dynamicTree, sweptBox, B2_DEFAULT_MASK_BITS, b2ContinuousQueryCallback, &context );\n\t\t}\n\t}\n\n\tconst float speculativeDistance = B2_SPECULATIVE_DISTANCE;\n\tconst float aabbMargin = B2_AABB_MARGIN;\n\n\tif ( context.fraction < 1.0f )\n\t{\n\t\t// Handle time of impact event\n\t\tb2Rot q = b2NLerp( sweep.q1, sweep.q2, context.fraction );\n\t\tb2Vec2 c = b2Lerp( sweep.c1, sweep.c2, context.fraction );\n\t\tb2Vec2 origin = b2Sub( c, b2RotateVector( q, sweep.localCenter ) );\n\n\t\t// Advance body\n\t\tb2Transform transform = { origin, q };\n\t\tfastBodySim->transform = transform;\n\t\tfastBodySim->center = c;\n\t\tfastBodySim->rotation0 = q;\n\t\tfastBodySim->center0 = c;\n\n\t\t// Update body move event\n\t\tb2BodyMoveEvent* event = b2BodyMoveEventArray_Get( &world->bodyMoveEvents, bodySimIndex );\n\t\tevent->transform = transform;\n\n\t\t// Prepare AABBs for broad-phase.\n\t\t// Even though a body is fast, it may not move much. So the AABB may not need enlargement.\n\n\t\tshapeId = fastBody->headShapeId;\n\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\t\t\t// Must recompute aabb at the interpolated transform\n\t\t\tb2AABB aabb = b2ComputeShapeAABB( shape, transform );\n\t\t\taabb.lowerBound.x -= speculativeDistance;\n\t\t\taabb.lowerBound.y -= speculativeDistance;\n\t\t\taabb.upperBound.x += speculativeDistance;\n\t\t\taabb.upperBound.y += speculativeDistance;\n\t\t\tshape->aabb = aabb;\n\n\t\t\tif ( b2AABB_Contains( shape->fatAABB, aabb ) == false )\n\t\t\t{\n\t\t\t\tb2AABB fatAABB;\n\t\t\t\tfatAABB.lowerBound.x = aabb.lowerBound.x - aabbMargin;\n\t\t\t\tfatAABB.lowerBound.y = aabb.lowerBound.y - aabbMargin;\n\t\t\t\tfatAABB.upperBound.x = aabb.upperBound.x + aabbMargin;\n\t\t\t\tfatAABB.upperBound.y = aabb.upperBound.y + aabbMargin;\n\t\t\t\tshape->fatAABB = fatAABB;\n\n\t\t\t\tshape->enlargedAABB = true;\n\t\t\t\tfastBodySim->flags |= b2_enlargeBounds;\n\t\t\t}\n\n\t\t\tshapeId = shape->nextShapeId;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// No time of impact event\n\n\t\t// Advance body\n\t\tfastBodySim->rotation0 = fastBodySim->transform.q;\n\t\tfastBodySim->center0 = fastBodySim->center;\n\n\t\t// Prepare AABBs for broad-phase\n\t\tshapeId = fastBody->headShapeId;\n\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\t\t\t// shape->aabb is still valid from above\n\n\t\t\tif ( b2AABB_Contains( shape->fatAABB, shape->aabb ) == false )\n\t\t\t{\n\t\t\t\tb2AABB fatAABB;\n\t\t\t\tfatAABB.lowerBound.x = shape->aabb.lowerBound.x - aabbMargin;\n\t\t\t\tfatAABB.lowerBound.y = shape->aabb.lowerBound.y - aabbMargin;\n\t\t\t\tfatAABB.upperBound.x = shape->aabb.upperBound.x + aabbMargin;\n\t\t\t\tfatAABB.upperBound.y = shape->aabb.upperBound.y + aabbMargin;\n\t\t\t\tshape->fatAABB = fatAABB;\n\n\t\t\t\tshape->enlargedAABB = true;\n\t\t\t\tfastBodySim->flags |= b2_enlargeBounds;\n\t\t\t}\n\n\t\t\tshapeId = shape->nextShapeId;\n\t\t}\n\t}\n\n\t// Push sensor hits on the the task context for serial processing.\n\tfor ( int i = 0; i < context.sensorCount; ++i )\n\t{\n\t\t// Skip any sensor hits that occurred after a solid hit\n\t\tif ( context.sensorFractions[i] < context.fraction )\n\t\t{\n\t\t\tb2SensorHitArray_Push( &taskContext->sensorHits, context.sensorHits[i] );\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( ccd );\n}\n\nstatic void b2FinalizeBodiesTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )\n{\n\tb2TracyCZoneNC( finalize_transfprms, \"Transforms\", b2_colorMediumSeaGreen, true );\n\n\tb2StepContext* stepContext = context;\n\tb2World* world = stepContext->world;\n\n\tB2_ASSERT( (int)threadIndex < world->workerCount );\n\n\tbool enableSleep = world->enableSleep;\n\tb2BodyState* states = stepContext->states;\n\tb2BodySim* sims = stepContext->sims;\n\tb2Body* bodies = world->bodies.data;\n\tfloat timeStep = stepContext->dt;\n\tfloat invTimeStep = stepContext->inv_dt;\n\n\tuint16_t worldId = world->worldId;\n\n\t// The body move event array should already have the correct size\n\tB2_ASSERT( endIndex <= world->bodyMoveEvents.count );\n\tb2BodyMoveEvent* moveEvents = world->bodyMoveEvents.data;\n\n\tb2BitSet* enlargedSimBitSet = &world->taskContexts.data[threadIndex].enlargedSimBitSet;\n\tb2BitSet* awakeIslandBitSet = &world->taskContexts.data[threadIndex].awakeIslandBitSet;\n\tb2TaskContext* taskContext = world->taskContexts.data + threadIndex;\n\n\tbool enableContinuous = world->enableContinuous;\n\n\tconst float speculativeDistance = B2_SPECULATIVE_DISTANCE;\n\tconst float aabbMargin = B2_AABB_MARGIN;\n\n\tB2_ASSERT( startIndex <= endIndex );\n\n\tfor ( int simIndex = startIndex; simIndex < endIndex; ++simIndex )\n\t{\n\t\tb2BodyState* state = states + simIndex;\n\t\tb2BodySim* sim = sims + simIndex;\n\n\t\tif ( state->flags & b2_lockLinearX )\n\t\t{\n\t\t\tstate->linearVelocity.x = 0.0f;\n\t\t}\n\n\t\tif ( state->flags & b2_lockLinearY )\n\t\t{\n\t\t\tstate->linearVelocity.y = 0.0f;\n\t\t}\n\n\t\tif ( state->flags & b2_lockAngularZ )\n\t\t{\n\t\t\tstate->angularVelocity = 0.0f;\n\t\t}\n\n\t\tb2Vec2 v = state->linearVelocity;\n\t\tfloat w = state->angularVelocity;\n\n\t\tB2_ASSERT( b2IsValidVec2( v ) );\n\t\tB2_ASSERT( b2IsValidFloat( w ) );\n\n\t\tsim->center = b2Add( sim->center, state->deltaPosition );\n\t\tsim->transform.q = b2NormalizeRot( b2MulRot( state->deltaRotation, sim->transform.q ) );\n\n\t\t// Use the velocity of the farthest point on the body to account for rotation.\n\t\tfloat maxVelocity = b2Length( v ) + b2AbsFloat( w ) * sim->maxExtent;\n\n\t\t// Sleep needs to observe position correction as well as true velocity.\n\t\tfloat maxDeltaPosition = b2Length( state->deltaPosition ) + b2AbsFloat( state->deltaRotation.s ) * sim->maxExtent;\n\n\t\t// Position correction is not as important for sleep as true velocity.\n\t\tfloat positionSleepFactor = 0.5f;\n\n\t\tfloat sleepVelocity = b2MaxFloat( maxVelocity, positionSleepFactor * invTimeStep * maxDeltaPosition );\n\n\t\t// reset state deltas\n\t\tstate->deltaPosition = b2Vec2_zero;\n\t\tstate->deltaRotation = b2Rot_identity;\n\n\t\tsim->transform.p = b2Sub( sim->center, b2RotateVector( sim->transform.q, sim->localCenter ) );\n\n\t\t// cache miss here, however I need the shape list below\n\t\tb2Body* body = bodies + sim->bodyId;\n\t\tbody->bodyMoveIndex = simIndex;\n\t\tmoveEvents[simIndex].transform = sim->transform;\n\t\tmoveEvents[simIndex].bodyId = (b2BodyId){ sim->bodyId + 1, worldId, body->generation };\n\t\tmoveEvents[simIndex].userData = body->userData;\n\t\tmoveEvents[simIndex].fellAsleep = false;\n\n\t\t// reset applied force and torque\n\t\tsim->force = b2Vec2_zero;\n\t\tsim->torque = 0.0f;\n\n\t\t// If you hit this then it means you deferred mass computation but never called b2Body_ApplyMassFromShapes\n\t\tB2_ASSERT( ( body->flags & b2_dirtyMass ) == 0 );\n\n\t\tbody->flags &= ~( b2_isFast | b2_isSpeedCapped | b2_hadTimeOfImpact );\n\t\tbody->flags |= ( sim->flags & ( b2_isSpeedCapped | b2_hadTimeOfImpact ) );\n\t\tsim->flags &= ~( b2_isFast | b2_isSpeedCapped | b2_hadTimeOfImpact );\n\n\t\tif ( enableSleep == false || body->enableSleep == false || sleepVelocity > body->sleepThreshold )\n\t\t{\n\t\t\t// Body is not sleepy\n\t\t\tbody->sleepTime = 0.0f;\n\n\t\t\tif ( body->type == b2_dynamicBody && enableContinuous && maxVelocity * timeStep > 0.5f * sim->minExtent )\n\t\t\t{\n\t\t\t\t// This flag is only retained for debug draw\n\t\t\t\tsim->flags |= b2_isFast;\n\n\t\t\t\t// Store in fast array for the continuous collision stage\n\t\t\t\t// This is deterministic because the order of TOI sweeps doesn't matter\n\t\t\t\tif ( sim->flags & b2_isBullet )\n\t\t\t\t{\n\t\t\t\t\tint bulletIndex = b2AtomicFetchAddInt( &stepContext->bulletBodyCount, 1 );\n\t\t\t\t\tstepContext->bulletBodies[bulletIndex] = simIndex;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tb2SolveContinuous( world, simIndex, taskContext );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Body is safe to advance\n\t\t\t\tsim->center0 = sim->center;\n\t\t\t\tsim->rotation0 = sim->transform.q;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Body is safe to advance and is falling asleep\n\t\t\tsim->center0 = sim->center;\n\t\t\tsim->rotation0 = sim->transform.q;\n\t\t\tbody->sleepTime += timeStep;\n\t\t}\n\n\t\t// Any single body in an island can keep it awake\n\t\tb2Island* island = b2IslandArray_Get( &world->islands, body->islandId );\n\t\tif ( body->sleepTime < B2_TIME_TO_SLEEP )\n\t\t{\n\t\t\t// keep island awake\n\t\t\tint islandIndex = island->localIndex;\n\t\t\tb2SetBit( awakeIslandBitSet, islandIndex );\n\t\t}\n\t\telse if ( island->constraintRemoveCount > 0 )\n\t\t{\n\t\t\t// body wants to sleep but its island needs splitting first\n\t\t\tif ( body->sleepTime > taskContext->splitSleepTime )\n\t\t\t{\n\t\t\t\t// pick the sleepiest candidate\n\t\t\t\ttaskContext->splitIslandId = body->islandId;\n\t\t\t\ttaskContext->splitSleepTime = body->sleepTime;\n\t\t\t}\n\t\t}\n\n\t\t// Update shapes AABBs\n\t\tb2Transform transform = sim->transform;\n\t\tbool isFast = ( sim->flags & b2_isFast ) != 0;\n\t\tint shapeId = body->headShapeId;\n\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );\n\n\t\t\tif ( isFast )\n\t\t\t{\n\t\t\t\t// For fast non-bullet bodies the AABB has already been updated in b2SolveContinuous\n\t\t\t\t// For fast bullet bodies the AABB will be updated at a later stage\n\n\t\t\t\t// Add to enlarged shapes regardless of AABB changes.\n\t\t\t\t// Bit-set to keep the move array sorted\n\t\t\t\tb2SetBit( enlargedSimBitSet, simIndex );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb2AABB aabb = b2ComputeShapeAABB( shape, transform );\n\t\t\t\taabb.lowerBound.x -= speculativeDistance;\n\t\t\t\taabb.lowerBound.y -= speculativeDistance;\n\t\t\t\taabb.upperBound.x += speculativeDistance;\n\t\t\t\taabb.upperBound.y += speculativeDistance;\n\t\t\t\tshape->aabb = aabb;\n\n\t\t\t\tB2_ASSERT( shape->enlargedAABB == false );\n\n\t\t\t\tif ( b2AABB_Contains( shape->fatAABB, aabb ) == false )\n\t\t\t\t{\n\t\t\t\t\tb2AABB fatAABB;\n\t\t\t\t\tfatAABB.lowerBound.x = aabb.lowerBound.x - aabbMargin;\n\t\t\t\t\tfatAABB.lowerBound.y = aabb.lowerBound.y - aabbMargin;\n\t\t\t\t\tfatAABB.upperBound.x = aabb.upperBound.x + aabbMargin;\n\t\t\t\t\tfatAABB.upperBound.y = aabb.upperBound.y + aabbMargin;\n\t\t\t\t\tshape->fatAABB = fatAABB;\n\n\t\t\t\t\tshape->enlargedAABB = true;\n\n\t\t\t\t\t// Bit-set to keep the move array sorted\n\t\t\t\t\tb2SetBit( enlargedSimBitSet, simIndex );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tshapeId = shape->nextShapeId;\n\t\t}\n\t}\n\n\tb2TracyCZoneEnd( finalize_transfprms );\n}\n\n/*\n typedef enum b2SolverStageType\n{\n\tb2_stagePrepareJoints,\n\tb2_stagePrepareContacts,\n\tb2_stageIntegrateVelocities,\n\tb2_stageWarmStart,\n\tb2_stageSolve,\n\tb2_stageIntegratePositions,\n\tb2_stageRelax,\n\tb2_stageRestitution,\n\tb2_stageStoreImpulses\n} b2SolverStageType;\n\ntypedef enum b2SolverBlockType\n{\n\tb2_bodyBlock,\n\tb2_jointBlock,\n\tb2_contactBlock,\n\tb2_graphJointBlock,\n\tb2_graphContactBlock\n} b2SolverBlockType;\n*/\n\nstatic void b2ExecuteBlock( b2SolverStage* stage, b2StepContext* context, b2SolverBlock* block, int workerIndex )\n{\n\tb2SolverStageType stageType = stage->type;\n\tb2SolverBlockType blockType = block->blockType;\n\tint startIndex = block->startIndex;\n\tint endIndex = startIndex + block->count;\n\n\tswitch ( stageType )\n\t{\n\t\tcase b2_stagePrepareJoints:\n\t\t\tb2PrepareJointsTask( startIndex, endIndex, context );\n\t\t\tbreak;\n\n\t\tcase b2_stagePrepareContacts:\n\t\t\tb2PrepareContactsTask( startIndex, endIndex, context );\n\t\t\tbreak;\n\n\t\tcase b2_stageIntegrateVelocities:\n\t\t\tb2IntegrateVelocitiesTask( startIndex, endIndex, context );\n\t\t\tbreak;\n\n\t\tcase b2_stageWarmStart:\n\t\t\tif ( blockType == b2_graphContactBlock )\n\t\t\t{\n\t\t\t\tb2WarmStartContactsTask( startIndex, endIndex, context, stage->colorIndex );\n\t\t\t}\n\t\t\telse if ( blockType == b2_graphJointBlock )\n\t\t\t{\n\t\t\t\tb2WarmStartJointsTask( startIndex, endIndex, context, stage->colorIndex );\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase b2_stageSolve:\n\t\t\tif ( blockType == b2_graphContactBlock )\n\t\t\t{\n\t\t\t\tb2SolveContactsTask( startIndex, endIndex, context, stage->colorIndex, true );\n\t\t\t}\n\t\t\telse if ( blockType == b2_graphJointBlock )\n\t\t\t{\n\t\t\t\tb2SolveJointsTask( startIndex, endIndex, context, stage->colorIndex, true, workerIndex );\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase b2_stageIntegratePositions:\n\t\t\tb2IntegratePositionsTask( startIndex, endIndex, context );\n\t\t\tbreak;\n\n\t\tcase b2_stageRelax:\n\t\t\tif ( blockType == b2_graphContactBlock )\n\t\t\t{\n\t\t\t\tb2SolveContactsTask( startIndex, endIndex, context, stage->colorIndex, false );\n\t\t\t}\n\t\t\telse if ( blockType == b2_graphJointBlock )\n\t\t\t{\n\t\t\t\tb2SolveJointsTask( startIndex, endIndex, context, stage->colorIndex, false, workerIndex );\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase b2_stageRestitution:\n\t\t\tif ( blockType == b2_graphContactBlock )\n\t\t\t{\n\t\t\t\tb2ApplyRestitutionTask( startIndex, endIndex, context, stage->colorIndex );\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase b2_stageStoreImpulses:\n\t\t\tb2StoreImpulsesTask( startIndex, endIndex, context );\n\t\t\tbreak;\n\t}\n}\n\nstatic inline int GetWorkerStartIndex( int workerIndex, int blockCount, int workerCount )\n{\n\tif ( blockCount <= workerCount )\n\t{\n\t\treturn workerIndex < blockCount ? workerIndex : B2_NULL_INDEX;\n\t}\n\n\tint blocksPerWorker = blockCount / workerCount;\n\tint remainder = blockCount - blocksPerWorker * workerCount;\n\treturn blocksPerWorker * workerIndex + b2MinInt( remainder, workerIndex );\n}\n\nstatic void b2ExecuteStage( b2SolverStage* stage, b2StepContext* context, int previousSyncIndex, int syncIndex, int workerIndex )\n{\n\tint completedCount = 0;\n\tb2SolverBlock* blocks = stage->blocks;\n\tint blockCount = stage->blockCount;\n\n\tint expectedSyncIndex = previousSyncIndex;\n\n\tint startIndex = GetWorkerStartIndex( workerIndex, blockCount, context->workerCount );\n\tif ( startIndex == B2_NULL_INDEX )\n\t{\n\t\treturn;\n\t}\n\n\tB2_ASSERT( 0 <= startIndex && startIndex < blockCount );\n\n\tint blockIndex = startIndex;\n\n\twhile ( b2AtomicCompareExchangeInt( &blocks[blockIndex].syncIndex, expectedSyncIndex, syncIndex ) == true )\n\t{\n\t\tB2_ASSERT( stage->type != b2_stagePrepareContacts || syncIndex < 2 );\n\n\t\tB2_ASSERT( completedCount < blockCount );\n\n\t\tb2ExecuteBlock( stage, context, blocks + blockIndex, workerIndex );\n\n\t\tcompletedCount += 1;\n\t\tblockIndex += 1;\n\t\tif ( blockIndex >= blockCount )\n\t\t{\n\t\t\t// Keep looking for work\n\t\t\tblockIndex = 0;\n\t\t}\n\n\t\texpectedSyncIndex = previousSyncIndex;\n\t}\n\n\t// Search backwards for blocks\n\tblockIndex = startIndex - 1;\n\twhile ( true )\n\t{\n\t\tif ( blockIndex < 0 )\n\t\t{\n\t\t\tblockIndex = blockCount - 1;\n\t\t}\n\n\t\texpectedSyncIndex = previousSyncIndex;\n\n\t\tif ( b2AtomicCompareExchangeInt( &blocks[blockIndex].syncIndex, expectedSyncIndex, syncIndex ) == false )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tb2ExecuteBlock( stage, context, blocks + blockIndex, workerIndex );\n\t\tcompletedCount += 1;\n\t\tblockIndex -= 1;\n\t}\n\n\t(void)b2AtomicFetchAddInt( &stage->completionCount, completedCount );\n}\n\nstatic void b2ExecuteMainStage( b2SolverStage* stage, b2StepContext* context, uint32_t syncBits )\n{\n\tint blockCount = stage->blockCount;\n\tif ( blockCount == 0 )\n\t{\n\t\treturn;\n\t}\n\n\tint workerIndex = 0;\n\n\tif ( blockCount == 1 )\n\t{\n\t\tb2ExecuteBlock( stage, context, stage->blocks, workerIndex );\n\t}\n\telse\n\t{\n\t\tb2AtomicStoreU32( &context->atomicSyncBits, syncBits );\n\n\t\tint syncIndex = ( syncBits >> 16 ) & 0xFFFF;\n\t\tB2_ASSERT( syncIndex > 0 );\n\t\tint previousSyncIndex = syncIndex - 1;\n\n\t\tb2ExecuteStage( stage, context, previousSyncIndex, syncIndex, workerIndex );\n\n\t\t// todo consider using the cycle counter as well\n\t\twhile ( b2AtomicLoadInt( &stage->completionCount ) != blockCount )\n\t\t{\n\t\t\tb2Pause();\n\t\t}\n\n\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t}\n}\n\n// This should not use the thread index because thread 0 can be called twice by enkiTS.\nstatic void b2SolverTask( int startIndex, int endIndex, uint32_t threadIndexIgnore, void* taskContext )\n{\n\tB2_UNUSED( startIndex, endIndex, threadIndexIgnore );\n\n\tb2WorkerContext* workerContext = taskContext;\n\tint workerIndex = workerContext->workerIndex;\n\tb2StepContext* context = workerContext->context;\n\tint activeColorCount = context->activeColorCount;\n\tb2SolverStage* stages = context->stages;\n\tb2Profile* profile = &context->world->profile;\n\n\tif ( workerIndex == 0 )\n\t{\n\t\t// Main thread synchronizes the workers and does work itself.\n\t\t//\n\t\t// Stages are re-used by loops so that I don't need more stages for large iteration counts.\n\t\t// The sync indices grow monotonically for the body/graph/constraint groupings because they share solver blocks.\n\t\t// The stage index and sync indices are combined in to sync bits for atomic synchronization.\n\t\t// The workers need to compute the previous sync index for a given stage so that CAS works correctly. This\n\t\t// setup makes this easy to do.\n\n\t\t/*\n\t\tb2_stagePrepareJoints,\n\t\tb2_stagePrepareContacts,\n\t\tb2_stageIntegrateVelocities,\n\t\tb2_stageWarmStart,\n\t\tb2_stageSolve,\n\t\tb2_stageIntegratePositions,\n\t\tb2_stageRelax,\n\t\tb2_stageRestitution,\n\t\tb2_stageStoreImpulses\n\t\t*/\n\n\t\tuint64_t ticks = b2GetTicks();\n\n\t\tint bodySyncIndex = 1;\n\t\tint stageIndex = 0;\n\n\t\t// This stage loops over all awake joints\n\t\tuint32_t jointSyncIndex = 1;\n\t\tuint32_t syncBits = ( jointSyncIndex << 16 ) | stageIndex;\n\t\tB2_ASSERT( stages[stageIndex].type == b2_stagePrepareJoints );\n\t\tb2ExecuteMainStage( stages + stageIndex, context, syncBits );\n\t\tstageIndex += 1;\n\t\tjointSyncIndex += 1;\n\n\t\t// This stage loops over all contact constraints\n\t\tuint32_t contactSyncIndex = 1;\n\t\tsyncBits = ( contactSyncIndex << 16 ) | stageIndex;\n\t\tB2_ASSERT( stages[stageIndex].type == b2_stagePrepareContacts );\n\t\tb2ExecuteMainStage( stages + stageIndex, context, syncBits );\n\t\tstageIndex += 1;\n\t\tcontactSyncIndex += 1;\n\n\t\tint graphSyncIndex = 1;\n\n\t\t// Single-threaded overflow work. These constraints don't fit in the graph coloring.\n\t\tb2PrepareOverflowJoints( context );\n\t\tb2PrepareOverflowContacts( context );\n\n\t\tprofile->prepareConstraints += b2GetMillisecondsAndReset( &ticks );\n\n\t\tint subStepCount = context->subStepCount;\n\t\tfor ( int i = 0; i < subStepCount; ++i )\n\t\t{\n\t\t\t// stage index restarted each iteration\n\t\t\t// syncBits still increases monotonically because the upper bits increase each iteration\n\t\t\tint iterStageIndex = stageIndex;\n\n\t\t\t// integrate velocities\n\t\t\tsyncBits = ( bodySyncIndex << 16 ) | iterStageIndex;\n\t\t\tB2_ASSERT( stages[iterStageIndex].type == b2_stageIntegrateVelocities );\n\t\t\tb2ExecuteMainStage( stages + iterStageIndex, context, syncBits );\n\t\t\titerStageIndex += 1;\n\t\t\tbodySyncIndex += 1;\n\n\t\t\tprofile->integrateVelocities += b2GetMillisecondsAndReset( &ticks );\n\n\t\t\t// warm start constraints\n\t\t\tb2WarmStartOverflowJoints( context );\n\t\t\tb2WarmStartOverflowContacts( context );\n\n\t\t\tfor ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex )\n\t\t\t{\n\t\t\t\tsyncBits = ( graphSyncIndex << 16 ) | iterStageIndex;\n\t\t\t\tB2_ASSERT( stages[iterStageIndex].type == b2_stageWarmStart );\n\t\t\t\tb2ExecuteMainStage( stages + iterStageIndex, context, syncBits );\n\t\t\t\titerStageIndex += 1;\n\t\t\t}\n\t\t\tgraphSyncIndex += 1;\n\n\t\t\tprofile->warmStart += b2GetMillisecondsAndReset( &ticks );\n\n\t\t\t// solve constraints\n\t\t\tbool useBias = true;\n\n\t\t\tfor ( int j = 0; j < ITERATIONS; ++j )\n\t\t\t{\n\t\t\t\t// Overflow constraints have lower priority\n\t\t\t\tb2SolveOverflowJoints( context, useBias );\n\t\t\t\tb2SolveOverflowContacts( context, useBias );\n\n\t\t\t\tfor ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex )\n\t\t\t\t{\n\t\t\t\t\tsyncBits = ( graphSyncIndex << 16 ) | iterStageIndex;\n\t\t\t\t\tB2_ASSERT( stages[iterStageIndex].type == b2_stageSolve );\n\t\t\t\t\tb2ExecuteMainStage( stages + iterStageIndex, context, syncBits );\n\t\t\t\t\titerStageIndex += 1;\n\t\t\t\t}\n\t\t\t\tgraphSyncIndex += 1;\n\t\t\t}\n\n\t\t\tprofile->solveImpulses += b2GetMillisecondsAndReset( &ticks );\n\n\t\t\t// integrate positions\n\t\t\tB2_ASSERT( stages[iterStageIndex].type == b2_stageIntegratePositions );\n\t\t\tsyncBits = ( bodySyncIndex << 16 ) | iterStageIndex;\n\t\t\tb2ExecuteMainStage( stages + iterStageIndex, context, syncBits );\n\t\t\titerStageIndex += 1;\n\t\t\tbodySyncIndex += 1;\n\n\t\t\tprofile->integratePositions += b2GetMillisecondsAndReset( &ticks );\n\n\t\t\t// relax constraints\n\t\t\tuseBias = false;\n\t\t\tfor ( int j = 0; j < RELAX_ITERATIONS; ++j )\n\t\t\t{\n\t\t\t\tb2SolveOverflowJoints( context, useBias );\n\t\t\t\tb2SolveOverflowContacts( context, useBias );\n\n\t\t\t\tfor ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex )\n\t\t\t\t{\n\t\t\t\t\tsyncBits = ( graphSyncIndex << 16 ) | iterStageIndex;\n\t\t\t\t\tB2_ASSERT( stages[iterStageIndex].type == b2_stageRelax );\n\t\t\t\t\tb2ExecuteMainStage( stages + iterStageIndex, context, syncBits );\n\t\t\t\t\titerStageIndex += 1;\n\t\t\t\t}\n\t\t\t\tgraphSyncIndex += 1;\n\t\t\t}\n\n\t\t\tprofile->relaxImpulses += b2GetMillisecondsAndReset( &ticks );\n\t\t}\n\n\t\t// advance the stage according to the sub-stepping tasks just completed\n\t\t// integrate velocities / warm start / solve / integrate positions / relax\n\t\tstageIndex += 1 + activeColorCount + ITERATIONS * activeColorCount + 1 + RELAX_ITERATIONS * activeColorCount;\n\n\t\t// Restitution\n\t\t{\n\t\t\tb2ApplyOverflowRestitution( context );\n\n\t\t\tint iterStageIndex = stageIndex;\n\t\t\tfor ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex )\n\t\t\t{\n\t\t\t\tsyncBits = ( graphSyncIndex << 16 ) | iterStageIndex;\n\t\t\t\tB2_ASSERT( stages[iterStageIndex].type == b2_stageRestitution );\n\t\t\t\tb2ExecuteMainStage( stages + iterStageIndex, context, syncBits );\n\t\t\t\titerStageIndex += 1;\n\t\t\t}\n\t\t\t// graphSyncIndex += 1;\n\t\t\tstageIndex += activeColorCount;\n\t\t}\n\n\t\tprofile->applyRestitution += b2GetMillisecondsAndReset( &ticks );\n\n\t\tb2StoreOverflowImpulses( context );\n\n\t\tsyncBits = ( contactSyncIndex << 16 ) | stageIndex;\n\t\tB2_ASSERT( stages[stageIndex].type == b2_stageStoreImpulses );\n\t\tb2ExecuteMainStage( stages + stageIndex, context, syncBits );\n\n\t\tprofile->storeImpulses += b2GetMillisecondsAndReset( &ticks );\n\n\t\t// Signal workers to finish\n\t\tb2AtomicStoreU32( &context->atomicSyncBits, UINT_MAX );\n\n\t\tB2_ASSERT( stageIndex + 1 == context->stageCount );\n\t\treturn;\n\t}\n\n\t// Worker spins and waits for work\n\tuint32_t lastSyncBits = 0;\n\t// uint64_t maxSpinTime = 10;\n\twhile ( true )\n\t{\n\t\t// Spin until main thread bumps changes the sync bits. This can waste significant time overall, but it is necessary for\n\t\t// parallel simulation with graph coloring.\n\t\tuint32_t syncBits;\n\t\tint spinCount = 0;\n\t\twhile ( ( syncBits = b2AtomicLoadU32( &context->atomicSyncBits ) ) == lastSyncBits )\n\t\t{\n\t\t\tif ( spinCount > 5 )\n\t\t\t{\n\t\t\t\tb2Yield();\n\t\t\t\tspinCount = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Using the cycle counter helps to account for variation in mm_pause timing across different\n\t\t\t\t// CPUs. However, this is X64 only.\n\t\t\t\t// uint64_t prev = __rdtsc();\n\t\t\t\t// do\n\t\t\t\t//{\n\t\t\t\t//\tb2Pause();\n\t\t\t\t//}\n\t\t\t\t// while ((__rdtsc() - prev) < maxSpinTime);\n\t\t\t\t// maxSpinTime += 10;\n\t\t\t\tb2Pause();\n\t\t\t\tb2Pause();\n\t\t\t\tspinCount += 1;\n\t\t\t}\n\t\t}\n\n\t\tif ( syncBits == UINT_MAX )\n\t\t{\n\t\t\t// sentinel hit\n\t\t\tbreak;\n\t\t}\n\n\t\tint stageIndex = syncBits & 0xFFFF;\n\t\tB2_ASSERT( stageIndex < context->stageCount );\n\n\t\tint syncIndex = ( syncBits >> 16 ) & 0xFFFF;\n\t\tB2_ASSERT( syncIndex > 0 );\n\n\t\tint previousSyncIndex = syncIndex - 1;\n\n\t\tb2SolverStage* stage = stages + stageIndex;\n\t\tb2ExecuteStage( stage, context, previousSyncIndex, syncIndex, workerIndex );\n\n\t\tlastSyncBits = syncBits;\n\t}\n}\n\nstatic void b2BulletBodyTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )\n{\n\tB2_UNUSED( threadIndex );\n\n\tb2TracyCZoneNC( bullet_body_task, \"Bullet\", b2_colorLightSkyBlue, true );\n\n\tb2StepContext* stepContext = context;\n\tb2TaskContext* taskContext = b2TaskContextArray_Get( &stepContext->world->taskContexts, threadIndex );\n\n\tB2_ASSERT( startIndex <= endIndex );\n\n\tfor ( int i = startIndex; i < endIndex; ++i )\n\t{\n\t\tint simIndex = stepContext->bulletBodies[i];\n\t\tb2SolveContinuous( stepContext->world, simIndex, taskContext );\n\t}\n\n\tb2TracyCZoneEnd( bullet_body_task );\n}\n\n#if B2_SIMD_WIDTH == 8\n#define B2_SIMD_SHIFT 3\n#elif B2_SIMD_WIDTH == 4\n#define B2_SIMD_SHIFT 2\n#else\n#define B2_SIMD_SHIFT 0\n#endif\n\n// Solve with graph coloring\nvoid b2Solve( b2World* world, b2StepContext* stepContext )\n{\n\tworld->stepIndex += 1;\n\n\t// Are there any awake bodies? This scenario should not be important for profiling.\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tint awakeBodyCount = awakeSet->bodySims.count;\n\tif ( awakeBodyCount == 0 )\n\t{\n\t\t// Nothing to simulate, however the tree rebuild must be finished.\n\t\tif ( world->userTreeTask != NULL )\n\t\t{\n\t\t\tworld->finishTaskFcn( world->userTreeTask, world->userTaskContext );\n\t\t\tworld->userTreeTask = NULL;\n\t\t\tworld->activeTaskCount -= 1;\n\t\t}\n\n\t\tb2ValidateNoEnlarged( &world->broadPhase );\n\t\treturn;\n\t}\n\n\t// Solve constraints using graph coloring\n\t{\n\t\t// Prepare buffers for bullets\n\t\tb2AtomicStoreInt( &stepContext->bulletBodyCount, 0 );\n\t\tstepContext->bulletBodies = b2AllocateArenaItem( &world->arena, awakeBodyCount * sizeof( int ), \"bullet bodies\" );\n\n\t\tb2TracyCZoneNC( prepare_stages, \"Prepare Stages\", b2_colorDarkOrange, true );\n\t\tuint64_t prepareTicks = b2GetTicks();\n\n\t\tb2ConstraintGraph* graph = &world->constraintGraph;\n\t\tb2GraphColor* colors = graph->colors;\n\n\t\tstepContext->sims = awakeSet->bodySims.data;\n\t\tstepContext->states = awakeSet->bodyStates.data;\n\n\t\t// count contacts, joints, and colors\n\t\tint awakeJointCount = 0;\n\t\tint activeColorCount = 0;\n\t\tfor ( int i = 0; i < B2_GRAPH_COLOR_COUNT - 1; ++i )\n\t\t{\n\t\t\tint perColorContactCount = colors[i].contactSims.count;\n\t\t\tint perColorJointCount = colors[i].jointSims.count;\n\t\t\tint occupancyCount = perColorContactCount + perColorJointCount;\n\t\t\tactiveColorCount += occupancyCount > 0 ? 1 : 0;\n\t\t\tawakeJointCount += perColorJointCount;\n\t\t}\n\n\t\t// prepare for move events\n\t\tb2BodyMoveEventArray_Resize( &world->bodyMoveEvents, awakeBodyCount );\n\n\t\t// A block is a range of tasks, a start index and count as a sub-array.\n\t\t// Each worker receives at most M blocks of work. The workers may receive less blocks if there is not sufficient work.\n\t\t// Each block of work has a minimum number of elements (block size). This in turn may limit the number of blocks.\n\t\t// If there are many elements then the block size is increased so there are still at most M blocks of work per worker.\n\t\t// M is a tunable number that has two goals:\n\t\t// 1. keep M small to reduce overhead\n\t\t// 2. keep M large enough for other workers to be able to steal work\n\t\t// The block size is a power of two to make math efficient.\n\n\t\tint workerCount = world->workerCount;\n\n\t\t// todo 4 seems good but more benchmarking would be good\n\t\tconst int blocksPerWorker = 4;\n\n\t\tconst int maxBlockCount = blocksPerWorker * workerCount;\n\n\t\t// Configure blocks for tasks that parallel-for bodies\n\t\tint bodyBlockSize = 1 << 5;\n\t\tint bodyBlockCount;\n\t\tif ( awakeBodyCount > bodyBlockSize * maxBlockCount )\n\t\t{\n\t\t\t// Too many blocks, increase block size\n\t\t\tbodyBlockSize = awakeBodyCount / maxBlockCount;\n\t\t\tbodyBlockCount = maxBlockCount;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Divide by bodyBlockSize (32) and ensure there is at least one block\n\t\t\tbodyBlockCount = ( ( awakeBodyCount - 1 ) >> 5 ) + 1;\n\t\t}\n\n\t\t// Configure blocks for tasks parallel-for each active graph color\n\t\t// The blocks are a mix of SIMD contact blocks and joint blocks\n\t\tint activeColorIndices[B2_GRAPH_COLOR_COUNT];\n\n\t\tint colorContactCounts[B2_GRAPH_COLOR_COUNT];\n\t\tint colorContactBlockSizes[B2_GRAPH_COLOR_COUNT];\n\t\tint colorContactBlockCounts[B2_GRAPH_COLOR_COUNT];\n\n\t\tint colorJointCounts[B2_GRAPH_COLOR_COUNT];\n\t\tint colorJointBlockSizes[B2_GRAPH_COLOR_COUNT];\n\t\tint colorJointBlockCounts[B2_GRAPH_COLOR_COUNT];\n\n\t\tint graphBlockCount = 0;\n\n\t\t// c is the active color index\n\t\tint simdContactCount = 0;\n\t\tint c = 0;\n\t\tfor ( int i = 0; i < B2_GRAPH_COLOR_COUNT - 1; ++i )\n\t\t{\n\t\t\tint colorContactCount = colors[i].contactSims.count;\n\t\t\tint colorJointCount = colors[i].jointSims.count;\n\n\t\t\tif ( colorContactCount + colorJointCount > 0 )\n\t\t\t{\n\t\t\t\tactiveColorIndices[c] = i;\n\n\t\t\t\t// 4/8-way SIMD\n\t\t\t\tint colorContactCountSIMD = colorContactCount > 0 ? ( ( colorContactCount - 1 ) >> B2_SIMD_SHIFT ) + 1 : 0;\n\n\t\t\t\tcolorContactCounts[c] = colorContactCountSIMD;\n\n\t\t\t\t// determine the number of contact work blocks for this color\n\t\t\t\tif ( colorContactCountSIMD > blocksPerWorker * maxBlockCount )\n\t\t\t\t{\n\t\t\t\t\t// too many contact blocks per worker, so make bigger blocks\n\t\t\t\t\tcolorContactBlockSizes[c] = colorContactCountSIMD / maxBlockCount;\n\t\t\t\t\tcolorContactBlockCounts[c] = maxBlockCount;\n\t\t\t\t}\n\t\t\t\telse if ( colorContactCountSIMD > 0 )\n\t\t\t\t{\n\t\t\t\t\t// dividing by blocksPerWorker (4)\n\t\t\t\t\tcolorContactBlockSizes[c] = blocksPerWorker;\n\n\t\t\t\t\t// This math makes sure there is at least one block\n\t\t\t\t\t//colorContactBlockCounts[c] = ( ( colorContactCountSIMD - 1 ) >> 2 ) + 1;\n\t\t\t\t\tcolorContactBlockCounts[c] = ( ( colorContactCountSIMD - 1 ) / blocksPerWorker ) + 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// no contacts in this color\n\t\t\t\t\tcolorContactBlockSizes[c] = 0;\n\t\t\t\t\tcolorContactBlockCounts[c] = 0;\n\t\t\t\t}\n\n\t\t\t\tcolorJointCounts[c] = colorJointCount;\n\n\t\t\t\t// determine number of joint work blocks for this color\n\t\t\t\tif ( colorJointCount > blocksPerWorker * maxBlockCount )\n\t\t\t\t{\n\t\t\t\t\t// too many joint blocks\n\t\t\t\t\tcolorJointBlockSizes[c] = colorJointCount / maxBlockCount;\n\t\t\t\t\tcolorJointBlockCounts[c] = maxBlockCount;\n\t\t\t\t}\n\t\t\t\telse if ( colorJointCount > 0 )\n\t\t\t\t{\n\t\t\t\t\t// dividing by blocksPerWorker (4)\n\t\t\t\t\tcolorJointBlockSizes[c] = blocksPerWorker;\n\t\t\t\t\t//colorJointBlockCounts[c] = ( ( colorJointCount - 1 ) >> 2 ) + 1;\n\t\t\t\t\tcolorJointBlockCounts[c] = ( ( colorJointCount - 1 ) / 4 ) + 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcolorJointBlockSizes[c] = 0;\n\t\t\t\t\tcolorJointBlockCounts[c] = 0;\n\t\t\t\t}\n\n\t\t\t\tgraphBlockCount += colorContactBlockCounts[c] + colorJointBlockCounts[c];\n\t\t\t\tsimdContactCount += colorContactCountSIMD;\n\t\t\t\tc += 1;\n\t\t\t}\n\t\t}\n\t\tactiveColorCount = c;\n\n\t\t// Gather contact pointers for easy parallel-for traversal. Some may be NULL due to SIMD remainders.\n\t\tb2ContactSim** contacts =\n\t\t\tb2AllocateArenaItem( &world->arena, B2_SIMD_WIDTH * simdContactCount * sizeof( b2ContactSim* ), \"contact pointers\" );\n\n\t\t// Gather joint pointers for easy parallel-for traversal.\n\t\tb2JointSim** joints = b2AllocateArenaItem( &world->arena, awakeJointCount * sizeof( b2JointSim* ), \"joint pointers\" );\n\n\t\tint simdConstraintSize = b2GetContactConstraintSIMDByteCount();\n\t\tb2ContactConstraintSIMD* simdContactConstraints =\n\t\t\tb2AllocateArenaItem( &world->arena, simdContactCount * simdConstraintSize, \"contact constraint\" );\n\n\t\tint overflowContactCount = colors[B2_OVERFLOW_INDEX].contactSims.count;\n\t\tb2ContactConstraint* overflowContactConstraints = b2AllocateArenaItem(\n\t\t\t&world->arena, overflowContactCount * sizeof( b2ContactConstraint ), \"overflow contact constraint\" );\n\n\t\tgraph->colors[B2_OVERFLOW_INDEX].overflowConstraints = overflowContactConstraints;\n\n\t\t// Distribute transient constraints to each graph color and build flat arrays of contact and joint pointers\n\t\t{\n\t\t\tint contactBase = 0;\n\t\t\tint jointBase = 0;\n\t\t\tfor ( int i = 0; i < activeColorCount; ++i )\n\t\t\t{\n\t\t\t\tint j = activeColorIndices[i];\n\t\t\t\tb2GraphColor* color = colors + j;\n\n\t\t\t\tint colorContactCount = color->contactSims.count;\n\n\t\t\t\tif ( colorContactCount == 0 )\n\t\t\t\t{\n\t\t\t\t\tcolor->simdConstraints = NULL;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcolor->simdConstraints =\n\t\t\t\t\t\t(b2ContactConstraintSIMD*)( (uint8_t*)simdContactConstraints + contactBase * simdConstraintSize );\n\n\t\t\t\t\tfor ( int k = 0; k < colorContactCount; ++k )\n\t\t\t\t\t{\n\t\t\t\t\t\tcontacts[B2_SIMD_WIDTH * contactBase + k] = color->contactSims.data + k;\n\t\t\t\t\t}\n\n\t\t\t\t\t// remainder\n\t\t\t\t\tint colorContactCountSIMD = ( ( colorContactCount - 1 ) >> B2_SIMD_SHIFT ) + 1;\n\t\t\t\t\tfor ( int k = colorContactCount; k < B2_SIMD_WIDTH * colorContactCountSIMD; ++k )\n\t\t\t\t\t{\n\t\t\t\t\t\tcontacts[B2_SIMD_WIDTH * contactBase + k] = NULL;\n\t\t\t\t\t}\n\n\t\t\t\t\tcontactBase += colorContactCountSIMD;\n\t\t\t\t}\n\n\t\t\t\tint colorJointCount = color->jointSims.count;\n\t\t\t\tfor ( int k = 0; k < colorJointCount; ++k )\n\t\t\t\t{\n\t\t\t\t\tjoints[jointBase + k] = color->jointSims.data + k;\n\t\t\t\t}\n\t\t\t\tjointBase += colorJointCount;\n\t\t\t}\n\n\t\t\tB2_ASSERT( contactBase == simdContactCount );\n\t\t\tB2_ASSERT( jointBase == awakeJointCount );\n\t\t}\n\n\t\t// Define work blocks for preparing contacts and storing contact impulses\n\t\tint contactBlockSize = blocksPerWorker;\n\t\t//int contactBlockCount = simdContactCount > 0 ? ( ( simdContactCount - 1 ) >> 2 ) + 1 : 0;\n\t\tint contactBlockCount = simdContactCount > 0 ? ( ( simdContactCount - 1 ) / blocksPerWorker ) + 1 : 0;\n\t\tif ( simdContactCount > contactBlockSize * maxBlockCount )\n\t\t{\n\t\t\t// Too many blocks, increase block size\n\t\t\tcontactBlockSize = simdContactCount / maxBlockCount;\n\t\t\tcontactBlockCount = maxBlockCount;\n\t\t}\n\n\t\t// Define work blocks for preparing joints\n\t\tint jointBlockSize = blocksPerWorker;\n\t\t//int jointBlockCount = awakeJointCount > 0 ? ( ( awakeJointCount - 1 ) >> 2 ) + 1 : 0;\n\t\tint jointBlockCount = awakeJointCount > 0 ? ( ( awakeJointCount - 1 ) / blocksPerWorker ) + 1 : 0;\n\t\tif ( awakeJointCount > jointBlockSize * maxBlockCount )\n\t\t{\n\t\t\t// Too many blocks, increase block size\n\t\t\tjointBlockSize = awakeJointCount / maxBlockCount;\n\t\t\tjointBlockCount = maxBlockCount;\n\t\t}\n\n\t\tint stageCount = 0;\n\n\t\t// b2_stagePrepareJoints\n\t\tstageCount += 1;\n\t\t// b2_stagePrepareContacts\n\t\tstageCount += 1;\n\t\t// b2_stageIntegrateVelocities\n\t\tstageCount += 1;\n\t\t// b2_stageWarmStart\n\t\tstageCount += activeColorCount;\n\t\t// b2_stageSolve\n\t\tstageCount += ITERATIONS * activeColorCount;\n\t\t// b2_stageIntegratePositions\n\t\tstageCount += 1;\n\t\t// b2_stageRelax\n\t\tstageCount += RELAX_ITERATIONS * activeColorCount;\n\t\t// b2_stageRestitution\n\t\tstageCount += activeColorCount;\n\t\t// b2_stageStoreImpulses\n\t\tstageCount += 1;\n\n\t\tb2SolverStage* stages = b2AllocateArenaItem( &world->arena, stageCount * sizeof( b2SolverStage ), \"stages\" );\n\t\tb2SolverBlock* bodyBlocks = b2AllocateArenaItem( &world->arena, bodyBlockCount * sizeof( b2SolverBlock ), \"body blocks\" );\n\t\tb2SolverBlock* contactBlocks =\n\t\t\tb2AllocateArenaItem( &world->arena, contactBlockCount * sizeof( b2SolverBlock ), \"contact blocks\" );\n\t\tb2SolverBlock* jointBlocks =\n\t\t\tb2AllocateArenaItem( &world->arena, jointBlockCount * sizeof( b2SolverBlock ), \"joint blocks\" );\n\t\tb2SolverBlock* graphBlocks =\n\t\t\tb2AllocateArenaItem( &world->arena, graphBlockCount * sizeof( b2SolverBlock ), \"graph blocks\" );\n\n\t\t// Split an awake island. This modifies:\n\t\t// - stack allocator\n\t\t// - world island array and solver set\n\t\t// - island indices on bodies, contacts, and joints\n\t\t// I'm squeezing this task in here because it may be expensive and this is a safe place to put it.\n\t\t// Note: cannot split islands in parallel with FinalizeBodies\n\t\tvoid* splitIslandTask = NULL;\n\t\tif ( world->splitIslandId != B2_NULL_INDEX )\n\t\t{\n\t\t\tsplitIslandTask = world->enqueueTaskFcn( &b2SplitIslandTask, 1, 1, world, world->userTaskContext );\n\t\t\tworld->taskCount += 1;\n\t\t\tworld->activeTaskCount += splitIslandTask == NULL ? 0 : 1;\n\t\t}\n\n\t\t// Prepare body work blocks\n\t\tfor ( int i = 0; i < bodyBlockCount; ++i )\n\t\t{\n\t\t\tb2SolverBlock* block = bodyBlocks + i;\n\t\t\tblock->startIndex = i * bodyBlockSize;\n\t\t\tblock->count = (int16_t)bodyBlockSize;\n\t\t\tblock->blockType = b2_bodyBlock;\n\t\t\tb2AtomicStoreInt( &block->syncIndex, 0 );\n\t\t}\n\t\tbodyBlocks[bodyBlockCount - 1].count = (int16_t)( awakeBodyCount - ( bodyBlockCount - 1 ) * bodyBlockSize );\n\n\t\t// Prepare joint work blocks\n\t\tfor ( int i = 0; i < jointBlockCount; ++i )\n\t\t{\n\t\t\tb2SolverBlock* block = jointBlocks + i;\n\t\t\tblock->startIndex = i * jointBlockSize;\n\t\t\tblock->count = (int16_t)jointBlockSize;\n\t\t\tblock->blockType = b2_jointBlock;\n\t\t\tb2AtomicStoreInt( &block->syncIndex, 0 );\n\t\t}\n\n\t\tif ( jointBlockCount > 0 )\n\t\t{\n\t\t\tjointBlocks[jointBlockCount - 1].count = (int16_t)( awakeJointCount - ( jointBlockCount - 1 ) * jointBlockSize );\n\t\t}\n\n\t\t// Prepare contact work blocks\n\t\tfor ( int i = 0; i < contactBlockCount; ++i )\n\t\t{\n\t\t\tb2SolverBlock* block = contactBlocks + i;\n\t\t\tblock->startIndex = i * contactBlockSize;\n\t\t\tblock->count = (int16_t)contactBlockSize;\n\t\t\tblock->blockType = b2_contactBlock;\n\t\t\tb2AtomicStoreInt( &block->syncIndex, 0 );\n\t\t}\n\n\t\tif ( contactBlockCount > 0 )\n\t\t{\n\t\t\tcontactBlocks[contactBlockCount - 1].count =\n\t\t\t\t(int16_t)( simdContactCount - ( contactBlockCount - 1 ) * contactBlockSize );\n\t\t}\n\n\t\t// Prepare graph work blocks\n\t\tb2SolverBlock* graphColorBlocks[B2_GRAPH_COLOR_COUNT] = {0};\n\t\tb2SolverBlock* baseGraphBlock = graphBlocks;\n\n\t\tfor ( int i = 0; i < activeColorCount; ++i )\n\t\t{\n\t\t\tgraphColorBlocks[i] = baseGraphBlock;\n\n\t\t\tint colorJointBlockCount = colorJointBlockCounts[i];\n\t\t\tint colorJointBlockSize = colorJointBlockSizes[i];\n\t\t\tfor ( int j = 0; j < colorJointBlockCount; ++j )\n\t\t\t{\n\t\t\t\tb2SolverBlock* block = baseGraphBlock + j;\n\t\t\t\tblock->startIndex = j * colorJointBlockSize;\n\t\t\t\tblock->count = (int16_t)colorJointBlockSize;\n\t\t\t\tblock->blockType = b2_graphJointBlock;\n\t\t\t\tb2AtomicStoreInt( &block->syncIndex, 0 );\n\t\t\t}\n\n\t\t\tif ( colorJointBlockCount > 0 )\n\t\t\t{\n\t\t\t\tbaseGraphBlock[colorJointBlockCount - 1].count =\n\t\t\t\t\t(int16_t)( colorJointCounts[i] - ( colorJointBlockCount - 1 ) * colorJointBlockSize );\n\t\t\t\tbaseGraphBlock += colorJointBlockCount;\n\t\t\t}\n\n\t\t\tint colorContactBlockCount = colorContactBlockCounts[i];\n\t\t\tint colorContactBlockSize = colorContactBlockSizes[i];\n\t\t\tfor ( int j = 0; j < colorContactBlockCount; ++j )\n\t\t\t{\n\t\t\t\tb2SolverBlock* block = baseGraphBlock + j;\n\t\t\t\tblock->startIndex = j * colorContactBlockSize;\n\t\t\t\tblock->count = (int16_t)colorContactBlockSize;\n\t\t\t\tblock->blockType = b2_graphContactBlock;\n\t\t\t\tb2AtomicStoreInt( &block->syncIndex, 0 );\n\t\t\t}\n\n\t\t\tif ( colorContactBlockCount > 0 )\n\t\t\t{\n\t\t\t\tbaseGraphBlock[colorContactBlockCount - 1].count =\n\t\t\t\t\t(int16_t)( colorContactCounts[i] - ( colorContactBlockCount - 1 ) * colorContactBlockSize );\n\t\t\t\tbaseGraphBlock += colorContactBlockCount;\n\t\t\t}\n\t\t}\n\n\t\tB2_ASSERT( (ptrdiff_t)( baseGraphBlock - graphBlocks ) == graphBlockCount );\n\n\t\tb2SolverStage* stage = stages;\n\n\t\t// Prepare joints\n\t\tstage->type = b2_stagePrepareJoints;\n\t\tstage->blocks = jointBlocks;\n\t\tstage->blockCount = jointBlockCount;\n\t\tstage->colorIndex = -1;\n\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t\tstage += 1;\n\n\t\t// Prepare contacts\n\t\tstage->type = b2_stagePrepareContacts;\n\t\tstage->blocks = contactBlocks;\n\t\tstage->blockCount = contactBlockCount;\n\t\tstage->colorIndex = -1;\n\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t\tstage += 1;\n\n\t\t// Integrate velocities\n\t\tstage->type = b2_stageIntegrateVelocities;\n\t\tstage->blocks = bodyBlocks;\n\t\tstage->blockCount = bodyBlockCount;\n\t\tstage->colorIndex = -1;\n\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t\tstage += 1;\n\n\t\t// Warm start\n\t\tfor ( int i = 0; i < activeColorCount; ++i )\n\t\t{\n\t\t\tstage->type = b2_stageWarmStart;\n\t\t\tstage->blocks = graphColorBlocks[i];\n\t\t\tstage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i];\n\t\t\tstage->colorIndex = activeColorIndices[i];\n\t\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t\t\tstage += 1;\n\t\t}\n\n\t\t// Solve graph\n\t\tfor ( int j = 0; j < ITERATIONS; ++j )\n\t\t{\n\t\t\tfor ( int i = 0; i < activeColorCount; ++i )\n\t\t\t{\n\t\t\t\tstage->type = b2_stageSolve;\n\t\t\t\tstage->blocks = graphColorBlocks[i];\n\t\t\t\tstage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i];\n\t\t\t\tstage->colorIndex = activeColorIndices[i];\n\t\t\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t\t\t\tstage += 1;\n\t\t\t}\n\t\t}\n\n\t\t// Integrate positions\n\t\tstage->type = b2_stageIntegratePositions;\n\t\tstage->blocks = bodyBlocks;\n\t\tstage->blockCount = bodyBlockCount;\n\t\tstage->colorIndex = -1;\n\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t\tstage += 1;\n\n\t\t// Relax constraints\n\t\tfor ( int j = 0; j < RELAX_ITERATIONS; ++j )\n\t\t{\n\t\t\tfor ( int i = 0; i < activeColorCount; ++i )\n\t\t\t{\n\t\t\t\tstage->type = b2_stageRelax;\n\t\t\t\tstage->blocks = graphColorBlocks[i];\n\t\t\t\tstage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i];\n\t\t\t\tstage->colorIndex = activeColorIndices[i];\n\t\t\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t\t\t\tstage += 1;\n\t\t\t}\n\t\t}\n\n\t\t// Restitution\n\t\t// Note: joint blocks mixed in, could have joint limit restitution\n\t\tfor ( int i = 0; i < activeColorCount; ++i )\n\t\t{\n\t\t\tstage->type = b2_stageRestitution;\n\t\t\tstage->blocks = graphColorBlocks[i];\n\t\t\tstage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i];\n\t\t\tstage->colorIndex = activeColorIndices[i];\n\t\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t\t\tstage += 1;\n\t\t}\n\n\t\t// Store impulses\n\t\tstage->type = b2_stageStoreImpulses;\n\t\tstage->blocks = contactBlocks;\n\t\tstage->blockCount = contactBlockCount;\n\t\tstage->colorIndex = -1;\n\t\tb2AtomicStoreInt( &stage->completionCount, 0 );\n\t\tstage += 1;\n\n\t\tB2_ASSERT( (int)( stage - stages ) == stageCount );\n\n\t\tB2_ASSERT( workerCount <= B2_MAX_WORKERS );\n\t\tb2WorkerContext workerContext[B2_MAX_WORKERS];\n\n\t\tstepContext->graph = graph;\n\t\tstepContext->joints = joints;\n\t\tstepContext->contacts = contacts;\n\t\tstepContext->simdContactConstraints = simdContactConstraints;\n\t\tstepContext->activeColorCount = activeColorCount;\n\t\tstepContext->workerCount = workerCount;\n\t\tstepContext->stageCount = stageCount;\n\t\tstepContext->stages = stages;\n\t\tb2AtomicStoreU32( &stepContext->atomicSyncBits, 0 );\n\n\t\tworld->profile.prepareStages = b2GetMillisecondsAndReset( &prepareTicks );\n\t\tb2TracyCZoneEnd( prepare_stages );\n\n\t\tb2TracyCZoneNC( solve_constraints, \"Solve Constraints\", b2_colorIndigo, true );\n\t\tuint64_t constraintTicks = b2GetTicks();\n\n\t\t// Must use worker index because thread 0 can be assigned multiple tasks by enkiTS\n\t\tint jointIdCapacity = b2GetIdCapacity( &world->jointIdPool );\n\t\tfor ( int i = 0; i < workerCount; ++i )\n\t\t{\n\t\t\tb2TaskContext* taskContext = b2TaskContextArray_Get( &world->taskContexts, i );\n\t\t\tb2SetBitCountAndClear( &taskContext->jointStateBitSet, jointIdCapacity );\n\n\t\t\tworkerContext[i].context = stepContext;\n\t\t\tworkerContext[i].workerIndex = i;\n\t\t\tworkerContext[i].userTask = world->enqueueTaskFcn( b2SolverTask, 1, 1, workerContext + i, world->userTaskContext );\n\t\t\tworld->taskCount += 1;\n\t\t\tworld->activeTaskCount += workerContext[i].userTask == NULL ? 0 : 1;\n\t\t}\n\n\t\t// Finish island split\n\t\tif ( splitIslandTask != NULL )\n\t\t{\n\t\t\tworld->finishTaskFcn( splitIslandTask, world->userTaskContext );\n\t\t\tworld->activeTaskCount -= 1;\n\t\t}\n\t\tworld->splitIslandId = B2_NULL_INDEX;\n\n\t\t// Finish constraint solve\n\t\tfor ( int i = 0; i < workerCount; ++i )\n\t\t{\n\t\t\tif ( workerContext[i].userTask != NULL )\n\t\t\t{\n\t\t\t\tworld->finishTaskFcn( workerContext[i].userTask, world->userTaskContext );\n\t\t\t\tworld->activeTaskCount -= 1;\n\t\t\t}\n\t\t}\n\n\t\tworld->profile.solveConstraints = b2GetMillisecondsAndReset( &constraintTicks );\n\t\tb2TracyCZoneEnd( solve_constraints );\n\n\t\tb2TracyCZoneNC( update_transforms, \"Update Transforms\", b2_colorMediumSeaGreen, true );\n\t\tuint64_t transformTicks = b2GetTicks();\n\n\t\t// Prepare contact, enlarged body, and island bit sets used in body finalization.\n\t\tint awakeIslandCount = awakeSet->islandSims.count;\n\t\tfor ( int i = 0; i < world->workerCount; ++i )\n\t\t{\n\t\t\tb2TaskContext* taskContext = world->taskContexts.data + i;\n\t\t\tb2SensorHitArray_Clear( &taskContext->sensorHits );\n\t\t\tb2SetBitCountAndClear( &taskContext->enlargedSimBitSet, awakeBodyCount );\n\t\t\tb2SetBitCountAndClear( &taskContext->awakeIslandBitSet, awakeIslandCount );\n\t\t\ttaskContext->splitIslandId = B2_NULL_INDEX;\n\t\t\ttaskContext->splitSleepTime = 0.0f;\n\t\t}\n\n\t\t// Finalize bodies. Must happen after the constraint solver and after island splitting.\n\t\tvoid* finalizeBodiesTask =\n\t\t\tworld->enqueueTaskFcn( b2FinalizeBodiesTask, awakeBodyCount, 64, stepContext, world->userTaskContext );\n\t\tworld->taskCount += 1;\n\t\tif ( finalizeBodiesTask != NULL )\n\t\t{\n\t\t\tworld->finishTaskFcn( finalizeBodiesTask, world->userTaskContext );\n\t\t}\n\n\t\tb2FreeArenaItem( &world->arena, graphBlocks );\n\t\tb2FreeArenaItem( &world->arena, jointBlocks );\n\t\tb2FreeArenaItem( &world->arena, contactBlocks );\n\t\tb2FreeArenaItem( &world->arena, bodyBlocks );\n\t\tb2FreeArenaItem( &world->arena, stages );\n\t\tb2FreeArenaItem( &world->arena, overflowContactConstraints );\n\t\tb2FreeArenaItem( &world->arena, simdContactConstraints );\n\t\tb2FreeArenaItem( &world->arena, joints );\n\t\tb2FreeArenaItem( &world->arena, contacts );\n\n\t\tworld->profile.transforms = b2GetMilliseconds( transformTicks );\n\t\tb2TracyCZoneEnd( update_transforms );\n\t}\n\n\t// Report joint events\n\t{\n\t\tb2TracyCZoneNC( joint_events, \"Joint Events\", b2_colorPeru, true );\n\t\tuint64_t jointEventTicks = b2GetTicks();\n\n\t\t// Gather bits for all joints that have force/torque events\n\t\tb2BitSet* jointStateBitSet = &world->taskContexts.data[0].jointStateBitSet;\n\t\tfor ( int i = 1; i < world->workerCount; ++i )\n\t\t{\n\t\t\tb2InPlaceUnion( jointStateBitSet, &world->taskContexts.data[i].jointStateBitSet );\n\t\t}\n\n\t\t{\n\t\t\tuint32_t wordCount = jointStateBitSet->blockCount;\n\t\t\tuint64_t* bits = jointStateBitSet->bits;\n\n\t\t\tb2Joint* jointArray = world->joints.data;\n\t\t\tuint16_t worldIndex0 = world->worldId;\n\n\t\t\tfor ( uint32_t k = 0; k < wordCount; ++k )\n\t\t\t{\n\t\t\t\tuint64_t word = bits[k];\n\t\t\t\twhile ( word != 0 )\n\t\t\t\t{\n\t\t\t\t\tuint32_t ctz = b2CTZ64( word );\n\t\t\t\t\tint jointId = (int)( 64 * k + ctz );\n\n\t\t\t\t\tB2_ASSERT( jointId < world->joints.capacity );\n\n\t\t\t\t\tb2Joint* joint = jointArray + jointId;\n\n\t\t\t\t\tB2_ASSERT( joint->setIndex == b2_awakeSet );\n\n\t\t\t\t\tb2JointEvent event = {\n\t\t\t\t\t\t.jointId =\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t.index1 = jointId + 1,\n\t\t\t\t\t\t\t\t.world0 = worldIndex0,\n\t\t\t\t\t\t\t\t.generation = joint->generation,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t.userData = joint->userData,\n\t\t\t\t\t};\n\n\t\t\t\t\tb2JointEventArray_Push( &world->jointEvents, event );\n\n\t\t\t\t\t// Clear the smallest set bit\n\t\t\t\t\tword = word & ( word - 1 );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tworld->profile.jointEvents = b2GetMilliseconds( jointEventTicks );\n\t\tb2TracyCZoneEnd( joint_events );\n\t}\n\n\t// Report hit events\n\t// todo_erin perhaps optimize this with a bitset\n\t// todo_erin perhaps do this in parallel with other work below\n\t{\n\t\tb2TracyCZoneNC( hit_events, \"Hit Events\", b2_colorRosyBrown, true );\n\t\tuint64_t hitTicks = b2GetTicks();\n\n\t\tB2_ASSERT( world->contactHitEvents.count == 0 );\n\n\t\tfloat threshold = world->hitEventThreshold;\n\t\tb2GraphColor* colors = world->constraintGraph.colors;\n\t\tfor ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )\n\t\t{\n\t\t\tb2GraphColor* color = colors + i;\n\t\t\tint contactCount = color->contactSims.count;\n\t\t\tb2ContactSim* contactSims = color->contactSims.data;\n\t\t\tfor ( int j = 0; j < contactCount; ++j )\n\t\t\t{\n\t\t\t\tb2ContactSim* contactSim = contactSims + j;\n\t\t\t\tif ( ( contactSim->simFlags & b2_simEnableHitEvent ) == 0 )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tb2ContactHitEvent event = { 0 };\n\t\t\t\tevent.approachSpeed = threshold;\n\n\t\t\t\tbool hit = false;\n\t\t\t\tint pointCount = contactSim->manifold.pointCount;\n\t\t\t\tfor ( int k = 0; k < pointCount; ++k )\n\t\t\t\t{\n\t\t\t\t\tb2ManifoldPoint* mp = contactSim->manifold.points + k;\n\t\t\t\t\tfloat approachSpeed = -mp->normalVelocity;\n\n\t\t\t\t\t// Need to check total impulse because the point may be speculative and not colliding\n\t\t\t\t\tif ( approachSpeed > event.approachSpeed && mp->totalNormalImpulse > 0.0f )\n\t\t\t\t\t{\n\t\t\t\t\t\tevent.approachSpeed = approachSpeed;\n\t\t\t\t\t\tevent.point = mp->point;\n\t\t\t\t\t\thit = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( hit == true )\n\t\t\t\t{\n\t\t\t\t\tevent.normal = contactSim->manifold.normal;\n\n\t\t\t\t\tb2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contactSim->shapeIdA );\n\t\t\t\t\tb2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contactSim->shapeIdB );\n\n\t\t\t\t\tevent.shapeIdA = (b2ShapeId){ shapeA->id + 1, world->worldId, shapeA->generation };\n\t\t\t\t\tevent.shapeIdB = (b2ShapeId){ shapeB->id + 1, world->worldId, shapeB->generation };\n\n\t\t\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactSim->contactId );\n\n\t\t\t\t\tevent.contactId = (b2ContactId){\n\t\t\t\t\t\t.index1 = contact->contactId + 1,\n\t\t\t\t\t\t.world0 = world->worldId,\n\t\t\t\t\t\t.padding = 0,\n\t\t\t\t\t\t.generation = contact->generation,\n\t\t\t\t\t};\n\n\t\t\t\t\tb2ContactHitEventArray_Push( &world->contactHitEvents, event );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tworld->profile.hitEvents = b2GetMilliseconds( hitTicks );\n\t\tb2TracyCZoneEnd( hit_events );\n\t}\n\n\t{\n\t\tb2TracyCZoneNC( refit_bvh, \"Refit BVH\", b2_colorFireBrick, true );\n\t\tuint64_t refitTicks = b2GetTicks();\n\n\t\t// Finish the user tree task that was queued earlier in the time step. This must be complete before touching the\n\t\t// broad-phase.\n\t\tif ( world->userTreeTask != NULL )\n\t\t{\n\t\t\tworld->finishTaskFcn( world->userTreeTask, world->userTaskContext );\n\t\t\tworld->userTreeTask = NULL;\n\t\t\tworld->activeTaskCount -= 1;\n\t\t}\n\n\t\tb2ValidateNoEnlarged( &world->broadPhase );\n\n\t\t// Gather bits for all sim bodies that have enlarged AABBs\n\t\tb2BitSet* enlargedBodyBitSet = &world->taskContexts.data[0].enlargedSimBitSet;\n\t\tfor ( int i = 1; i < world->workerCount; ++i )\n\t\t{\n\t\t\tb2InPlaceUnion( enlargedBodyBitSet, &world->taskContexts.data[i].enlargedSimBitSet );\n\t\t}\n\n\t\t// Enlarge broad-phase proxies and build move array\n\t\t// Apply shape AABB changes to broad-phase. This also create the move array which must be\n\t\t// in deterministic order. I'm tracking sim bodies because the number of shape ids can be huge.\n\t\t// This has to happen before bullets are processed.\n\t\t{\n\t\t\tb2BroadPhase* broadPhase = &world->broadPhase;\n\t\t\tuint32_t wordCount = enlargedBodyBitSet->blockCount;\n\t\t\tuint64_t* bits = enlargedBodyBitSet->bits;\n\n\t\t\t// Fast array access is important here\n\t\t\tb2Body* bodyArray = world->bodies.data;\n\t\t\tb2BodySim* bodySimArray = awakeSet->bodySims.data;\n\t\t\tb2Shape* shapeArray = world->shapes.data;\n\n\t\t\tfor ( uint32_t k = 0; k < wordCount; ++k )\n\t\t\t{\n\t\t\t\tuint64_t word = bits[k];\n\t\t\t\twhile ( word != 0 )\n\t\t\t\t{\n\t\t\t\t\tuint32_t ctz = b2CTZ64( word );\n\t\t\t\t\tuint32_t bodySimIndex = 64 * k + ctz;\n\n\t\t\t\t\tb2BodySim* bodySim = bodySimArray + bodySimIndex;\n\n\t\t\t\t\tb2Body* body = bodyArray + bodySim->bodyId;\n\n\t\t\t\t\tint shapeId = body->headShapeId;\n\t\t\t\t\tif ( ( bodySim->flags & ( b2_isBullet | b2_isFast ) ) == ( b2_isBullet | b2_isFast ) )\n\t\t\t\t\t{\n\t\t\t\t\t\t// Fast bullet bodies don't have their final AABB yet\n\t\t\t\t\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb2Shape* shape = shapeArray + shapeId;\n\n\t\t\t\t\t\t\t// Shape is fast. It's aabb will be enlarged in continuous collision.\n\t\t\t\t\t\t\t// Update the move array here for determinism because bullets are processed\n\t\t\t\t\t\t\t// below in non-deterministic order.\n\t\t\t\t\t\t\tb2BufferMove( broadPhase, shape->proxyKey );\n\n\t\t\t\t\t\t\tshapeId = shape->nextShapeId;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb2Shape* shape = shapeArray + shapeId;\n\n\t\t\t\t\t\t\t// The AABB may not have been enlarged, despite the body being flagged as enlarged.\n\t\t\t\t\t\t\t// For example, a body with multiple shapes may have not have all shapes enlarged.\n\t\t\t\t\t\t\t// A fast body may have been flagged as enlarged despite having no shapes enlarged.\n\t\t\t\t\t\t\tif ( shape->enlargedAABB )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tb2BroadPhase_EnlargeProxy( broadPhase, shape->proxyKey, shape->fatAABB );\n\t\t\t\t\t\t\t\tshape->enlargedAABB = false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tshapeId = shape->nextShapeId;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Clear the smallest set bit\n\t\t\t\t\tword = word & ( word - 1 );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tb2ValidateBroadphase( &world->broadPhase );\n\n\t\tworld->profile.refit = b2GetMilliseconds( refitTicks );\n\t\tb2TracyCZoneEnd( refit_bvh );\n\t}\n\n\tint bulletBodyCount = b2AtomicLoadInt( &stepContext->bulletBodyCount );\n\tif ( bulletBodyCount > 0 )\n\t{\n\t\tb2TracyCZoneNC( bullets, \"Bullets\", b2_colorLightYellow, true );\n\t\tuint64_t bulletTicks = b2GetTicks();\n\n\t\t// Fast bullet bodies\n\t\t// Note: a bullet body may be moving slow\n\t\tint minRange = 8;\n\t\tvoid* userBulletBodyTask =\n\t\t\tworld->enqueueTaskFcn( &b2BulletBodyTask, bulletBodyCount, minRange, stepContext, world->userTaskContext );\n\t\tworld->taskCount += 1;\n\t\tif ( userBulletBodyTask != NULL )\n\t\t{\n\t\t\tworld->finishTaskFcn( userBulletBodyTask, world->userTaskContext );\n\t\t}\n\n\t\t// Serially enlarge broad-phase proxies for bullet shapes\n\t\tb2BroadPhase* broadPhase = &world->broadPhase;\n\t\tb2DynamicTree* dynamicTree = broadPhase->trees + b2_dynamicBody;\n\n\t\t// Fast array access is important here\n\t\tb2Body* bodyArray = world->bodies.data;\n\t\tb2BodySim* bodySimArray = awakeSet->bodySims.data;\n\t\tb2Shape* shapeArray = world->shapes.data;\n\n\t\t// Serially enlarge broad-phase proxies for bullet shapes\n\t\tint* bulletBodySimIndices = stepContext->bulletBodies;\n\n\t\t// This loop has non-deterministic order but it shouldn't affect the result\n\t\tfor ( int i = 0; i < bulletBodyCount; ++i )\n\t\t{\n\t\t\tb2BodySim* bulletBodySim = bodySimArray + bulletBodySimIndices[i];\n\t\t\tif ( ( bulletBodySim->flags & b2_enlargeBounds ) == 0 )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Clear flag\n\t\t\tbulletBodySim->flags &= ~b2_enlargeBounds;\n\n\t\t\tint bodyId = bulletBodySim->bodyId;\n\t\t\tB2_ASSERT( 0 <= bodyId && bodyId < world->bodies.count );\n\t\t\tb2Body* bulletBody = bodyArray + bodyId;\n\n\t\t\tint shapeId = bulletBody->headShapeId;\n\t\t\twhile ( shapeId != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tb2Shape* shape = shapeArray + shapeId;\n\t\t\t\tif ( shape->enlargedAABB == false )\n\t\t\t\t{\n\t\t\t\t\tshapeId = shape->nextShapeId;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Clear flag\n\t\t\t\tshape->enlargedAABB = false;\n\n\t\t\t\tint proxyKey = shape->proxyKey;\n\t\t\t\tint proxyId = B2_PROXY_ID( proxyKey );\n\t\t\t\tB2_ASSERT( B2_PROXY_TYPE( proxyKey ) == b2_dynamicBody );\n\n\t\t\t\t// all fast bullet shapes should already be in the move buffer\n\t\t\t\tB2_ASSERT( b2ContainsKey( &broadPhase->moveSet, proxyKey + 1 ) );\n\n\t\t\t\tb2DynamicTree_EnlargeProxy( dynamicTree, proxyId, shape->fatAABB );\n\n\t\t\t\tshapeId = shape->nextShapeId;\n\t\t\t}\n\t\t}\n\n\t\tworld->profile.bullets = b2GetMilliseconds( bulletTicks );\n\t\tb2TracyCZoneEnd( bullets );\n\t}\n\n\t// Need to free this even if no bullets got processed.\n\tb2FreeArenaItem( &world->arena, stepContext->bulletBodies );\n\tstepContext->bulletBodies = NULL;\n\tb2AtomicStoreInt( &stepContext->bulletBodyCount, 0 );\n\n\t// Report sensor hits. This may include bullets sensor hits.\n\t{\n\t\tb2TracyCZoneNC( sensor_hits, \"Sensor Hits\", b2_colorPowderBlue, true );\n\t\tuint64_t sensorHitTicks = b2GetTicks();\n\n\t\tint workerCount = world->workerCount;\n\t\tB2_ASSERT( workerCount == world->taskContexts.count );\n\n\t\tfor ( int i = 0; i < workerCount; ++i )\n\t\t{\n\t\t\tb2TaskContext* taskContext = world->taskContexts.data + i;\n\t\t\tint hitCount = taskContext->sensorHits.count;\n\t\t\tb2SensorHit* hits = taskContext->sensorHits.data;\n\n\t\t\tfor ( int j = 0; j < hitCount; ++j )\n\t\t\t{\n\t\t\t\tb2SensorHit hit = hits[j];\n\t\t\t\tb2Shape* sensorShape = b2ShapeArray_Get( &world->shapes, hit.sensorId );\n\t\t\t\tb2Shape* visitor = b2ShapeArray_Get( &world->shapes, hit.visitorId );\n\n\t\t\t\tb2Sensor* sensor = b2SensorArray_Get( &world->sensors, sensorShape->sensorIndex );\n\t\t\t\tb2Visitor shapeRef = {\n\t\t\t\t\t.shapeId = hit.visitorId,\n\t\t\t\t\t.generation = visitor->generation,\n\t\t\t\t};\n\t\t\t\tb2VisitorArray_Push( &sensor->hits, shapeRef );\n\t\t\t}\n\t\t}\n\n\t\tworld->profile.sensorHits = b2GetMilliseconds( sensorHitTicks );\n\t\tb2TracyCZoneEnd( sensor_hits );\n\t}\n\n\t// Island sleeping\n\t// This must be done last because putting islands to sleep invalidates the enlarged body bits.\n\t// todo_erin figure out how to do this in parallel with tree refit\n\tif ( world->enableSleep == true )\n\t{\n\t\tb2TracyCZoneNC( sleep_islands, \"Island Sleep\", b2_colorLightSlateGray, true );\n\t\tuint64_t sleepTicks = b2GetTicks();\n\n\t\t// Collect split island candidate for the next time step. No need to split if sleeping is disabled.\n\t\tB2_ASSERT( world->splitIslandId == B2_NULL_INDEX );\n\t\tfloat splitSleepTimer = 0.0f;\n\t\tfor ( int i = 0; i < world->workerCount; ++i )\n\t\t{\n\t\t\tb2TaskContext* taskContext = world->taskContexts.data + i;\n\t\t\tif ( taskContext->splitIslandId != B2_NULL_INDEX && taskContext->splitSleepTime >= splitSleepTimer )\n\t\t\t{\n\t\t\t\tB2_ASSERT( taskContext->splitSleepTime > 0.0f );\n\n\t\t\t\t// Tie breaking for determinism. Largest island id wins. Needed due to work stealing.\n\t\t\t\tif ( taskContext->splitSleepTime == splitSleepTimer && taskContext->splitIslandId < world->splitIslandId )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tworld->splitIslandId = taskContext->splitIslandId;\n\t\t\t\tsplitSleepTimer = taskContext->splitSleepTime;\n\t\t\t}\n\t\t}\n\n\t\tb2BitSet* awakeIslandBitSet = &world->taskContexts.data[0].awakeIslandBitSet;\n\t\tfor ( int i = 1; i < world->workerCount; ++i )\n\t\t{\n\t\t\tb2InPlaceUnion( awakeIslandBitSet, &world->taskContexts.data[i].awakeIslandBitSet );\n\t\t}\n\n\t\t// Need to process in reverse because this moves islands to sleeping solver sets.\n\t\tb2IslandSim* islands = awakeSet->islandSims.data;\n\t\tint count = awakeSet->islandSims.count;\n\t\tfor ( int islandIndex = count - 1; islandIndex >= 0; islandIndex -= 1 )\n\t\t{\n\t\t\tif ( b2GetBit( awakeIslandBitSet, islandIndex ) == true )\n\t\t\t{\n\t\t\t\t// this island is still awake\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tb2IslandSim* island = islands + islandIndex;\n\t\t\tint islandId = island->islandId;\n\n\t\t\tb2TrySleepIsland( world, islandId );\n\t\t}\n\n\t\tb2ValidateSolverSets( world );\n\n\t\tworld->profile.sleepIslands = b2GetMilliseconds( sleepTicks );\n\t\tb2TracyCZoneEnd( sleep_islands );\n\t}\n}\n"
  },
  {
    "path": "src/solver.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"core.h\"\n\n#include \"box2d/math_functions.h\"\n\n#include <stdbool.h>\n#include <stdint.h>\n\ntypedef struct b2BodySim b2BodySim;\ntypedef struct b2BodyState b2BodyState;\ntypedef struct b2ContactSim b2ContactSim;\ntypedef struct b2JointSim b2JointSim;\ntypedef struct b2World b2World;\n\ntypedef struct b2Softness\n{\n\tfloat biasRate;\n\tfloat massScale;\n\tfloat impulseScale;\n} b2Softness;\n\ntypedef enum b2SolverStageType\n{\n\tb2_stagePrepareJoints,\n\tb2_stagePrepareContacts,\n\tb2_stageIntegrateVelocities,\n\tb2_stageWarmStart,\n\tb2_stageSolve,\n\tb2_stageIntegratePositions,\n\tb2_stageRelax,\n\tb2_stageRestitution,\n\tb2_stageStoreImpulses\n} b2SolverStageType;\n\ntypedef enum b2SolverBlockType\n{\n\tb2_bodyBlock,\n\tb2_jointBlock,\n\tb2_contactBlock,\n\tb2_graphJointBlock,\n\tb2_graphContactBlock\n} b2SolverBlockType;\n\n// Each block of work has a sync index that gets incremented when a worker claims the block. This ensures only a single worker\n// claims a block, yet lets work be distributed dynamically across multiple workers (work stealing). This also reduces contention\n// on a single block index atomic. For non-iterative stages the sync index is simply set to one. For iterative stages (solver\n// iteration) the same block of work is executed once per iteration and the atomic sync index is shared across iterations, so it\n// increases monotonically.\ntypedef struct b2SolverBlock\n{\n\tint startIndex;\n\tint16_t count;\n\tint16_t blockType; // b2SolverBlockType\n\t// todo consider false sharing of this atomic\n\tb2AtomicInt syncIndex;\n} b2SolverBlock;\n\n// Each stage must be completed before going to the next stage.\n// Non-iterative stages use a stage instance once while iterative stages re-use the same instance each iteration.\ntypedef struct b2SolverStage\n{\n\tb2SolverStageType type;\n\tb2SolverBlock* blocks;\n\tint blockCount;\n\tint colorIndex;\n\t// todo consider false sharing of this atomic\n\tb2AtomicInt completionCount;\n} b2SolverStage;\n\n// Context for a time step. Recreated each time step.\ntypedef struct b2StepContext\n{\n\t// time step\n\tfloat dt;\n\n\t// inverse time step (0 if dt == 0).\n\tfloat inv_dt;\n\n\t// sub-step\n\tfloat h;\n\tfloat inv_h;\n\n\tint subStepCount;\n\n\tb2Softness contactSoftness;\n\tb2Softness staticSoftness;\n\n\tfloat restitutionThreshold;\n\tfloat maxLinearVelocity;\n\n\tstruct b2World* world;\n\tstruct b2ConstraintGraph* graph;\n\n\t// shortcut to body states from awake set\n\tb2BodyState* states;\n\n\t// shortcut to body sims from awake set\n\tb2BodySim* sims;\n\n\t// array of all shape ids for shapes that have enlarged AABBs\n\tint* enlargedShapes;\n\tint enlargedShapeCount;\n\n\t// Array of bullet bodies that need continuous collision handling\n\tint* bulletBodies;\n\tb2AtomicInt bulletBodyCount;\n\n\t// joint pointers for simplified parallel-for access.\n\tb2JointSim** joints;\n\n\t// contact pointers for simplified parallel-for access.\n\t// - parallel-for collide with no gaps\n\t// - parallel-for prepare and store contacts with NULL gaps for SIMD remainders\n\t// despite being an array of pointers, these are contiguous sub-arrays corresponding\n\t// to constraint graph colors\n\tb2ContactSim** contacts;\n\n\tstruct b2ContactConstraintSIMD* simdContactConstraints;\n\tint activeColorCount;\n\tint workerCount;\n\n\tb2SolverStage* stages;\n\tint stageCount;\n\tbool enableWarmStarting;\n\n\t// todo padding to prevent false sharing\n\tchar dummy1[64];\n\n\t// sync index (16-bits) | stage type (16-bits)\n\tb2AtomicU32 atomicSyncBits;\n\n\tchar dummy2[64];\n\n} b2StepContext;\n\nstatic inline b2Softness b2MakeSoft( float hertz, float zeta, float h )\n{\n\tif ( hertz == 0.0f )\n\t{\n\t\treturn (b2Softness){\n\t\t\t.biasRate = 0.0f,\n\t\t\t.massScale = 0.0f,\n\t\t\t.impulseScale = 0.0f,\n\t\t};\n\t}\n\n\tfloat omega = 2.0f * B2_PI * hertz;\n\tfloat a1 = 2.0f * zeta + h * omega;\n\tfloat a2 = h * omega * a1;\n\tfloat a3 = 1.0f / ( 1.0f + a2 );\n\n\t// bias = w / (2 * z + hw)\n\t// massScale = hw * (2 * z + hw) / (1 + hw * (2 * z + hw))\n\t// impulseScale = 1 / (1 + hw * (2 * z + hw))\n\n\t// If z == 0\n\t// bias = 1/h\n\t// massScale = hw^2 / (1 + hw^2)\n\t// impulseScale = 1 / (1 + hw^2)\n\n\t// w -> inf\n\t// bias = 1/h\n\t// massScale = 1\n\t// impulseScale = 0\n\n\t// if w = pi / 4  * inv_h\n\t// massScale = (pi/4)^2 / (1 + (pi/4)^2) = pi^2 / (16 + pi^2) ~= 0.38\n\t// impulseScale = 1 / (1 + (pi/4)^2) = 16 / (16 + pi^2) ~= 0.62\n\n\t// In all cases:\n\t// massScale + impulseScale == 1\n\n\treturn (b2Softness){\n\t\t.biasRate = omega / a1,\n\t\t.massScale = a2 * a3,\n\t\t.impulseScale = a3,\n\t};\n}\n\nvoid b2Solve( b2World* world, b2StepContext* stepContext );\n"
  },
  {
    "path": "src/solver_set.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"solver_set.h\"\n\n#include \"body.h\"\n#include \"constraint_graph.h\"\n#include \"contact.h\"\n#include \"core.h\"\n#include \"island.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n\n#include <string.h>\n\nB2_ARRAY_SOURCE( b2SolverSet, b2SolverSet )\n\nvoid b2DestroySolverSet( b2World* world, int setIndex )\n{\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );\n\tb2BodySimArray_Destroy( &set->bodySims );\n\tb2BodyStateArray_Destroy( &set->bodyStates );\n\tb2ContactSimArray_Destroy( &set->contactSims );\n\tb2JointSimArray_Destroy( &set->jointSims );\n\tb2IslandSimArray_Destroy( &set->islandSims );\n\tb2FreeId( &world->solverSetIdPool, setIndex );\n\t*set = ( b2SolverSet ){ 0 };\n\tset->setIndex = B2_NULL_INDEX;\n}\n\n// Wake a solver set. Does not merge islands.\n// Contacts can be in several places:\n// 1. non-touching contacts in the disabled set\n// 2. non-touching contacts already in the awake set\n// 3. touching contacts in the sleeping set\n// This handles contact types 1 and 3. Type 2 doesn't need any action.\nvoid b2WakeSolverSet( b2World* world, int setIndex )\n{\n\tB2_ASSERT( setIndex >= b2_firstSleepingSet );\n\tb2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tb2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet );\n\n\tb2Body* bodies = world->bodies.data;\n\n\tint bodyCount = set->bodySims.count;\n\tfor ( int i = 0; i < bodyCount; ++i )\n\t{\n\t\tb2BodySim* simSrc = set->bodySims.data + i;\n\n\t\tb2Body* body = bodies + simSrc->bodyId;\n\t\tB2_ASSERT( body->setIndex == setIndex );\n\t\tbody->setIndex = b2_awakeSet;\n\t\tbody->localIndex = awakeSet->bodySims.count;\n\n\t\t// Reset sleep timer\n\t\tbody->sleepTime = 0.0f;\n\n\t\tb2BodySim* simDst = b2BodySimArray_Add( &awakeSet->bodySims );\n\t\tmemcpy( simDst, simSrc, sizeof( b2BodySim ) );\n\n\t\tb2BodyState* state = b2BodyStateArray_Add( &awakeSet->bodyStates );\n\t\t*state = b2_identityBodyState;\n\t\tstate->flags = body->flags;\n\n\t\t// move non-touching contacts from disabled set to awake set\n\t\tint contactKey = body->headContactKey;\n\t\twhile ( contactKey != B2_NULL_INDEX )\n\t\t{\n\t\t\tint edgeIndex = contactKey & 1;\n\t\t\tint contactId = contactKey >> 1;\n\n\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\n\t\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\n\t\t\tif ( contact->setIndex != b2_disabledSet )\n\t\t\t{\n\t\t\t\tB2_ASSERT( contact->setIndex == b2_awakeSet || contact->setIndex == setIndex );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tint localIndex = contact->localIndex;\n\t\t\tb2ContactSim* contactSim = b2ContactSimArray_Get( &disabledSet->contactSims, localIndex );\n\n\t\t\tB2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) == 0 && contactSim->manifold.pointCount == 0 );\n\n\t\t\tcontact->setIndex = b2_awakeSet;\n\t\t\tcontact->localIndex = awakeSet->contactSims.count;\n\t\t\tb2ContactSim* awakeContactSim = b2ContactSimArray_Add( &awakeSet->contactSims );\n\t\t\tmemcpy( awakeContactSim, contactSim, sizeof( b2ContactSim ) );\n\n\t\t\tint movedLocalIndex = b2ContactSimArray_RemoveSwap( &disabledSet->contactSims, localIndex );\n\t\t\tif ( movedLocalIndex != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\t// fix moved element\n\t\t\t\tb2ContactSim* movedContactSim = disabledSet->contactSims.data + localIndex;\n\t\t\t\tb2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );\n\t\t\t\tB2_ASSERT( movedContact->localIndex == movedLocalIndex );\n\t\t\t\tmovedContact->localIndex = localIndex;\n\t\t\t}\n\t\t}\n\t}\n\n\t// transfer touching contacts from sleeping set to contact graph\n\t{\n\t\tint contactCount = set->contactSims.count;\n\t\tfor ( int i = 0; i < contactCount; ++i )\n\t\t{\n\t\t\tb2ContactSim* contactSim = set->contactSims.data + i;\n\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactSim->contactId );\n\t\t\tB2_ASSERT( contact->flags & b2_contactTouchingFlag );\n\t\t\tB2_ASSERT( contactSim->simFlags & b2_simTouchingFlag );\n\t\t\tB2_ASSERT( contactSim->manifold.pointCount > 0 );\n\t\t\tB2_ASSERT( contact->setIndex == setIndex );\n\t\t\tb2AddContactToGraph( world, contactSim, contact );\n\t\t\tcontact->setIndex = b2_awakeSet;\n\t\t}\n\t}\n\n\t// transfer joints from sleeping set to awake set\n\t{\n\t\tint jointCount = set->jointSims.count;\n\t\tfor ( int i = 0; i < jointCount; ++i )\n\t\t{\n\t\t\tb2JointSim* jointSim = set->jointSims.data + i;\n\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointSim->jointId );\n\t\t\tB2_ASSERT( joint->setIndex == setIndex );\n\t\t\tb2AddJointToGraph( world, jointSim, joint );\n\t\t\tjoint->setIndex = b2_awakeSet;\n\t\t}\n\t}\n\n\t// transfer island from sleeping set to awake set\n\t// Usually a sleeping set has only one island, but it is possible\n\t// that joints are created between sleeping islands and they\n\t// are moved to the same sleeping set.\n\t{\n\t\tint islandCount = set->islandSims.count;\n\t\tfor ( int i = 0; i < islandCount; ++i )\n\t\t{\n\t\t\tb2IslandSim* islandSrc = set->islandSims.data + i;\n\t\t\tb2Island* island = b2IslandArray_Get( &world->islands, islandSrc->islandId );\n\t\t\tisland->setIndex = b2_awakeSet;\n\t\t\tisland->localIndex = awakeSet->islandSims.count;\n\t\t\tb2IslandSim* islandDst = b2IslandSimArray_Add( &awakeSet->islandSims );\n\t\t\tmemcpy( islandDst, islandSrc, sizeof( b2IslandSim ) );\n\t\t}\n\t}\n\n\t// destroy the sleeping set\n\tb2DestroySolverSet( world, setIndex );\n}\n\nvoid b2TrySleepIsland( b2World* world, int islandId )\n{\n\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\tB2_ASSERT( island->setIndex == b2_awakeSet );\n\n\t// cannot put an island to sleep while it has a pending split\n\tif ( island->constraintRemoveCount > 0 )\n\t{\n\t\treturn;\n\t}\n\n\t// island is sleeping\n\t// - create new sleeping solver set\n\t// - move island to sleeping solver set\n\t// - identify non-touching contacts that should move to sleeping solver set or disabled set\n\t// - remove old island\n\t// - fix island\n\tint sleepSetId = b2AllocId( &world->solverSetIdPool );\n\tif ( sleepSetId == world->solverSets.count )\n\t{\n\t\tb2SolverSet set = { 0 };\n\t\tset.setIndex = B2_NULL_INDEX;\n\t\tb2SolverSetArray_Push( &world->solverSets, set );\n\t}\n\n\tb2SolverSet* sleepSet = b2SolverSetArray_Get( &world->solverSets, sleepSetId );\n\t*sleepSet = ( b2SolverSet ){ 0 };\n\n\t// grab awake set after creating the sleep set because the solver set array may have been resized\n\tb2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );\n\tB2_ASSERT( 0 <= island->localIndex && island->localIndex < awakeSet->islandSims.count );\n\n\tsleepSet->setIndex = sleepSetId;\n\tsleepSet->bodySims = b2BodySimArray_Create( island->bodyCount );\n\tsleepSet->contactSims = b2ContactSimArray_Create( island->contactCount );\n\tsleepSet->jointSims = b2JointSimArray_Create( island->jointCount );\n\n\t// move awake bodies to sleeping set\n\t// this shuffles around bodies in the awake set\n\t{\n\t\tb2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet );\n\t\tint bodyId = island->headBody;\n\t\twhile ( bodyId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Body* body = b2BodyArray_Get( &world->bodies, bodyId );\n\t\t\tB2_ASSERT( body->setIndex == b2_awakeSet );\n\t\t\tB2_ASSERT( body->islandId == islandId );\n\n\t\t\t// Update the body move event to indicate this body fell asleep\n\t\t\t// It could happen the body is forced asleep before it ever moves.\n\t\t\tif ( body->bodyMoveIndex != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tb2BodyMoveEvent* moveEvent = b2BodyMoveEventArray_Get( &world->bodyMoveEvents, body->bodyMoveIndex );\n\t\t\t\tB2_ASSERT( moveEvent->bodyId.index1 - 1 == bodyId );\n\t\t\t\tB2_ASSERT( moveEvent->bodyId.generation == body->generation );\n\t\t\t\tmoveEvent->fellAsleep = true;\n\t\t\t\tbody->bodyMoveIndex = B2_NULL_INDEX;\n\t\t\t}\n\n\t\t\tint awakeBodyIndex = body->localIndex;\n\t\t\tb2BodySim* awakeSim = b2BodySimArray_Get( &awakeSet->bodySims, awakeBodyIndex );\n\n\t\t\t// move body sim to sleep set\n\t\t\tint sleepBodyIndex = sleepSet->bodySims.count;\n\t\t\tb2BodySim* sleepBodySim = b2BodySimArray_Add( &sleepSet->bodySims );\n\t\t\tmemcpy( sleepBodySim, awakeSim, sizeof( b2BodySim ) );\n\n\t\t\tint movedIndex = b2BodySimArray_RemoveSwap( &awakeSet->bodySims, awakeBodyIndex );\n\t\t\tif ( movedIndex != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\t// fix local index on moved element\n\t\t\t\tb2BodySim* movedSim = awakeSet->bodySims.data + awakeBodyIndex;\n\t\t\t\tint movedId = movedSim->bodyId;\n\t\t\t\tb2Body* movedBody = b2BodyArray_Get( &world->bodies, movedId );\n\t\t\t\tB2_ASSERT( movedBody->localIndex == movedIndex );\n\t\t\t\tmovedBody->localIndex = awakeBodyIndex;\n\t\t\t}\n\n\t\t\t// destroy state, no need to clone\n\t\t\tb2BodyStateArray_RemoveSwap( &awakeSet->bodyStates, awakeBodyIndex );\n\n\t\t\tbody->setIndex = sleepSetId;\n\t\t\tbody->localIndex = sleepBodyIndex;\n\n\t\t\t// Move non-touching contacts to the disabled set.\n\t\t\t// Non-touching contacts may exist between sleeping islands and there is no clear ownership.\n\t\t\tint contactKey = body->headContactKey;\n\t\t\twhile ( contactKey != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\tint contactId = contactKey >> 1;\n\t\t\t\tint edgeIndex = contactKey & 1;\n\n\t\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\n\t\t\t\tB2_ASSERT( contact->setIndex == b2_awakeSet || contact->setIndex == b2_disabledSet );\n\t\t\t\tcontactKey = contact->edges[edgeIndex].nextKey;\n\n\t\t\t\tif ( contact->setIndex == b2_disabledSet )\n\t\t\t\t{\n\t\t\t\t\t// already moved to disabled set by another body in the island\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif ( contact->colorIndex != B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\t// contact is touching and will be moved separately\n\t\t\t\t\tB2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) != 0 );\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// the other body may still be awake, it still may go to sleep and then it will be responsible\n\t\t\t\t// for moving this contact to the disabled set.\n\t\t\t\tint otherEdgeIndex = edgeIndex ^ 1;\n\t\t\t\tint otherBodyId = contact->edges[otherEdgeIndex].bodyId;\n\t\t\t\tb2Body* otherBody = b2BodyArray_Get( &world->bodies, otherBodyId );\n\t\t\t\tif ( otherBody->setIndex == b2_awakeSet )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tint localIndex = contact->localIndex;\n\t\t\t\tb2ContactSim* contactSim = b2ContactSimArray_Get( &awakeSet->contactSims, localIndex );\n\n\t\t\t\tB2_ASSERT( contactSim->manifold.pointCount == 0 );\n\t\t\t\tB2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) == 0 );\n\n\t\t\t\t// move the non-touching contact to the disabled set\n\t\t\t\tcontact->setIndex = b2_disabledSet;\n\t\t\t\tcontact->localIndex = disabledSet->contactSims.count;\n\t\t\t\tb2ContactSim* disabledContactSim = b2ContactSimArray_Add( &disabledSet->contactSims );\n\t\t\t\tmemcpy( disabledContactSim, contactSim, sizeof( b2ContactSim ) );\n\n\t\t\t\tint movedLocalIndex = b2ContactSimArray_RemoveSwap( &awakeSet->contactSims, localIndex );\n\t\t\t\tif ( movedLocalIndex != B2_NULL_INDEX )\n\t\t\t\t{\n\t\t\t\t\t// fix moved element\n\t\t\t\t\tb2ContactSim* movedContactSim = awakeSet->contactSims.data + localIndex;\n\t\t\t\t\tb2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );\n\t\t\t\t\tB2_ASSERT( movedContact->localIndex == movedLocalIndex );\n\t\t\t\t\tmovedContact->localIndex = localIndex;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbodyId = body->islandNext;\n\t\t}\n\t}\n\n\t// move touching contacts\n\t// this shuffles contacts in the awake set\n\t{\n\t\tint contactId = island->headContact;\n\t\twhile ( contactId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );\n\t\t\tB2_ASSERT( contact->setIndex == b2_awakeSet );\n\t\t\tB2_ASSERT( contact->islandId == islandId );\n\t\t\tint colorIndex = contact->colorIndex;\n\t\t\tB2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );\n\n\t\t\tb2GraphColor* color = world->constraintGraph.colors + colorIndex;\n\n\t\t\t// Remove bodies from graph coloring associated with this constraint\n\t\t\tif ( colorIndex != B2_OVERFLOW_INDEX )\n\t\t\t{\n\t\t\t\t// might clear a bit for a static body, but this has no effect\n\t\t\t\tb2ClearBit( &color->bodySet, contact->edges[0].bodyId );\n\t\t\t\tb2ClearBit( &color->bodySet, contact->edges[1].bodyId );\n\t\t\t}\n\n\t\t\tint localIndex = contact->localIndex;\n\t\t\tb2ContactSim* awakeContactSim = b2ContactSimArray_Get( &color->contactSims, localIndex );\n\n\t\t\tint sleepContactIndex = sleepSet->contactSims.count;\n\t\t\tb2ContactSim* sleepContactSim = b2ContactSimArray_Add( &sleepSet->contactSims );\n\t\t\tmemcpy( sleepContactSim, awakeContactSim, sizeof( b2ContactSim ) );\n\n\t\t\tint movedLocalIndex = b2ContactSimArray_RemoveSwap( &color->contactSims, localIndex );\n\t\t\tif ( movedLocalIndex != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\t// fix moved element\n\t\t\t\tb2ContactSim* movedContactSim = color->contactSims.data + localIndex;\n\t\t\t\tb2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );\n\t\t\t\tB2_ASSERT( movedContact->localIndex == movedLocalIndex );\n\t\t\t\tmovedContact->localIndex = localIndex;\n\t\t\t}\n\n\t\t\tcontact->setIndex = sleepSetId;\n\t\t\tcontact->colorIndex = B2_NULL_INDEX;\n\t\t\tcontact->localIndex = sleepContactIndex;\n\n\t\t\tcontactId = contact->islandNext;\n\t\t}\n\t}\n\n\t// move joints\n\t// this shuffles joints in the awake set\n\t{\n\t\tint jointId = island->headJoint;\n\t\twhile ( jointId != B2_NULL_INDEX )\n\t\t{\n\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointId );\n\t\t\tB2_ASSERT( joint->setIndex == b2_awakeSet );\n\t\t\tB2_ASSERT( joint->islandId == islandId );\n\t\t\tint colorIndex = joint->colorIndex;\n\t\t\tint localIndex = joint->localIndex;\n\n\t\t\tB2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );\n\n\t\t\tb2GraphColor* color = world->constraintGraph.colors + colorIndex;\n\n\t\t\tb2JointSim* awakeJointSim = b2JointSimArray_Get( &color->jointSims, localIndex );\n\n\t\t\tif ( colorIndex != B2_OVERFLOW_INDEX )\n\t\t\t{\n\t\t\t\t// might clear a bit for a static body, but this has no effect\n\t\t\t\tb2ClearBit( &color->bodySet, joint->edges[0].bodyId );\n\t\t\t\tb2ClearBit( &color->bodySet, joint->edges[1].bodyId );\n\t\t\t}\n\n\t\t\tint sleepJointIndex = sleepSet->jointSims.count;\n\t\t\tb2JointSim* sleepJointSim = b2JointSimArray_Add( &sleepSet->jointSims );\n\t\t\tmemcpy( sleepJointSim, awakeJointSim, sizeof( b2JointSim ) );\n\n\t\t\tint movedIndex = b2JointSimArray_RemoveSwap( &color->jointSims, localIndex );\n\t\t\tif ( movedIndex != B2_NULL_INDEX )\n\t\t\t{\n\t\t\t\t// fix moved element\n\t\t\t\tb2JointSim* movedJointSim = color->jointSims.data + localIndex;\n\t\t\t\tint movedId = movedJointSim->jointId;\n\t\t\t\tb2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId );\n\t\t\t\tB2_ASSERT( movedJoint->localIndex == movedIndex );\n\t\t\t\tmovedJoint->localIndex = localIndex;\n\t\t\t}\n\n\t\t\tjoint->setIndex = sleepSetId;\n\t\t\tjoint->colorIndex = B2_NULL_INDEX;\n\t\t\tjoint->localIndex = sleepJointIndex;\n\n\t\t\tjointId = joint->islandNext;\n\t\t}\n\t}\n\n\t// move island struct\n\t{\n\t\tB2_ASSERT( island->setIndex == b2_awakeSet );\n\n\t\tint islandIndex = island->localIndex;\n\t\tb2IslandSim* sleepIsland = b2IslandSimArray_Add( &sleepSet->islandSims );\n\t\tsleepIsland->islandId = islandId;\n\n\t\tint movedIslandIndex = b2IslandSimArray_RemoveSwap( &awakeSet->islandSims, islandIndex );\n\t\tif ( movedIslandIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\t// fix index on moved element\n\t\t\tb2IslandSim* movedIslandSim = awakeSet->islandSims.data + islandIndex;\n\t\t\tint movedIslandId = movedIslandSim->islandId;\n\t\t\tb2Island* movedIsland = b2IslandArray_Get( &world->islands, movedIslandId );\n\t\t\tB2_ASSERT( movedIsland->localIndex == movedIslandIndex );\n\t\t\tmovedIsland->localIndex = islandIndex;\n\t\t}\n\n\t\tisland->setIndex = sleepSetId;\n\t\tisland->localIndex = 0;\n\t}\n\n\tb2ValidateSolverSets( world );\n}\n\n// This is called when joints are created between sets. I want to allow the sets\n// to continue sleeping if both are asleep. Otherwise one set is waked.\n// Islands will get merge when the set is waked.\nvoid b2MergeSolverSets( b2World* world, int setId1, int setId2 )\n{\n\tB2_ASSERT( setId1 >= b2_firstSleepingSet );\n\tB2_ASSERT( setId2 >= b2_firstSleepingSet );\n\tb2SolverSet* set1 = b2SolverSetArray_Get( &world->solverSets, setId1 );\n\tb2SolverSet* set2 = b2SolverSetArray_Get( &world->solverSets, setId2 );\n\n\t// Move the fewest number of bodies\n\tif ( set1->bodySims.count < set2->bodySims.count )\n\t{\n\t\tb2SolverSet* tempSet = set1;\n\t\tset1 = set2;\n\t\tset2 = tempSet;\n\n\t\tint tempId = setId1;\n\t\tsetId1 = setId2;\n\t\tsetId2 = tempId;\n\t}\n\n\t// transfer bodies\n\t{\n\t\tb2Body* bodies = world->bodies.data;\n\t\tint bodyCount = set2->bodySims.count;\n\t\tfor ( int i = 0; i < bodyCount; ++i )\n\t\t{\n\t\t\tb2BodySim* simSrc = set2->bodySims.data + i;\n\n\t\t\tb2Body* body = bodies + simSrc->bodyId;\n\t\t\tB2_ASSERT( body->setIndex == setId2 );\n\t\t\tbody->setIndex = setId1;\n\t\t\tbody->localIndex = set1->bodySims.count;\n\n\t\t\tb2BodySim* simDst = b2BodySimArray_Add( &set1->bodySims );\n\t\t\tmemcpy( simDst, simSrc, sizeof( b2BodySim ) );\n\t\t}\n\t}\n\n\t// transfer contacts\n\t{\n\t\tint contactCount = set2->contactSims.count;\n\t\tfor ( int i = 0; i < contactCount; ++i )\n\t\t{\n\t\t\tb2ContactSim* contactSrc = set2->contactSims.data + i;\n\n\t\t\tb2Contact* contact = b2ContactArray_Get( &world->contacts, contactSrc->contactId );\n\t\t\tB2_ASSERT( contact->setIndex == setId2 );\n\t\t\tcontact->setIndex = setId1;\n\t\t\tcontact->localIndex = set1->contactSims.count;\n\n\t\t\tb2ContactSim* contactDst = b2ContactSimArray_Add( &set1->contactSims );\n\t\t\tmemcpy( contactDst, contactSrc, sizeof( b2ContactSim ) );\n\t\t}\n\t}\n\n\t// transfer joints\n\t{\n\t\tint jointCount = set2->jointSims.count;\n\t\tfor ( int i = 0; i < jointCount; ++i )\n\t\t{\n\t\t\tb2JointSim* jointSrc = set2->jointSims.data + i;\n\n\t\t\tb2Joint* joint = b2JointArray_Get( &world->joints, jointSrc->jointId );\n\t\t\tB2_ASSERT( joint->setIndex == setId2 );\n\t\t\tjoint->setIndex = setId1;\n\t\t\tjoint->localIndex = set1->jointSims.count;\n\n\t\t\tb2JointSim* jointDst = b2JointSimArray_Add( &set1->jointSims );\n\t\t\tmemcpy( jointDst, jointSrc, sizeof( b2JointSim ) );\n\t\t}\n\t}\n\n\t// transfer islands\n\t{\n\t\tint islandCount = set2->islandSims.count;\n\t\tfor ( int i = 0; i < islandCount; ++i )\n\t\t{\n\t\t\tb2IslandSim* islandSrc = set2->islandSims.data + i;\n\t\t\tint islandId = islandSrc->islandId;\n\n\t\t\tb2Island* island = b2IslandArray_Get( &world->islands, islandId );\n\t\t\tisland->setIndex = setId1;\n\t\t\tisland->localIndex = set1->islandSims.count;\n\n\t\t\tb2IslandSim* islandDst = b2IslandSimArray_Add( &set1->islandSims );\n\t\t\tmemcpy( islandDst, islandSrc, sizeof( b2IslandSim ) );\n\t\t}\n\t}\n\n\t// destroy the merged set\n\tb2DestroySolverSet( world, setId2 );\n\n\tb2ValidateSolverSets( world );\n}\n\nvoid b2TransferBody( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Body* body )\n{\n\tif (targetSet == sourceSet)\n\t{\n\t\treturn;\n\t}\n\n\tint sourceIndex = body->localIndex;\n\tb2BodySim* sourceSim = b2BodySimArray_Get( &sourceSet->bodySims, sourceIndex );\n\n\tint targetIndex = targetSet->bodySims.count;\n\tb2BodySim* targetSim = b2BodySimArray_Add( &targetSet->bodySims );\n\tmemcpy( targetSim, sourceSim, sizeof( b2BodySim ) );\n\n\t// Clear transient body flags\n\ttargetSim->flags &= ~(b2_isFast | b2_isSpeedCapped | b2_hadTimeOfImpact);\n\n\t// Remove body sim from solver set that owns it\n\tint movedIndex = b2BodySimArray_RemoveSwap( &sourceSet->bodySims, sourceIndex );\n\tif ( movedIndex != B2_NULL_INDEX )\n\t{\n\t\t// Fix moved body index\n\t\tb2BodySim* movedSim = sourceSet->bodySims.data + sourceIndex;\n\t\tint movedId = movedSim->bodyId;\n\t\tb2Body* movedBody = b2BodyArray_Get( &world->bodies, movedId );\n\t\tB2_ASSERT( movedBody->localIndex == movedIndex );\n\t\tmovedBody->localIndex = sourceIndex;\n\t}\n\n\tif ( sourceSet->setIndex == b2_awakeSet )\n\t{\n\t\tb2BodyStateArray_RemoveSwap( &sourceSet->bodyStates, sourceIndex );\n\t}\n\telse if ( targetSet->setIndex == b2_awakeSet )\n\t{\n\t\tb2BodyState* state = b2BodyStateArray_Add( &targetSet->bodyStates );\n\t\t*state = b2_identityBodyState;\n\t\tstate->flags = body->flags;\n\t}\n\n\tbody->setIndex = targetSet->setIndex;\n\tbody->localIndex = targetIndex;\n}\n\nvoid b2TransferJoint( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Joint* joint )\n{\n\tif (targetSet == sourceSet)\n\t{\n\t\treturn;\n\t}\n\n\tint localIndex = joint->localIndex;\n\tint colorIndex = joint->colorIndex;\n\n\t// Retrieve source.\n\tb2JointSim* sourceSim;\n\tif ( sourceSet->setIndex == b2_awakeSet )\n\t{\n\t\tB2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );\n\t\tb2GraphColor* color = world->constraintGraph.colors + colorIndex;\n\n\t\tsourceSim = b2JointSimArray_Get( &color->jointSims, localIndex );\n\t}\n\telse\n\t{\n\t\tB2_ASSERT( colorIndex == B2_NULL_INDEX );\n\t\tsourceSim = b2JointSimArray_Get( &sourceSet->jointSims, localIndex );\n\t}\n\n\t// Create target and copy. Fix joint.\n\tif ( targetSet->setIndex == b2_awakeSet )\n\t{\n\t\tb2AddJointToGraph( world, sourceSim, joint );\n\t\tjoint->setIndex = b2_awakeSet;\n\t}\n\telse\n\t{\n\t\tjoint->setIndex = targetSet->setIndex;\n\t\tjoint->localIndex = targetSet->jointSims.count;\n\t\tjoint->colorIndex = B2_NULL_INDEX;\n\n\t\tb2JointSim* targetSim = b2JointSimArray_Add( &targetSet->jointSims );\n\t\tmemcpy( targetSim, sourceSim, sizeof( b2JointSim ) );\n\t}\n\n\t// Destroy source.\n\tif ( sourceSet->setIndex == b2_awakeSet )\n\t{\n\t\tb2RemoveJointFromGraph( world, joint->edges[0].bodyId, joint->edges[1].bodyId, colorIndex, localIndex );\n\t}\n\telse\n\t{\n\t\tint movedIndex = b2JointSimArray_RemoveSwap( &sourceSet->jointSims, localIndex );\n\t\tif ( movedIndex != B2_NULL_INDEX )\n\t\t{\n\t\t\t// fix swapped element\n\t\t\tb2JointSim* movedJointSim = sourceSet->jointSims.data + localIndex;\n\t\t\tint movedId = movedJointSim->jointId;\n\t\t\tb2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId );\n\t\t\tmovedJoint->localIndex = localIndex;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/solver_set.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"array.h\"\n\ntypedef struct b2Body b2Body;\ntypedef struct b2Joint b2Joint;\ntypedef struct b2World b2World;\n\n// The solver set type by index\nenum b2SolverSetType\n{\n\t// Static set for static bodies and joints between static bodies\n\tb2_staticSet = 0,\n\n\t// Disabled set for disabled bodies and their joints\n\tb2_disabledSet = 1,\n\n\t// Awake set for awake bodies and awake non-touching contacts. Awake touching contacts\n\t// and awake joints live in the constraint graph\n\tb2_awakeSet = 2,\n\n\t// The index of the first sleeping set. Each island that goes to sleep is put into\n\t// a sleeping set. This holds all bodies, contacts, and joints from the sleeping island.\n\t// A separate set for each sleeping island makes it very efficient to wake a single island.\n\tb2_firstSleepingSet = 3,\n};\n\n// This holds solver set data. The following sets are used:\n// - static set for all static bodies and joints between static bodies\n// - active set for all active bodies with body states (no contacts or joints)\n// - disabled set for disabled bodies and their joints\n// - all further sets are sleeping island sets along with their contacts and joints\n// The purpose of solver sets is to achieve high memory locality.\n// https://www.youtube.com/watch?v=nZNd5FjSquk\ntypedef struct b2SolverSet\n{\n\t// Body array. Empty for unused set.\n\tb2BodySimArray bodySims;\n\n\t// Body state only exists for active set\n\tb2BodyStateArray bodyStates;\n\n\t// This holds sleeping/disabled joints. Empty for static/active set.\n\tb2JointSimArray jointSims;\n\n\t// This holds all contacts for sleeping sets.\n\t// This holds non-touching contacts for the awake set.\n\tb2ContactSimArray contactSims;\n\n\t// The awake set has an array of islands. Sleeping sets normally have a single islands. However, joints\n\t// created between sleeping sets causes the sets to merge, leaving them with multiple islands. These sleeping\n\t// islands will be naturally merged with the set is woken.\n\t// The static and disabled sets have no islands.\n\t// Islands live in the solver sets to limit the number of islands that need to be considered for sleeping.\n\tb2IslandSimArray islandSims;\n\n\t// Aligns with b2World::solverSetIdPool. Used to create a stable id for body/contact/joint/islands.\n\tint setIndex;\n} b2SolverSet;\n\nvoid b2DestroySolverSet( b2World* world, int setIndex );\n\nvoid b2WakeSolverSet( b2World* world, int setIndex );\nvoid b2TrySleepIsland( b2World* world, int islandId );\n\n// Merge set 2 into set 1 then destroy set 2.\n// Warning: any pointers into these sets will be orphaned.\nvoid b2MergeSolverSets( b2World* world, int setIndex1, int setIndex2 );\n\nvoid b2TransferBody( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Body* body );\nvoid b2TransferJoint( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Joint* joint );\n\nB2_ARRAY_INLINE( b2SolverSet, b2SolverSet )\n"
  },
  {
    "path": "src/table.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"table.h\"\n\n#include \"atomic.h\"\n#include \"bitset.h\"\n#include \"core.h\"\n#include \"ctz.h\"\n\n#include <stdbool.h>\n#include <string.h>\n\n#if B2_SNOOP_TABLE_COUNTERS\nb2AtomicInt b2_findCount;\nb2AtomicInt b2_probeCount;\n#endif\n\nb2HashSet b2CreateSet( int capacity )\n{\n\tb2HashSet set = { 0 };\n\n\t// Capacity must be a power of 2\n\tif ( capacity > 16 )\n\t{\n\t\tset.capacity = b2RoundUpPowerOf2( capacity );\n\t}\n\telse\n\t{\n\t\tset.capacity = 16;\n\t}\n\n\tset.count = 0;\n\tset.items = b2Alloc( set.capacity * sizeof( b2SetItem ) );\n\tmemset( set.items, 0, set.capacity * sizeof( b2SetItem ) );\n\n\treturn set;\n}\n\nvoid b2DestroySet( b2HashSet* set )\n{\n\tb2Free( set->items, set->capacity * sizeof( b2SetItem ) );\n\tset->items = NULL;\n\tset->count = 0;\n\tset->capacity = 0;\n}\n\nvoid b2ClearSet( b2HashSet* set )\n{\n\tset->count = 0;\n\tmemset( set->items, 0, set->capacity * sizeof( b2SetItem ) );\n}\n\n// I need a good hash because the keys are built from pairs of increasing integers.\n// A simple hash like hash = (integer1 XOR integer2) has many collisions.\n// https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere/\n// https://preshing.com/20130107/this-hash-set-is-faster-than-a-judy-array/\n// todo try: https://www.jandrewrogers.com/2019/02/12/fast-perfect-hashing/\n// todo try:\n// https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/\n\n// I compared with CC on https://jacksonallan.github.io/c_cpp_hash_tables_benchmark/ and got slightly better performance\n// in the washer benchmark.\n// I compared with verstable across 8 benchmarks and the performance was similar.\n\n#if 0\n// Fast-hash\n// https://jonkagstrom.com/bit-mixer-construction\n// https://code.google.com/archive/p/fast-hash\nstatic uint64_t b2KeyHash( uint64_t key )\n{\n\tkey ^= key >> 23;\n\tkey *= 0x2127599BF4325C37ULL;\n\tkey ^= key >> 47;\n\treturn key;\n}\n#elif 1\nstatic uint64_t b2KeyHash( uint64_t key )\n{\n\t// Murmur hash\n\tuint64_t h = key;\n\th ^= h >> 33;\n\th *= 0xff51afd7ed558ccduLL;\n\th ^= h >> 33;\n\th *= 0xc4ceb9fe1a85ec53uLL;\n\th ^= h >> 33;\n\treturn h;\n}\n#endif\n\nstatic int b2FindSlot( const b2HashSet* set, uint64_t key, uint64_t hash )\n{\n#if B2_SNOOP_TABLE_COUNTERS\n\tb2AtomicFetchAddInt( &b2_findCount, 1 );\n#endif\n\n\tuint32_t capacity = set->capacity;\n\tuint32_t index = (uint32_t)hash & ( capacity - 1 );\n\tconst b2SetItem* items = set->items;\n\twhile ( items[index].key != 0 && items[index].key != key )\n\t{\n#if B2_SNOOP_TABLE_COUNTERS\n\t\tb2AtomicFetchAddInt( &b2_probeCount, 1 );\n#endif\n\t\tindex = ( index + 1 ) & ( capacity - 1 );\n\t}\n\n\treturn index;\n}\n\nstatic void b2AddKeyHaveCapacity( b2HashSet* set, uint64_t key, uint64_t hash )\n{\n\tint index = b2FindSlot( set, key, hash );\n\tb2SetItem* items = set->items;\n\n\tB2_ASSERT( items[index].key == 0 );\n\titems[index].key = key;\n\tset->count += 1;\n}\n\nstatic void b2GrowTable( b2HashSet* set )\n{\n\tuint32_t oldCount = set->count;\n\tB2_UNUSED( oldCount );\n\n\tuint32_t oldCapacity = set->capacity;\n\tb2SetItem* oldItems = set->items;\n\n\tset->count = 0;\n\t// Capacity must be a power of 2\n\tset->capacity = 2 * oldCapacity;\n\tset->items = b2Alloc( set->capacity * sizeof( b2SetItem ) );\n\tmemset( set->items, 0, set->capacity * sizeof( b2SetItem ) );\n\n\t// Transfer items into new array\n\tfor ( uint32_t i = 0; i < oldCapacity; ++i )\n\t{\n\t\tb2SetItem* item = oldItems + i;\n\t\tif ( item->key == 0 )\n\t\t{\n\t\t\t// this item was empty\n\t\t\tcontinue;\n\t\t}\n\n\t\tuint64_t hash = b2KeyHash( item->key );\n\t\tb2AddKeyHaveCapacity( set, item->key, hash );\n\t}\n\n\tB2_ASSERT( set->count == oldCount );\n\n\tb2Free( oldItems, oldCapacity * sizeof( b2SetItem ) );\n}\n\nbool b2ContainsKey( const b2HashSet* set, uint64_t key )\n{\n\t// key of zero is a sentinel\n\tB2_ASSERT( key != 0 );\n\tuint64_t hash = b2KeyHash( key );\n\tint index = b2FindSlot( set, key, hash );\n\treturn set->items[index].key == key;\n}\n\nint b2GetHashSetBytes( b2HashSet* set )\n{\n\treturn set->capacity * (int)sizeof( b2SetItem );\n}\n\nbool b2AddKey( b2HashSet* set, uint64_t key )\n{\n\t// key of zero is a sentinel\n\tB2_ASSERT( key != 0 );\n\n\tuint64_t hash = b2KeyHash( key );\n\tB2_ASSERT( hash != 0 );\n\n\tint index = b2FindSlot( set, key, hash );\n\tif ( set->items[index].key != 0 )\n\t{\n\t\t// Already in set\n\t\tB2_ASSERT( set->items[index].key == key );\n\t\treturn true;\n\t}\n\n\tif ( 2 * set->count >= set->capacity )\n\t{\n\t\tb2GrowTable( set );\n\t}\n\n\tb2AddKeyHaveCapacity( set, key, hash );\n\treturn false;\n}\n\n// See https://en.wikipedia.org/wiki/Open_addressing\nbool b2RemoveKey( b2HashSet* set, uint64_t key )\n{\n\tuint64_t hash = b2KeyHash( key );\n\tint i = b2FindSlot( set, key, hash );\n\tb2SetItem* items = set->items;\n\tif ( items[i].key == 0 )\n\t{\n\t\t// Not in set\n\t\treturn false;\n\t}\n\n\t// Mark item i as unoccupied\n\titems[i].key = 0;\n\n\tB2_ASSERT( set->count > 0 );\n\tset->count -= 1;\n\n\t// Attempt to fill item i\n\tint j = i;\n\tuint32_t capacity = set->capacity;\n\tfor ( ;; )\n\t{\n\t\tj = ( j + 1 ) & ( capacity - 1 );\n\t\tif ( items[j].key == 0 )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t// k is the first item for the hash of j\n\t\tuint64_t hash_j = b2KeyHash( items[j].key );\n\t\tint k = hash_j & ( capacity - 1 );\n\n\t\t// determine if k lies cyclically in (i,j]\n\t\t// i <= j: | i..k..j |\n\t\t// i > j: |.k..j  i....| or |....j     i..k.|\n\t\tif ( i <= j )\n\t\t{\n\t\t\tif ( i < k && k <= j )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ( i < k || k <= j )\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Move j into i\n\t\titems[i] = items[j];\n\n\t\t// Mark item j as unoccupied\n\t\titems[j].key = 0;\n\n\t\ti = j;\n\t}\n\n\treturn true;\n}\n\n// This function is here because ctz.h is included by\n// this file but not in bitset.c\nint b2CountSetBits( b2BitSet* bitSet )\n{\n\tint popCount = 0;\n\tuint32_t blockCount = bitSet->blockCount;\n\tfor ( uint32_t i = 0; i < blockCount; ++i )\n\t{\n\t\tpopCount += b2PopCount64( bitSet->bits[i] );\n\t}\n\n\treturn popCount;\n}\n"
  },
  {
    "path": "src/table.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#define B2_SHAPE_PAIR_KEY( K1, K2 ) K1 < K2 ? (uint64_t)K1 << 32 | (uint64_t)K2 : (uint64_t)K2 << 32 | (uint64_t)K1\n\ntypedef struct b2SetItem\n{\n\tuint64_t key;\n\n\t// storing lower 32 bits of hash\n\t// this is wasteful because I just need to know if the item is occupied\n\t// I could require the key to be non-zero and use 0 to indicate an empty slot\n\t// Update: looks like I store this to make growing the table faster, however this is wasteful once\n\t// the table has hit the high water mark\n\t//uint32_t hash;\n} b2SetItem;\n\ntypedef struct b2HashSet\n{\n\tb2SetItem* items;\n\tuint32_t capacity;\n\tuint32_t count;\n} b2HashSet;\n\nb2HashSet b2CreateSet( int capacity );\nvoid b2DestroySet( b2HashSet* set );\n\nvoid b2ClearSet( b2HashSet* set );\n\n// Returns true if key was already in set\nbool b2AddKey( b2HashSet* set, uint64_t key );\n\n// Returns true if the key was found\nbool b2RemoveKey( b2HashSet* set, uint64_t key );\n\nbool b2ContainsKey( const b2HashSet* set, uint64_t key );\n\nint b2GetHashSetBytes( b2HashSet* set );\n\nstatic inline int b2GetSetCount( b2HashSet* set )\n{\n\treturn set->count;\n}\n\nstatic inline int b2GetSetCapacity( b2HashSet* set )\n{\n\treturn set->capacity;\n}\n"
  },
  {
    "path": "src/timer.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"core.h\"\n\n#include \"box2d/base.h\"\n\n#include <stddef.h>\n\n#if defined( _MSC_VER )\n\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN 1\n#endif\n\n#include <Windows.h>\n\nstatic double s_invFrequency = 0.0;\n\nuint64_t b2GetTicks( void )\n{\n\tLARGE_INTEGER counter;\n\tQueryPerformanceCounter( &counter );\n\treturn (uint64_t)counter.QuadPart;\n}\n\nfloat b2GetMilliseconds( uint64_t ticks )\n{\n\tif ( s_invFrequency == 0.0 )\n\t{\n\t\tLARGE_INTEGER frequency;\n\t\tQueryPerformanceFrequency( &frequency );\n\n\t\ts_invFrequency = (double)frequency.QuadPart;\n\t\tif ( s_invFrequency > 0.0 )\n\t\t{\n\t\t\ts_invFrequency = 1000.0 / s_invFrequency;\n\t\t}\n\t}\n\n\tuint64_t ticksNow = b2GetTicks();\n\treturn (float)( s_invFrequency * ( ticksNow - ticks ) );\n}\n\nfloat b2GetMillisecondsAndReset( uint64_t* ticks )\n{\n\tif ( s_invFrequency == 0.0 )\n\t{\n\t\tLARGE_INTEGER frequency;\n\t\tQueryPerformanceFrequency( &frequency );\n\n\t\ts_invFrequency = (double)frequency.QuadPart;\n\t\tif ( s_invFrequency > 0.0 )\n\t\t{\n\t\t\ts_invFrequency = 1000.0 / s_invFrequency;\n\t\t}\n\t}\n\n\tuint64_t ticksNow = b2GetTicks();\n\tfloat ms = (float)( s_invFrequency * ( ticksNow - *ticks ) );\n\t*ticks = ticksNow;\n\treturn ms;\n}\n\nvoid b2Yield( void )\n{\n\tSwitchToThread();\n}\n\ntypedef struct b2Mutex\n{\n\tCRITICAL_SECTION cs;\n} b2Mutex;\n\nb2Mutex* b2CreateMutex( void )\n{\n\tb2Mutex* m = b2Alloc( sizeof( b2Mutex ) );\n\tInitializeCriticalSection( &m->cs );\n\treturn m;\n}\n\nvoid b2DestroyMutex( b2Mutex* m )\n{\n\tDeleteCriticalSection( &m->cs );\n\t*m = (b2Mutex){ 0 };\n\tb2Free( m, sizeof( b2Mutex ) );\n}\n\nvoid b2LockMutex( b2Mutex* m )\n{\n\tEnterCriticalSection( &m->cs );\n}\n\nvoid b2UnlockMutex( b2Mutex* m )\n{\n\tLeaveCriticalSection( &m->cs );\n}\n\n#elif defined( __linux__ ) || defined( __EMSCRIPTEN__ )\n\n#include <sched.h>\n#include <time.h>\n\nuint64_t b2GetTicks( void )\n{\n\tstruct timespec ts;\n\tclock_gettime( CLOCK_MONOTONIC, &ts );\n\treturn ts.tv_sec * 1000000000LL + ts.tv_nsec;\n}\n\nfloat b2GetMilliseconds( uint64_t ticks )\n{\n\tuint64_t ticksNow = b2GetTicks();\n\treturn (float)( ( ticksNow - ticks ) / 1000000.0 );\n}\n\nfloat b2GetMillisecondsAndReset( uint64_t* ticks )\n{\n\tuint64_t ticksNow = b2GetTicks();\n\tfloat ms = (float)( ( ticksNow - *ticks ) / 1000000.0 );\n\t*ticks = ticksNow;\n\treturn ms;\n}\n\nvoid b2Yield( void )\n{\n\tsched_yield();\n}\n\n#include <pthread.h>\ntypedef struct b2Mutex\n{\n\tpthread_mutex_t mtx;\n} b2Mutex;\n\nb2Mutex* b2CreateMutex( void )\n{\n\tb2Mutex* m = b2Alloc( sizeof( b2Mutex ) );\n\tpthread_mutex_init( &m->mtx, NULL );\n\treturn m;\n}\n\nvoid b2DestroyMutex( b2Mutex* m )\n{\n\tpthread_mutex_destroy( &m->mtx );\n\t*m = (b2Mutex){ 0 };\n\tb2Free( m, sizeof( b2Mutex ) );\n}\n\nvoid b2LockMutex( b2Mutex* m )\n{\n\tpthread_mutex_lock( &m->mtx );\n}\n\nvoid b2UnlockMutex( b2Mutex* m )\n{\n\tpthread_mutex_unlock( &m->mtx );\n}\n\n#elif defined( __APPLE__ )\n\n#include <mach/mach_time.h>\n#include <sched.h>\n#include <sys/time.h>\n\nstatic double s_invFrequency = 0.0;\n\nuint64_t b2GetTicks( void )\n{\n\treturn mach_absolute_time();\n}\n\nfloat b2GetMilliseconds( uint64_t ticks )\n{\n\tif ( s_invFrequency == 0 )\n\t{\n\t\tmach_timebase_info_data_t timebase;\n\t\tmach_timebase_info( &timebase );\n\n\t\t// convert to ns then to ms\n\t\ts_invFrequency = 1e-6 * (double)timebase.numer / (double)timebase.denom;\n\t}\n\n\tuint64_t ticksNow = b2GetTicks();\n\treturn (float)( s_invFrequency * ( ticksNow - ticks ) );\n}\n\nfloat b2GetMillisecondsAndReset( uint64_t* ticks )\n{\n\tif ( s_invFrequency == 0 )\n\t{\n\t\tmach_timebase_info_data_t timebase;\n\t\tmach_timebase_info( &timebase );\n\n\t\t// convert to ns then to ms\n\t\ts_invFrequency = 1e-6 * (double)timebase.numer / (double)timebase.denom;\n\t}\n\n\tuint64_t ticksNow = b2GetTicks();\n\tfloat ms = (float)( s_invFrequency * ( ticksNow - *ticks ) );\n\t*ticks = ticksNow;\n\treturn ms;\n}\n\nvoid b2Yield( void )\n{\n\tsched_yield();\n}\n\n#include <pthread.h>\ntypedef struct b2Mutex\n{\n\tpthread_mutex_t mtx;\n} b2Mutex;\n\nb2Mutex* b2CreateMutex( void )\n{\n\tb2Mutex* m = b2Alloc( sizeof( b2Mutex ) );\n\tpthread_mutex_init( &m->mtx, NULL );\n\treturn m;\n}\n\nvoid b2DestroyMutex( b2Mutex* m )\n{\n\tpthread_mutex_destroy( &m->mtx );\n\t*m = (b2Mutex){ 0 };\n\tb2Free( m, sizeof( b2Mutex ) );\n}\n\nvoid b2LockMutex( b2Mutex* m )\n{\n\tpthread_mutex_lock( &m->mtx );\n}\n\nvoid b2UnlockMutex( b2Mutex* m )\n{\n\tpthread_mutex_unlock( &m->mtx );\n}\n\n#else\n\nuint64_t b2GetTicks( void )\n{\n\treturn 0;\n}\n\nfloat b2GetMilliseconds( uint64_t ticks )\n{\n\t( (void)( ticks ) );\n\treturn 0.0f;\n}\n\nfloat b2GetMillisecondsAndReset( uint64_t* ticks )\n{\n\t( (void)( ticks ) );\n\treturn 0.0f;\n}\n\nvoid b2Yield( void )\n{\n}\n\ntypedef struct b2Mutex\n{\n\tint dummy;\n} b2Mutex;\n\nb2Mutex* b2CreateMutex( void )\n{\n\tb2Mutex* m = b2Alloc( sizeof( b2Mutex ) );\n\tm->dummy = 42;\n\treturn m;\n}\n\nvoid b2DestroyMutex( b2Mutex* m )\n{\n\t*m = (b2Mutex){ 0 };\n\tb2Free( m, sizeof( b2Mutex ) );\n}\n\nvoid b2LockMutex( b2Mutex* m )\n{\n\t(void)m;\n}\n\nvoid b2UnlockMutex( b2Mutex* m )\n{\n\t(void)m;\n}\n\n#endif\n\n// djb2 hash\n// https://en.wikipedia.org/wiki/List_of_hash_functions\nuint32_t b2Hash( uint32_t hash, const uint8_t* data, int count )\n{\n\tuint32_t result = hash;\n\tfor ( int i = 0; i < count; i++ )\n\t{\n\t\tresult = ( result << 5 ) + result + data[i];\n\t}\n\n\treturn result;\n}\n"
  },
  {
    "path": "src/types.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"box2d/types.h\"\n\n#include \"constants.h\"\n#include \"core.h\"\n\nb2WorldDef b2DefaultWorldDef( void )\n{\n\tb2WorldDef def = { 0 };\n\tdef.gravity.x = 0.0f;\n\tdef.gravity.y = -10.0f;\n\tdef.hitEventThreshold = 1.0f * b2_lengthUnitsPerMeter;\n\tdef.restitutionThreshold = 1.0f * b2_lengthUnitsPerMeter;\n\tdef.contactSpeed = 3.0f * b2_lengthUnitsPerMeter;\n\tdef.contactHertz = 30.0;\n\tdef.contactDampingRatio = 10.0f;\n\n\t// 400 meters per second, faster than the speed of sound\n\tdef.maximumLinearSpeed = 400.0f * b2_lengthUnitsPerMeter;\n\n\tdef.enableSleep = true;\n\tdef.enableContinuous = true;\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2BodyDef b2DefaultBodyDef( void )\n{\n\tb2BodyDef def = { 0 };\n\tdef.type = b2_staticBody;\n\tdef.rotation = b2Rot_identity;\n\tdef.sleepThreshold = 0.05f * b2_lengthUnitsPerMeter;\n\tdef.gravityScale = 1.0f;\n\tdef.enableSleep = true;\n\tdef.isAwake = true;\n\tdef.isEnabled = true;\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2Filter b2DefaultFilter( void )\n{\n\tb2Filter filter = { B2_DEFAULT_CATEGORY_BITS, B2_DEFAULT_MASK_BITS, 0 };\n\treturn filter;\n}\n\nb2QueryFilter b2DefaultQueryFilter( void )\n{\n\tb2QueryFilter filter = { B2_DEFAULT_CATEGORY_BITS, B2_DEFAULT_MASK_BITS };\n\treturn filter;\n}\n\nb2ShapeDef b2DefaultShapeDef( void )\n{\n\tb2ShapeDef def = { 0 };\n\tdef.material.friction = 0.6f;\n\tdef.density = 1.0f;\n\tdef.filter = b2DefaultFilter();\n\tdef.updateBodyMass = true;\n\tdef.invokeContactCreation = true;\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nb2SurfaceMaterial b2DefaultSurfaceMaterial( void )\n{\n\tb2SurfaceMaterial material = {\n\t\t.friction = 0.6f,\n\t};\n\n\treturn material;\n}\n\nb2ChainDef b2DefaultChainDef( void )\n{\n\tstatic b2SurfaceMaterial defaultMaterial = {\n\t\t.friction = 0.6f,\n\t};\n\n\tb2ChainDef def = { 0 };\n\tdef.materials = &defaultMaterial;\n\tdef.materialCount = 1;\n\tdef.filter = b2DefaultFilter();\n\tdef.internalValue = B2_SECRET_COOKIE;\n\treturn def;\n}\n\nstatic void b2EmptyDrawPolygon( const b2Vec2* vertices, int vertexCount, b2HexColor color, void* context )\n{\n\tB2_UNUSED( vertices, vertexCount, color, context );\n}\n\nstatic void b2EmptyDrawSolidPolygon( b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius,\n\t\t\t\t\t\t\t\t\t b2HexColor color, void* context )\n{\n\tB2_UNUSED( transform, vertices, vertexCount, radius, color, context );\n}\n\nstatic void b2EmptyDrawCircle( b2Vec2 center, float radius, b2HexColor color, void* context )\n{\n\tB2_UNUSED( center, radius, color, context );\n}\n\nstatic void b2EmptyDrawSolidCircle( b2Transform transform, float radius, b2HexColor color, void* context )\n{\n\tB2_UNUSED( transform, radius, color, context );\n}\n\nstatic void b2EmptyDrawSolidCapsule( b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color, void* context )\n{\n\tB2_UNUSED( p1, p2, radius, color, context );\n}\n\nstatic void b2EmptyDrawSegment( b2Vec2 p1, b2Vec2 p2, b2HexColor color, void* context )\n{\n\tB2_UNUSED( p1, p2, color, context );\n}\n\nstatic void b2EmptyDrawTransform( b2Transform transform, void* context )\n{\n\tB2_UNUSED( transform, context );\n}\n\nstatic void b2EmptyDrawPoint( b2Vec2 p, float size, b2HexColor color, void* context )\n{\n\tB2_UNUSED( p, size, color, context );\n}\n\nstatic void b2EmptyDrawString( b2Vec2 p, const char* s, b2HexColor color, void* context )\n{\n\tB2_UNUSED( p, s, color, context );\n}\n\nb2DebugDraw b2DefaultDebugDraw( void )\n{\n\tb2DebugDraw draw = { 0 };\n\n\t// These allow the user to skip some implementations and not hit null exceptions.\n\tdraw.DrawPolygonFcn = b2EmptyDrawPolygon;\n\tdraw.DrawSolidPolygonFcn = b2EmptyDrawSolidPolygon;\n\tdraw.DrawCircleFcn = b2EmptyDrawCircle;\n\tdraw.DrawSolidCircleFcn = b2EmptyDrawSolidCircle;\n\tdraw.DrawSolidCapsuleFcn = b2EmptyDrawSolidCapsule;\n\tdraw.DrawLineFcn = b2EmptyDrawSegment;\n\tdraw.DrawTransformFcn = b2EmptyDrawTransform;\n\tdraw.DrawPointFcn = b2EmptyDrawPoint;\n\tdraw.DrawStringFcn = b2EmptyDrawString;\n\n\tdraw.drawingBounds.lowerBound = (b2Vec2){ -FLT_MAX, -FLT_MAX };\n\tdraw.drawingBounds.upperBound = (b2Vec2){ FLT_MAX, FLT_MAX };\n\tdraw.forceScale = 1.0f;\n\tdraw.jointScale = 1.0f;\n\tdraw.drawShapes = true;\n\t\n\treturn draw;\n}\n"
  },
  {
    "path": "src/weld_joint.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"body.h\"\n#include \"core.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"solver.h\"\n#include \"solver_set.h\"\n\n// needed for dll export\n#include \"box2d/box2d.h\"\n\n#define B2_WELD_BLOCK_SOLVE 0\n\n#if B2_WELD_BLOCK_SOLVE\ntypedef struct\n{\n\tfloat x, y, z;\n} b2Vec3;\n\n// A 3-by-3 matrix. Stored in column-major order.\ntypedef struct\n{\n\tb2Vec3 cx, cy, cz;\n} b2Mat33;\n\nstatic inline float b2Dot3( b2Vec3 a, b2Vec3 b )\n{\n\treturn a.x * b.x + a.y * b.y + a.z * b.z;\n}\n\nstatic inline b2Vec3 b2Cross3( b2Vec3 a, b2Vec3 b )\n{\n\treturn (b2Vec3){ a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x };\n}\n\nstatic inline b2Vec3 b2Solve33(const b2Mat33* m, b2Vec3 b )\n{\n\tfloat det = b2Dot3( m->cx, b2Cross3( m->cy, m->cz ) );\n\tif ( det != 0.0f )\n\t{\n\t\tdet = 1.0f / det;\n\t}\n\n\tb2Vec3 x;\n\tx.x = det * b2Dot3( b, b2Cross3( m->cy, m->cz ) );\n\tx.y = det * b2Dot3( m->cx, b2Cross3( b, m->cz ) );\n\tx.z = det * b2Dot3( m->cx, b2Cross3( m->cy, b ) );\n\treturn x;\n}\n#endif\n\nvoid b2WeldJoint_SetLinearHertz( b2JointId jointId, float hertz )\n{\n\tB2_ASSERT( b2IsValidFloat( hertz ) && hertz >= 0.0f );\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint );\n\tjoint->weldJoint.linearHertz = hertz;\n}\n\nfloat b2WeldJoint_GetLinearHertz( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint );\n\treturn joint->weldJoint.linearHertz;\n}\n\nvoid b2WeldJoint_SetLinearDampingRatio( b2JointId jointId, float dampingRatio )\n{\n\tB2_ASSERT( b2IsValidFloat( dampingRatio ) && dampingRatio >= 0.0f );\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint );\n\tjoint->weldJoint.linearDampingRatio = dampingRatio;\n}\n\nfloat b2WeldJoint_GetLinearDampingRatio( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint );\n\treturn joint->weldJoint.linearDampingRatio;\n}\n\nvoid b2WeldJoint_SetAngularHertz( b2JointId jointId, float hertz )\n{\n\tB2_ASSERT( b2IsValidFloat( hertz ) && hertz >= 0.0f );\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint );\n\tjoint->weldJoint.angularHertz = hertz;\n}\n\nfloat b2WeldJoint_GetAngularHertz( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint );\n\treturn joint->weldJoint.angularHertz;\n}\n\nvoid b2WeldJoint_SetAngularDampingRatio( b2JointId jointId, float dampingRatio )\n{\n\tB2_ASSERT( b2IsValidFloat( dampingRatio ) && dampingRatio >= 0.0f );\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint );\n\tjoint->weldJoint.angularDampingRatio = dampingRatio;\n}\n\nfloat b2WeldJoint_GetAngularDampingRatio( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint );\n\treturn joint->weldJoint.angularDampingRatio;\n}\n\nb2Vec2 b2GetWeldJointForce( b2World* world, b2JointSim* base )\n{\n\tb2Vec2 force = b2MulSV( world->inv_h, base->weldJoint.linearImpulse );\n\treturn force;\n}\n\nfloat b2GetWeldJointTorque( b2World* world, b2JointSim* base )\n{\n\treturn world->inv_h * base->weldJoint.angularImpulse;\n}\n\n// Point-to-point constraint\n// C = p2 - p1\n// Cdot = v2 - v1\n//      = v2 + cross(w2, r2) - v1 - cross(w1, r1)\n// J = [-E -r1_skew E r2_skew ]\n// Identity used:\n// w k % (rx i + ry j) = w * (-ry i + rx j)\n\n// Angle constraint\n// C = angle2 - angle1 - referenceAngle\n// Cdot = w2 - w1\n// J = [0 0 -1 0 0 1]\n// K = invI1 + invI2\n\n// 3x3 Block\n// K = [J1] * invM * [J1T J2T]\n//     [J2]\n//   = [J1] * [invM * J1T invM * J2T]\n//     [J2]\n//   = [J1 * invM * J1T J1 * invM * J2T]\n//     [J2 * invM * J1T J2 * invM * J2T]\n\nvoid b2PrepareWeldJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_weldJoint );\n\n\t// chase body id to the solver set where the body lives\n\tint idA = base->bodyIdA;\n\tint idB = base->bodyIdB;\n\n\tb2World* world = context->world;\n\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, idA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, idB );\n\n\tB2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet );\n\tb2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex );\n\tb2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex );\n\n\tint localIndexA = bodyA->localIndex;\n\tint localIndexB = bodyB->localIndex;\n\n\tb2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA );\n\tb2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB );\n\n\tfloat mA = bodySimA->invMass;\n\tfloat iA = bodySimA->invInertia;\n\tfloat mB = bodySimB->invMass;\n\tfloat iB = bodySimB->invInertia;\n\n\tbase->invMassA = mA;\n\tbase->invMassB = mB;\n\tbase->invIA = iA;\n\tbase->invIB = iB;\n\n\tb2WeldJoint* joint = &base->weldJoint;\n\tjoint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX;\n\tjoint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX;\n\n\t// Compute joint anchor frames with world space rotation, relative to center of mass\n\tjoint->frameA.q = b2MulRot( bodySimA->transform.q, base->localFrameA.q );\n\tjoint->frameA.p = b2RotateVector( bodySimA->transform.q, b2Sub( base->localFrameA.p, bodySimA->localCenter ) );\n\tjoint->frameB.q = b2MulRot( bodySimB->transform.q, base->localFrameB.q );\n\tjoint->frameB.p = b2RotateVector( bodySimB->transform.q, b2Sub( base->localFrameB.p, bodySimB->localCenter ) );\n\n\t// Compute the initial center delta. Incremental position updates are relative to this.\n\tjoint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center );\n\n\tfloat ka = iA + iB;\n\tjoint->axialMass = ka > 0.0f ? 1.0f / ka : 0.0f;\n\n\tif ( joint->linearHertz == 0.0f )\n\t{\n\t\tjoint->linearSpring = base->constraintSoftness;\n\t}\n\telse\n\t{\n\t\tjoint->linearSpring = b2MakeSoft( joint->linearHertz, joint->linearDampingRatio, context->h );\n\t}\n\n\tif ( joint->angularHertz == 0.0f )\n\t{\n\t\tjoint->angularSpring = base->constraintSoftness;\n\t}\n\telse\n\t{\n\t\tjoint->angularSpring = b2MakeSoft( joint->angularHertz, joint->angularDampingRatio, context->h );\n\t}\n\n\tif ( context->enableWarmStarting == false )\n\t{\n\t\tjoint->linearImpulse = b2Vec2_zero;\n\t\tjoint->angularImpulse = 0.0f;\n\t}\n}\n\nvoid b2WarmStartWeldJoint( b2JointSim* base, b2StepContext* context )\n{\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2WeldJoint* joint = &base->weldJoint;\n\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, joint->linearImpulse );\n\t\tstateA->angularVelocity -= iA * ( b2Cross( rA, joint->linearImpulse ) + joint->angularImpulse );\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, joint->linearImpulse );\n\t\tstateB->angularVelocity += iB * ( b2Cross( rB, joint->linearImpulse ) + joint->angularImpulse );\n\t}\n}\n\nvoid b2SolveWeldJoint( b2JointSim* base, b2StepContext* context, bool useBias )\n{\n\tB2_ASSERT( base->type == b2_weldJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2WeldJoint* joint = &base->weldJoint;\n\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 vA = stateA->linearVelocity;\n\tfloat wA = stateA->angularVelocity;\n\tb2Vec2 vB = stateB->linearVelocity;\n\tfloat wB = stateB->angularVelocity;\n\n\t// Block solve doesn't work correctly with mixed stiffness values\n#if B2_WELD_BLOCK_SOLVE\n\t// J = [-I -r1_skew I r2_skew]\n\t//     [ 0       -1 0       1]\n\t// r_skew = [-ry; rx]\n\n\t// Matlab\n\t// K = [ mA+r1y^2*iA+mB+r2y^2*iB,  -r1y*iA*r1x-r2y*iB*r2x,          -r1y*iA-r2y*iB]\n\t//     [  -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB,           r1x*iA+r2x*iB]\n\t//     [          -r1y*iA-r2y*iB,           r1x*iA+r2x*iB,                   iA+iB]\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\tb2Mat33 K;\n\tK.cx.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB;\n\tK.cy.x = -rA.y * rA.x * iA - rB.y * rB.x * iB;\n\tK.cz.x = -rA.y * iA - rB.y * iB;\n\tK.cx.y = K.cy.x;\n\tK.cy.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB;\n\tK.cz.y = rA.x * iA + rB.x * iB;\n\tK.cx.z = K.cz.x;\n\tK.cy.z = K.cz.y;\n\tK.cz.z = iA + iB;\n\n\tb2Vec3 bias = {0.0f, 0.0f, 0.0f};\n\tfloat linearMassScale = 1.0f;\n\tfloat linearImpulseScale = 0.0f;\n\tif ( useBias || joint->linearHertz > 0.0f )\n\t{\n\t\t// linear\n\t\tb2Vec2 dcA = stateA->deltaPosition;\n\t\tb2Vec2 dcB = stateB->deltaPosition;\n\t\tb2Vec2 jointTranslation = b2Add( b2Add( b2Sub( dcB, dcA ), b2Sub( rB, rA ) ), joint->deltaCenter );\n\n\t\tbias.x = joint->linearSpring.biasRate * jointTranslation.x;\n\t\tbias.y = joint->linearSpring.biasRate * jointTranslation.y;\n\t\t\n\t\tlinearMassScale = joint->linearSpring.massScale;\n\t\tlinearImpulseScale = joint->linearSpring.impulseScale;\n\t}\n\n\tfloat angularMassScale = 1.0f;\n\tfloat angularImpulseScale = 0.0f;\n\tif ( useBias || joint->angularHertz > 0.0f )\n\t{\n\t\t// angular\n\t\tb2Rot qA = b2MulRot( stateA->deltaRotation, joint->frameA.q );\n\t\tb2Rot qB = b2MulRot( stateB->deltaRotation, joint->frameB.q );\n\t\tb2Rot relQ = b2InvMulRot( qA, qB );\n\t\tfloat jointAngle = b2Rot_GetAngle( relQ );\n\n\t\tbias.z = joint->angularSpring.biasRate * jointAngle;\n\n\t\tangularMassScale = joint->angularSpring.massScale;\n\t\tangularImpulseScale = joint->angularSpring.impulseScale;\n\t}\n\n\tb2Vec2 Cdot1 = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) );\n\tfloat Cdot2 = wB - wA;\n\n\tb2Vec3 Cdot = {Cdot1.x + bias.x, Cdot1.y + bias.y, Cdot2 + bias.z};\n\n\tb2Vec3 b = b2Solve33( &K, Cdot );\n\n\tb2Vec2 linearImpulse = {\n\t\t-linearMassScale * b.x - linearImpulseScale * joint->linearImpulse.x,\n\t\t-linearMassScale * b.y - linearImpulseScale * joint->linearImpulse.y,\n\t};\n\tjoint->linearImpulse = b2Add( joint->linearImpulse, linearImpulse );\n\n\tfloat angularImpulse = -angularMassScale * b.z - angularImpulseScale * joint->angularImpulse;\n\tjoint->angularImpulse += angularImpulse;\n\n\tvA = b2MulSub( vA, mA, linearImpulse );\n\twA -= iA * (b2Cross( rA, linearImpulse ) + angularImpulse);\n\tvB = b2MulAdd( vB, mB, linearImpulse );\n\twB += iB * (b2Cross( rB, linearImpulse ) + angularImpulse);\n\n\t// todo debugging\n\tCdot1 = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) );\n\tCdot2 = wB - wA;\n\n\tif ( useBias == false && b2Length(Cdot1) > 0.0001f )\n\t{\n\t\tCdot1.x += 0.0f;\n\t}\n\n\tif ( useBias == false && b2AbsFloat( Cdot2 ) > 0.0001f )\n\t{\n\t\tCdot2 += 0.0f;\n\t}\n\n#else\n\n\t// angular constraint\n\t{\n\t\tb2Rot qA = b2MulRot( stateA->deltaRotation, joint->frameA.q );\n\t\tb2Rot qB = b2MulRot( stateB->deltaRotation, joint->frameB.q );\n\t\tb2Rot relQ = b2InvMulRot( qA, qB );\n\t\tfloat jointAngle = b2Rot_GetAngle( relQ );\n\n\t\tfloat bias = 0.0f;\n\t\tfloat massScale = 1.0f;\n\t\tfloat impulseScale = 0.0f;\n\t\tif ( useBias || joint->angularHertz > 0.0f )\n\t\t{\n\t\t\tfloat C = jointAngle;\n\t\t\tbias = joint->angularSpring.biasRate * C;\n\t\t\tmassScale = joint->angularSpring.massScale;\n\t\t\timpulseScale = joint->angularSpring.impulseScale;\n\t\t}\n\n\t\tfloat Cdot = wB - wA;\n\t\tfloat impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->angularImpulse;\n\t\tjoint->angularImpulse += impulse;\n\n\t\twA -= iA * impulse;\n\t\twB += iB * impulse;\n\t}\n\n\t// linear constraint\n\t{\n\t\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\t\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\t\tb2Vec2 bias = b2Vec2_zero;\n\t\tfloat massScale = 1.0f;\n\t\tfloat impulseScale = 0.0f;\n\t\tif ( useBias || joint->linearHertz > 0.0f )\n\t\t{\n\t\t\tb2Vec2 dcA = stateA->deltaPosition;\n\t\t\tb2Vec2 dcB = stateB->deltaPosition;\n\t\t\tb2Vec2 C = b2Add( b2Add( b2Sub( dcB, dcA ), b2Sub( rB, rA ) ), joint->deltaCenter );\n\n\t\t\tbias = b2MulSV( joint->linearSpring.biasRate, C );\n\t\t\tmassScale = joint->linearSpring.massScale;\n\t\t\timpulseScale = joint->linearSpring.impulseScale;\n\t\t}\n\n\t\tb2Vec2 Cdot = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) );\n\n\t\tb2Mat22 K;\n\t\tK.cx.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB;\n\t\tK.cy.x = -rA.y * rA.x * iA - rB.y * rB.x * iB;\n\t\tK.cx.y = K.cy.x;\n\t\tK.cy.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB;\n\t\tb2Vec2 b = b2Solve22( K, b2Add( Cdot, bias ) );\n\n\t\tb2Vec2 impulse = {\n\t\t\t-massScale * b.x - impulseScale * joint->linearImpulse.x,\n\t\t\t-massScale * b.y - impulseScale * joint->linearImpulse.y,\n\t\t};\n\n\t\tjoint->linearImpulse = b2Add( joint->linearImpulse, impulse );\n\n\t\tvA = b2MulSub( vA, mA, impulse );\n\t\twA -= iA * b2Cross( rA, impulse );\n\t\tvB = b2MulAdd( vB, mB, impulse );\n\t\twB += iB * b2Cross( rB, impulse );\n\t}\n#endif\n\n\tB2_ASSERT( b2IsValidVec2( vA ) );\n\tB2_ASSERT( b2IsValidFloat( wA ) );\n\tB2_ASSERT( b2IsValidVec2( vB ) );\n\tB2_ASSERT( b2IsValidFloat( wB ) );\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = vA;\n\t\tstateA->angularVelocity = wA;\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = vB;\n\t\tstateB->angularVelocity = wB;\n\t}\n}\n\n#if 0\nvoid b2DumpWeldJoint()\n{\n\tint32 indexA = bodyA->islandIndex;\n\tint32 indexB = bodyB->islandIndex;\n\n\tb2Dump(\"  b2WeldJointDef jd;\\n\");\n\tb2Dump(\"  jd.bodyA = sims[%d];\\n\", indexA);\n\tb2Dump(\"  jd.bodyB = sims[%d];\\n\", indexB);\n\tb2Dump(\"  jd.collideConnected = bool(%d);\\n\", collideConnected);\n\tb2Dump(\"  jd.localAnchorA.Set(%.9g, %.9g);\\n\", localAnchorA.x, localAnchorA.y);\n\tb2Dump(\"  jd.localAnchorB.Set(%.9g, %.9g);\\n\", localAnchorB.x, localAnchorB.y);\n\tb2Dump(\"  jd.referenceAngle = %.9g;\\n\", referenceAngle);\n\tb2Dump(\"  jd.stiffness = %.9g;\\n\", stiffness);\n\tb2Dump(\"  jd.damping = %.9g;\\n\", damping);\n\tb2Dump(\"  joints[%d] = world->CreateJoint(&jd);\\n\", index);\n}\n#endif\n\nvoid b2DrawWeldJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, float drawScale )\n{\n\tB2_ASSERT( base->type == b2_weldJoint );\n\n\tb2Transform frameA = b2MulTransforms( transformA, base->localFrameA );\n\tb2Transform frameB = b2MulTransforms( transformB, base->localFrameB );\n\n\tb2Polygon box = b2MakeBox( 0.25f * drawScale, 0.125f * drawScale );\n\n\tb2Vec2 points[4];\n\n\tfor ( int i = 0; i < 4; ++i )\n\t{\n\t\tpoints[i] = b2TransformPoint( frameA, box.vertices[i] );\n\t}\n\tdraw->DrawPolygonFcn( points, 4, b2_colorDarkOrange, draw->context );\n\n\tfor ( int i = 0; i < 4; ++i )\n\t{\n\t\tpoints[i] = b2TransformPoint( frameB, box.vertices[i] );\n\t}\n\n\tdraw->DrawPolygonFcn( points, 4, b2_colorDarkCyan, draw->context );\n}\n"
  },
  {
    "path": "src/wheel_joint.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"body.h\"\n#include \"core.h\"\n#include \"joint.h\"\n#include \"physics_world.h\"\n#include \"solver.h\"\n#include \"solver_set.h\"\n\n// needed for dll export\n#include \"box2d/box2d.h\"\n\n#include <stdio.h>\n\nvoid b2WheelJoint_EnableSpring( b2JointId jointId, bool enableSpring )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\n\tif ( enableSpring != joint->wheelJoint.enableSpring )\n\t{\n\t\tjoint->wheelJoint.enableSpring = enableSpring;\n\t\tjoint->wheelJoint.springImpulse = 0.0f;\n\t}\n}\n\nbool b2WheelJoint_IsSpringEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn joint->wheelJoint.enableSpring;\n}\n\nvoid b2WheelJoint_SetSpringHertz( b2JointId jointId, float hertz )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\tjoint->wheelJoint.hertz = hertz;\n}\n\nfloat b2WheelJoint_GetSpringHertz( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn joint->wheelJoint.hertz;\n}\n\nvoid b2WheelJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\tjoint->wheelJoint.dampingRatio = dampingRatio;\n}\n\nfloat b2WheelJoint_GetSpringDampingRatio( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn joint->wheelJoint.dampingRatio;\n}\n\nvoid b2WheelJoint_EnableLimit( b2JointId jointId, bool enableLimit )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\tif ( joint->wheelJoint.enableLimit != enableLimit )\n\t{\n\t\tjoint->wheelJoint.lowerImpulse = 0.0f;\n\t\tjoint->wheelJoint.upperImpulse = 0.0f;\n\t\tjoint->wheelJoint.enableLimit = enableLimit;\n\t}\n}\n\nbool b2WheelJoint_IsLimitEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn joint->wheelJoint.enableLimit;\n}\n\nfloat b2WheelJoint_GetLowerLimit( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn joint->wheelJoint.lowerTranslation;\n}\n\nfloat b2WheelJoint_GetUpperLimit( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn joint->wheelJoint.upperTranslation;\n}\n\nvoid b2WheelJoint_SetLimits( b2JointId jointId, float lower, float upper )\n{\n\tB2_ASSERT( lower <= upper );\n\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\tif ( lower != joint->wheelJoint.lowerTranslation || upper != joint->wheelJoint.upperTranslation )\n\t{\n\t\tjoint->wheelJoint.lowerTranslation = b2MinFloat( lower, upper );\n\t\tjoint->wheelJoint.upperTranslation = b2MaxFloat( lower, upper );\n\t\tjoint->wheelJoint.lowerImpulse = 0.0f;\n\t\tjoint->wheelJoint.upperImpulse = 0.0f;\n\t}\n}\n\nvoid b2WheelJoint_EnableMotor( b2JointId jointId, bool enableMotor )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\tif ( joint->wheelJoint.enableMotor != enableMotor )\n\t{\n\t\tjoint->wheelJoint.motorImpulse = 0.0f;\n\t\tjoint->wheelJoint.enableMotor = enableMotor;\n\t}\n}\n\nbool b2WheelJoint_IsMotorEnabled( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn joint->wheelJoint.enableMotor;\n}\n\nvoid b2WheelJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\tjoint->wheelJoint.motorSpeed = motorSpeed;\n}\n\nfloat b2WheelJoint_GetMotorSpeed( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn joint->wheelJoint.motorSpeed;\n}\n\nfloat b2WheelJoint_GetMotorTorque( b2JointId jointId )\n{\n\tb2World* world = b2GetWorld( jointId.world0 );\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn world->inv_h * joint->wheelJoint.motorImpulse;\n}\n\nvoid b2WheelJoint_SetMaxMotorTorque( b2JointId jointId, float torque )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\tjoint->wheelJoint.maxMotorTorque = torque;\n}\n\nfloat b2WheelJoint_GetMaxMotorTorque( b2JointId jointId )\n{\n\tb2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint );\n\treturn joint->wheelJoint.maxMotorTorque;\n}\n\nb2Vec2 b2GetWheelJointForce( b2World* world, b2JointSim* base )\n{\n\tint idA = base->bodyIdA;\n\tb2Transform transformA = b2GetBodyTransform( world, idA );\n\n\tb2Vec2 localAxisA = b2RotateVector( base->localFrameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\tb2Vec2 axisA = b2RotateVector( transformA.q, localAxisA );\n\tb2Vec2 perpA = b2LeftPerp( axisA );\n\n\tb2WheelJoint* joint = &base->wheelJoint;\n\n\tfloat perpForce = world->inv_h * joint->perpImpulse;\n\tfloat axialForce = world->inv_h * ( joint->springImpulse + joint->lowerImpulse - joint->upperImpulse );\n\n\tb2Vec2 force = b2Add( b2MulSV( perpForce, perpA ), b2MulSV( axialForce, axisA ) );\n\treturn force;\n}\n\nfloat b2GetWheelJointTorque( b2World* world, b2JointSim* base )\n{\n\treturn world->inv_h * base->wheelJoint.motorImpulse;\n}\n\n// Linear constraint (point-to-line)\n// d = pB - pA = xB + rB - xA - rA\n// C = dot(ay, d)\n// Cdot = dot(d, cross(wA, ay)) + dot(ay, vB + cross(wB, rB) - vA - cross(wA, rA))\n//      = -dot(ay, vA) - dot(cross(d + rA, ay), wA) + dot(ay, vB) + dot(cross(rB, ay), vB)\n// J = [-ay, -cross(d + rA, ay), ay, cross(rB, ay)]\n\n// Spring linear constraint\n// C = dot(ax, d)\n// Cdot = = -dot(ax, vA) - dot(cross(d + rA, ax), wA) + dot(ax, vB) + dot(cross(rB, ax), vB)\n// J = [-ax -cross(d+rA, ax) ax cross(rB, ax)]\n\n// Motor rotational constraint\n// Cdot = wB - wA\n// J = [0 0 -1 0 0 1]\n\nvoid b2PrepareWheelJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_wheelJoint );\n\n\t// chase body id to the solver set where the body lives\n\tint idA = base->bodyIdA;\n\tint idB = base->bodyIdB;\n\n\tb2World* world = context->world;\n\n\tb2Body* bodyA = b2BodyArray_Get( &world->bodies, idA );\n\tb2Body* bodyB = b2BodyArray_Get( &world->bodies, idB );\n\n\tB2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet );\n\tb2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex );\n\tb2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex );\n\n\tint localIndexA = bodyA->localIndex;\n\tint localIndexB = bodyB->localIndex;\n\n\tb2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA );\n\tb2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB );\n\n\tfloat mA = bodySimA->invMass;\n\tfloat iA = bodySimA->invInertia;\n\tfloat mB = bodySimB->invMass;\n\tfloat iB = bodySimB->invInertia;\n\n\tbase->invMassA = mA;\n\tbase->invMassB = mB;\n\tbase->invIA = iA;\n\tbase->invIB = iB;\n\n\tb2WheelJoint* joint = &base->wheelJoint;\n\n\tjoint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX;\n\tjoint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX;\n\n\t// Compute joint anchor frames with world space rotation, relative to center of mass\n\tjoint->frameA.q = b2MulRot( bodySimA->transform.q, base->localFrameA.q );\n\tjoint->frameA.p = b2RotateVector( bodySimA->transform.q, b2Sub( base->localFrameA.p, bodySimA->localCenter ) );\n\tjoint->frameB.q = b2MulRot( bodySimB->transform.q, base->localFrameB.q );\n\tjoint->frameB.p = b2RotateVector( bodySimB->transform.q, b2Sub( base->localFrameB.p, bodySimB->localCenter ) );\n\n\t// Compute the initial center delta. Incremental position updates are relative to this.\n\tjoint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center );\n\n\tb2Vec2 rA = joint->frameA.p;\n\tb2Vec2 rB = joint->frameB.p;\n\n\tb2Vec2 d = b2Add( joint->deltaCenter, b2Sub( rB, rA ) );\n\tb2Vec2 axisA = b2RotateVector( joint->frameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\tb2Vec2 perpA = b2LeftPerp( axisA );\n\n\t// perpendicular constraint (keep wheel on line)\n\tfloat s1 = b2Cross( b2Add( d, rA ), perpA );\n\tfloat s2 = b2Cross( rB, perpA );\n\n\tfloat kp = mA + mB + iA * s1 * s1 + iB * s2 * s2;\n\tjoint->perpMass = kp > 0.0f ? 1.0f / kp : 0.0f;\n\n\t// spring constraint\n\tfloat a1 = b2Cross( b2Add( d, rA ), axisA );\n\tfloat a2 = b2Cross( rB, axisA );\n\n\tfloat ka = mA + mB + iA * a1 * a1 + iB * a2 * a2;\n\tjoint->axialMass = ka > 0.0f ? 1.0f / ka : 0.0f;\n\n\tjoint->springSoftness = b2MakeSoft( joint->hertz, joint->dampingRatio, context->h );\n\n\tfloat km = iA + iB;\n\tjoint->motorMass = km > 0.0f ? 1.0f / km : 0.0f;\n\n\tif ( context->enableWarmStarting == false )\n\t{\n\t\tjoint->perpImpulse = 0.0f;\n\t\tjoint->springImpulse = 0.0f;\n\t\tjoint->motorImpulse = 0.0f;\n\t\tjoint->lowerImpulse = 0.0f;\n\t\tjoint->upperImpulse = 0.0f;\n\t}\n}\n\nvoid b2WarmStartWheelJoint( b2JointSim* base, b2StepContext* context )\n{\n\tB2_ASSERT( base->type == b2_wheelJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2WheelJoint* joint = &base->wheelJoint;\n\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\tb2Vec2 d = b2Add( b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), joint->deltaCenter ), b2Sub( rB, rA ) );\n\tb2Vec2 axisA = b2RotateVector( joint->frameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\taxisA = b2RotateVector( stateA->deltaRotation, axisA );\n\tb2Vec2 perpA = b2LeftPerp( axisA );\n\n\tfloat a1 = b2Cross( b2Add( d, rA ), axisA );\n\tfloat a2 = b2Cross( rB, axisA );\n\tfloat s1 = b2Cross( b2Add( d, rA ), perpA );\n\tfloat s2 = b2Cross( rB, perpA );\n\n\tfloat axialImpulse = joint->springImpulse + joint->lowerImpulse - joint->upperImpulse;\n\n\tb2Vec2 P = b2Add( b2MulSV( axialImpulse, axisA ), b2MulSV( joint->perpImpulse, perpA ) );\n\tfloat LA = axialImpulse * a1 + joint->perpImpulse * s1 + joint->motorImpulse;\n\tfloat LB = axialImpulse * a2 + joint->perpImpulse * s2 + joint->motorImpulse;\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, P );\n\t\tstateA->angularVelocity -= iA * LA;\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, P );\n\t\tstateB->angularVelocity += iB * LB;\n\t}\n}\n\nvoid b2SolveWheelJoint( b2JointSim* base, b2StepContext* context, bool useBias )\n{\n\tB2_ASSERT( base->type == b2_wheelJoint );\n\n\tfloat mA = base->invMassA;\n\tfloat mB = base->invMassB;\n\tfloat iA = base->invIA;\n\tfloat iB = base->invIB;\n\n\t// dummy state for static bodies\n\tb2BodyState dummyState = b2_identityBodyState;\n\n\tb2WheelJoint* joint = &base->wheelJoint;\n\n\tb2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA;\n\tb2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB;\n\n\tb2Vec2 vA = stateA->linearVelocity;\n\tfloat wA = stateA->angularVelocity;\n\tb2Vec2 vB = stateB->linearVelocity;\n\tfloat wB = stateB->angularVelocity;\n\n\tbool fixedRotation = ( iA + iB == 0.0f );\n\n\t// current anchors\n\tb2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->frameA.p );\n\tb2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->frameB.p );\n\n\tb2Vec2 d = b2Add( b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), joint->deltaCenter ), b2Sub( rB, rA ) );\n\tb2Vec2 axisA = b2RotateVector( joint->frameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\taxisA = b2RotateVector( stateA->deltaRotation, axisA );\n\tfloat translation = b2Dot( axisA, d );\n\n\tfloat a1 = b2Cross( b2Add( d, rA ), axisA );\n\tfloat a2 = b2Cross( rB, axisA );\n\n\t// motor constraint\n\tif ( joint->enableMotor && fixedRotation == false )\n\t{\n\t\tfloat Cdot = wB - wA - joint->motorSpeed;\n\t\tfloat impulse = -joint->motorMass * Cdot;\n\t\tfloat oldImpulse = joint->motorImpulse;\n\t\tfloat maxImpulse = context->h * joint->maxMotorTorque;\n\t\tjoint->motorImpulse = b2ClampFloat( joint->motorImpulse + impulse, -maxImpulse, maxImpulse );\n\t\timpulse = joint->motorImpulse - oldImpulse;\n\n\t\twA -= iA * impulse;\n\t\twB += iB * impulse;\n\t}\n\n\t// spring constraint\n\tif ( joint->enableSpring )\n\t{\n\t\t// This is a real spring and should be applied even during relax\n\t\tfloat C = translation;\n\t\tfloat bias = joint->springSoftness.biasRate * C;\n\t\tfloat massScale = joint->springSoftness.massScale;\n\t\tfloat impulseScale = joint->springSoftness.impulseScale;\n\n\t\tfloat Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA;\n\t\tfloat impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->springImpulse;\n\t\tjoint->springImpulse += impulse;\n\n\t\tb2Vec2 P = b2MulSV( impulse, axisA );\n\t\tfloat LA = impulse * a1;\n\t\tfloat LB = impulse * a2;\n\n\t\tvA = b2MulSub( vA, mA, P );\n\t\twA -= iA * LA;\n\t\tvB = b2MulAdd( vB, mB, P );\n\t\twB += iB * LB;\n\t}\n\n\tif ( joint->enableLimit )\n\t{\n\t\t// Lower limit\n\t\t{\n\t\t\tfloat C = translation - joint->lowerTranslation;\n\t\t\tfloat bias = 0.0f;\n\t\t\tfloat massScale = 1.0f;\n\t\t\tfloat impulseScale = 0.0f;\n\n\t\t\tif ( C > 0.0f )\n\t\t\t{\n\t\t\t\t// speculation\n\t\t\t\tbias = C * context->inv_h;\n\t\t\t}\n\t\t\telse if ( useBias )\n\t\t\t{\n\t\t\t\tbias = base->constraintSoftness.biasRate * C;\n\t\t\t\tmassScale = base->constraintSoftness.massScale;\n\t\t\t\timpulseScale = base->constraintSoftness.impulseScale;\n\t\t\t}\n\n\t\t\tfloat Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA;\n\t\t\tfloat impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->lowerImpulse;\n\t\t\tfloat oldImpulse = joint->lowerImpulse;\n\t\t\tjoint->lowerImpulse = b2MaxFloat( oldImpulse + impulse, 0.0f );\n\t\t\timpulse = joint->lowerImpulse - oldImpulse;\n\n\t\t\tb2Vec2 P = b2MulSV( impulse, axisA );\n\t\t\tfloat LA = impulse * a1;\n\t\t\tfloat LB = impulse * a2;\n\n\t\t\tvA = b2MulSub( vA, mA, P );\n\t\t\twA -= iA * LA;\n\t\t\tvB = b2MulAdd( vB, mB, P );\n\t\t\twB += iB * LB;\n\t\t}\n\n\t\t// Upper limit\n\t\t// Note: signs are flipped to keep C positive when the constraint is satisfied.\n\t\t// This also keeps the impulse positive when the limit is active.\n\t\t{\n\t\t\t// sign flipped\n\t\t\tfloat C = joint->upperTranslation - translation;\n\t\t\tfloat bias = 0.0f;\n\t\t\tfloat massScale = 1.0f;\n\t\t\tfloat impulseScale = 0.0f;\n\n\t\t\tif ( C > 0.0f )\n\t\t\t{\n\t\t\t\t// speculation\n\t\t\t\tbias = C * context->inv_h;\n\t\t\t}\n\t\t\telse if ( useBias )\n\t\t\t{\n\t\t\t\tbias = base->constraintSoftness.biasRate * C;\n\t\t\t\tmassScale = base->constraintSoftness.massScale;\n\t\t\t\timpulseScale = base->constraintSoftness.impulseScale;\n\t\t\t}\n\n\t\t\t// sign flipped on Cdot\n\t\t\tfloat Cdot = b2Dot( axisA, b2Sub( vA, vB ) ) + a1 * wA - a2 * wB;\n\t\t\tfloat impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->upperImpulse;\n\t\t\tfloat oldImpulse = joint->upperImpulse;\n\t\t\tjoint->upperImpulse = b2MaxFloat( oldImpulse + impulse, 0.0f );\n\t\t\timpulse = joint->upperImpulse - oldImpulse;\n\n\t\t\tb2Vec2 P = b2MulSV( impulse, axisA );\n\t\t\tfloat LA = impulse * a1;\n\t\t\tfloat LB = impulse * a2;\n\n\t\t\t// sign flipped on applied impulse\n\t\t\tvA = b2MulAdd( vA, mA, P );\n\t\t\twA += iA * LA;\n\t\t\tvB = b2MulSub( vB, mB, P );\n\t\t\twB -= iB * LB;\n\t\t}\n\t}\n\n\t// point to line constraint\n\t{\n\t\tb2Vec2 perpA = b2LeftPerp( axisA );\n\n\t\tfloat bias = 0.0f;\n\t\tfloat massScale = 1.0f;\n\t\tfloat impulseScale = 0.0f;\n\t\tif ( useBias )\n\t\t{\n\t\t\tfloat C = b2Dot( perpA, d );\n\t\t\tbias = base->constraintSoftness.biasRate * C;\n\t\t\tmassScale = base->constraintSoftness.massScale;\n\t\t\timpulseScale = base->constraintSoftness.impulseScale;\n\t\t}\n\n\t\tfloat s1 = b2Cross( b2Add( d, rA ), perpA );\n\t\tfloat s2 = b2Cross( rB, perpA );\n\t\tfloat Cdot = b2Dot( perpA, b2Sub( vB, vA ) ) + s2 * wB - s1 * wA;\n\n\t\tfloat impulse = -massScale * joint->perpMass * ( Cdot + bias ) - impulseScale * joint->perpImpulse;\n\t\tjoint->perpImpulse += impulse;\n\n\t\tb2Vec2 P = b2MulSV( impulse, perpA );\n\t\tfloat LA = impulse * s1;\n\t\tfloat LB = impulse * s2;\n\n\t\tvA = b2MulSub( vA, mA, P );\n\t\twA -= iA * LA;\n\t\tvB = b2MulAdd( vB, mB, P );\n\t\twB += iB * LB;\n\t}\n\n\tif ( stateA->flags & b2_dynamicFlag )\n\t{\n\t\tstateA->linearVelocity = vA;\n\t\tstateA->angularVelocity = wA;\n\t}\n\n\tif ( stateB->flags & b2_dynamicFlag )\n\t{\n\t\tstateB->linearVelocity = vB;\n\t\tstateB->angularVelocity = wB;\n\t}\n}\n\n#if 0\nvoid b2WheelJoint_Dump()\n{\n\tint32 indexA = joint->bodyA->joint->islandIndex;\n\tint32 indexB = joint->bodyB->joint->islandIndex;\n\n\tb2Dump(\"  b2WheelJointDef jd;\\n\");\n\tb2Dump(\"  jd.bodyA = sims[%d];\\n\", indexA);\n\tb2Dump(\"  jd.bodyB = sims[%d];\\n\", indexB);\n\tb2Dump(\"  jd.collideConnected = bool(%d);\\n\", joint->collideConnected);\n\tb2Dump(\"  jd.localAnchorA.Set(%.9g, %.9g);\\n\", joint->localAnchorA.x, joint->localAnchorA.y);\n\tb2Dump(\"  jd.localAnchorB.Set(%.9g, %.9g);\\n\", joint->localAnchorB.x, joint->localAnchorB.y);\n\tb2Dump(\"  jd.referenceAngle = %.9g;\\n\", joint->referenceAngle);\n\tb2Dump(\"  jd.enableLimit = bool(%d);\\n\", joint->enableLimit);\n\tb2Dump(\"  jd.lowerAngle = %.9g;\\n\", joint->lowerAngle);\n\tb2Dump(\"  jd.upperAngle = %.9g;\\n\", joint->upperAngle);\n\tb2Dump(\"  jd.enableMotor = bool(%d);\\n\", joint->enableMotor);\n\tb2Dump(\"  jd.motorSpeed = %.9g;\\n\", joint->motorSpeed);\n\tb2Dump(\"  jd.maxMotorTorque = %.9g;\\n\", joint->maxMotorTorque);\n\tb2Dump(\"  joints[%d] = joint->world->CreateJoint(&jd);\\n\", joint->index);\n}\n#endif\n\nvoid b2DrawWheelJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, float drawScale )\n{\n\tB2_ASSERT( base->type == b2_wheelJoint );\n\n\tb2WheelJoint* joint = &base->wheelJoint;\n\n\tb2Transform frameA = b2MulTransforms( transformA, base->localFrameA );\n\tb2Transform frameB = b2MulTransforms( transformB, base->localFrameB );\n\tb2Vec2 axisA = b2RotateVector( frameA.q, (b2Vec2){ 1.0f, 0.0f } );\n\n\tb2HexColor c1 = b2_colorGray;\n\tb2HexColor c2 = b2_colorGreen;\n\tb2HexColor c3 = b2_colorRed;\n\tb2HexColor c4 = b2_colorDimGray;\n\tb2HexColor c5 = b2_colorBlue;\n\n\tdraw->DrawLineFcn( frameA.p, frameB.p, c5, draw->context );\n\n\tif ( joint->enableLimit )\n\t{\n\t\tb2Vec2 lower = b2MulAdd( frameA.p, joint->lowerTranslation, axisA );\n\t\tb2Vec2 upper = b2MulAdd( frameA.p, joint->upperTranslation, axisA );\n\t\tb2Vec2 perp = b2LeftPerp( axisA );\n\t\tdraw->DrawLineFcn( lower, upper, c1, draw->context );\n\t\tdraw->DrawLineFcn( b2MulSub( lower, 0.1f * drawScale, perp ), b2MulAdd( lower, 0.1f * drawScale, perp ), c2,\n\t\t\t\t\t\t\t  draw->context );\n\t\tdraw->DrawLineFcn( b2MulSub( upper, 0.1f * drawScale, perp ), b2MulAdd( upper, 0.1f * drawScale, perp ), c3,\n\t\t\t\t\t\t\t  draw->context );\n\t}\n\telse\n\t{\n\t\tdraw->DrawLineFcn( b2MulSub( frameA.p, 1.0f, axisA ), b2MulAdd( frameA.p, 1.0f, axisA ), c1, draw->context );\n\t}\n\n\tdraw->DrawPointFcn( frameA.p, 5.0f, c1, draw->context );\n\tdraw->DrawPointFcn( frameB.p, 5.0f, c4, draw->context );\n}\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "# Box2D unit test app\n\nset(BOX2D_TEST_FILES\n    main.c\n    test_bitset.c\n    test_collision.c\n    test_determinism.c\n    test_distance.c\n\ttest_dynamic_tree.c\n    test_id.c\n    test_macros.h\n    test_math.c\n    test_shape.c\n    test_table.c\n    test_world.c\n)\n\nadd_executable(test ${BOX2D_TEST_FILES})\n\nset_target_properties(test PROPERTIES\n    C_STANDARD 17\n    C_STANDARD_REQUIRED YES\n    C_EXTENSIONS NO\n)\n\nif (BOX2D_COMPILE_WARNING_AS_ERROR)\n\tset_target_properties(test PROPERTIES COMPILE_WARNING_AS_ERROR ON)\nendif()\n\n# Special access to Box2D internals for testing\ntarget_include_directories(test PRIVATE ${CMAKE_SOURCE_DIR}/src)\n\ntarget_link_libraries(test PRIVATE box2d shared enkiTS)\n\nsource_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"\" FILES ${BOX2D_TEST_FILES})\n"
  },
  {
    "path": "test/main.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"test_macros.h\"\n\n#if defined( _MSC_VER )\n\t#include <crtdbg.h>\n\n// int MyAllocHook(int allocType, void* userData, size_t size, int blockType, long requestNumber, const unsigned char* filename,\n//\tint lineNumber)\n//{\n//\tif (size == 16416)\n//\t{\n//\t\tsize += 0;\n//\t}\n//\n//\treturn 1;\n// }\n#endif\n\nextern int BitSetTest( void );\nextern int CollisionTest( void );\nextern int DeterminismTest( void );\nextern int DistanceTest( void );\nextern int DynamicTreeTest( void );\nextern int IdTest( void );\nextern int MathTest( void );\nextern int ShapeTest( void );\nextern int TableTest( void );\nextern int WorldTest( void );\n\nint main( void )\n{\n#if defined( _MSC_VER )\n\t// Enable memory-leak reports\n\n\t// How to break at the leaking allocation, in the watch window enter this variable\n\t// and set it to the allocation number in {}. Do this at the first line in main.\n\t// {,,ucrtbased.dll}_crtBreakAlloc = <allocation number> 3970\n\t// Note:\n\t// Just _crtBreakAlloc in static link\n\t// Tracy Profile server leaks\n\n\t_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE );\n\t_CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );\n\t//_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));\n\t//_CrtSetAllocHook(MyAllocHook);\n\t//_CrtSetBreakAlloc(196);\n#endif\n\n\tprintf( \"Starting Box2D unit tests\\n\" );\n\tprintf( \"======================================\\n\" );\n\n\tRUN_TEST( TableTest );\n\tRUN_TEST( MathTest );\n\tRUN_TEST( BitSetTest );\n\tRUN_TEST( CollisionTest );\n\tRUN_TEST( DeterminismTest );\n\tRUN_TEST( DistanceTest );\n\tRUN_TEST( DynamicTreeTest );\n\tRUN_TEST( IdTest );\n\tRUN_TEST( ShapeTest );\n\tRUN_TEST( WorldTest );\n\n\tprintf( \"======================================\\n\" );\n\tprintf( \"All Box2D tests passed!\\n\" );\n\n#if defined( _MSC_VER )\n\tif ( _CrtDumpMemoryLeaks() )\n\t{\n\t\treturn 1;\n\t}\n#endif\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_bitset.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"bitset.h\"\n#include \"test_macros.h\"\n\n#define COUNT 169\n\nint BitSetTest( void )\n{\n\tb2BitSet bitSet = b2CreateBitSet( COUNT );\n\n\tb2SetBitCountAndClear( &bitSet, COUNT );\n\tbool values[COUNT] = { false };\n\n\tint32_t i1 = 0, i2 = 1;\n\tb2SetBit( &bitSet, i1 );\n\tvalues[i1] = true;\n\n\twhile ( i2 < COUNT )\n\t{\n\t\tb2SetBit( &bitSet, i2 );\n\t\tvalues[i2] = true;\n\t\tint32_t next = i1 + i2;\n\t\ti1 = i2;\n\t\ti2 = next;\n\t}\n\n\tfor ( int32_t i = 0; i < COUNT; ++i )\n\t{\n\t\tbool value = b2GetBit( &bitSet, i );\n\t\tENSURE( value == values[i] );\n\t}\n\n\tb2DestroyBitSet( &bitSet );\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_collision.c",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"aabb.h\"\n#include \"test_macros.h\"\n\n#include \"box2d/math_functions.h\"\n\nstatic int AABBTest( void )\n{\n\tb2AABB a;\n\ta.lowerBound = (b2Vec2){ -1.0f, -1.0f };\n\ta.upperBound = (b2Vec2){ -2.0f, -2.0f };\n\n\tENSURE( b2IsValidAABB( a ) == false );\n\n\ta.upperBound = (b2Vec2){ 1.0f, 1.0f };\n\tENSURE( b2IsValidAABB( a ) == true );\n\n\tb2AABB b = { { 2.0f, 2.0f }, { 4.0f, 4.0f } };\n\tENSURE( b2AABB_Overlaps( a, b ) == false );\n\tENSURE( b2AABB_Contains( a, b ) == false );\n\n\treturn 0;\n}\n\nstatic int AABBRayCastTest( void )\n{\n\t// Test AABB centered at origin with bounds [-1, -1] to [1, 1]\n\tb2AABB aabb = { { -1.0f, -1.0f }, { 1.0f, 1.0f } };\n\n\t// Test 1: Ray hits AABB from left side\n\t{\n\t\tb2Vec2 p1 = { -3.0f, 0.0f };\n\t\tb2Vec2 p2 = { 3.0f, 0.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == true );\n\t\tENSURE_SMALL( output.fraction - 1.0f / 3.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.x + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.x + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.y, FLT_EPSILON );\n\t}\n\n\t// Test 2: Ray hits AABB from right side\n\t{\n\t\tb2Vec2 p1 = { 3.0f, 0.0f };\n\t\tb2Vec2 p2 = { -3.0f, 0.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == true );\n\t\tENSURE_SMALL( output.fraction - 1.0f / 3.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.x - 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.x - 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.y, FLT_EPSILON );\n\t}\n\n\t// Test 3: Ray hits AABB from bottom\n\t{\n\t\tb2Vec2 p1 = { 0.0f, -3.0f };\n\t\tb2Vec2 p2 = { 0.0f, 3.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == true );\n\t\tENSURE_SMALL( output.fraction - 1.0f / 3.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.x, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.x, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.y + 1.0f, FLT_EPSILON );\n\t}\n\n\t// Test 4: Ray hits AABB from top\n\t{\n\t\tb2Vec2 p1 = { 0.0f, 3.0f };\n\t\tb2Vec2 p2 = { 0.0f, -3.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == true );\n\t\tENSURE_SMALL( output.fraction - 1.0f / 3.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.x, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y - 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.x, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.y - 1.0f, FLT_EPSILON );\n\t}\n\n\t// Test 5: Ray misses AABB completely (parallel to x-axis)\n\t{\n\t\tb2Vec2 p1 = { -3.0f, 2.0f };\n\t\tb2Vec2 p2 = { 3.0f, 2.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == false );\n\t}\n\n\t// Test 6: Ray misses AABB completely (parallel to y-axis)\n\t{\n\t\tb2Vec2 p1 = { 2.0f, -3.0f };\n\t\tb2Vec2 p2 = { 2.0f, 3.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == false );\n\t}\n\n\t// Test 7: Ray starts inside AABB\n\t{\n\t\tb2Vec2 p1 = { 0.0f, 0.0f };\n\t\tb2Vec2 p2 = { 2.0f, 0.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == false );\n\t}\n\n\t// Test 8: Ray hits corner of AABB (diagonal ray)\n\t{\n\t\tb2Vec2 p1 = { -2.0f, -2.0f };\n\t\tb2Vec2 p2 = { 2.0f, 2.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == true );\n\t\tENSURE_SMALL( output.fraction - 0.25f, FLT_EPSILON );\n\t\t// Normal should be either (-1, 0) or (0, -1) depending on which edge is hit first\n\t\tENSURE( ( output.normal.x == -1.0f && output.normal.y == 0.0f ) ||\n\t\t\t\t( output.normal.x == 0.0f && output.normal.y == -1.0f ) );\n\t}\n\n\t// Test 9: Ray parallel to AABB edge but outside\n\t{\n\t\tb2Vec2 p1 = { -2.0f, 1.5f };\n\t\tb2Vec2 p2 = { 2.0f, 1.5f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == false );\n\t}\n\n\t// Test 10: Ray parallel to AABB edge and exactly on boundary\n\t{\n\t\tb2Vec2 p1 = { -2.0f, 1.0f };\n\t\tb2Vec2 p2 = { 2.0f, 1.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == true );\n\t\tENSURE_SMALL( output.fraction - 0.25f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.x + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y, FLT_EPSILON );\n\t}\n\n\t// Test 11: Very short ray that doesn't reach AABB\n\t{\n\t\tb2Vec2 p1 = { -3.0f, 0.0f };\n\t\tb2Vec2 p2 = { -2.5f, 0.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == false );\n\t}\n\n\t// Test 12: Zero-length ray (degenerate case)\n\t{\n\t\tb2Vec2 p1 = { 0.0f, 0.0f };\n\t\tb2Vec2 p2 = { 0.0f, 0.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == false );\n\t}\n\n\t// Test 13: Ray hits AABB at exact boundary condition (t = 1.0)\n\t{\n\t\tb2Vec2 p1 = { -2.0f, 0.0f };\n\t\tb2Vec2 p2 = { -1.0f, 0.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( aabb, p1, p2 );\n\n\t\tENSURE( output.hit == true );\n\t\tENSURE_SMALL( output.fraction - 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.x + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y, FLT_EPSILON );\n\t}\n\n\t// Test 14: Different AABB position (not centered at origin)\n\t{\n\t\tb2AABB offsetAABB = { { 2.0f, 3.0f }, { 4.0f, 5.0f } };\n\t\tb2Vec2 p1 = { 0.0f, 4.0f };\n\t\tb2Vec2 p2 = { 6.0f, 4.0f };\n\t\tb2CastOutput output = b2AABB_RayCast( offsetAABB, p1, p2 );\n\n\t\tENSURE( output.hit == true );\n\t\tENSURE_SMALL( output.fraction - 1.0f / 3.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.x + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.x - 2.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.point.y - 4.0f, FLT_EPSILON );\n\t}\n\n\treturn 0;\n}\n\nint CollisionTest( void )\n{\n\tRUN_SUBTEST( AABBTest );\n\tRUN_SUBTEST( AABBRayCastTest );\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_determinism.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"TaskScheduler_c.h\"\n#include \"determinism.h\"\n#include \"test_macros.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/types.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#ifdef BOX2D_PROFILE\n#include <tracy/TracyC.h>\n#else\n#define TracyCFrameMark\n#endif\n\n#define EXPECTED_SLEEP_STEP 300\n#define EXPECTED_HASH 0xD4F49FD3\n\nenum\n{\n\te_maxTasks = 128,\n};\n\ntypedef struct TaskData\n{\n\tb2TaskCallback* box2dTask;\n\tvoid* box2dContext;\n} TaskData;\n\nenkiTaskScheduler* scheduler;\nenkiTaskSet* tasks[e_maxTasks];\nTaskData taskData[e_maxTasks];\nint taskCount;\n\nstatic void ExecuteRangeTask( uint32_t start, uint32_t end, uint32_t threadIndex, void* context )\n{\n\tTaskData* data = context;\n\tdata->box2dTask( start, end, threadIndex, data->box2dContext );\n}\n\nstatic void* EnqueueTask( b2TaskCallback* box2dTask, int itemCount, int minRange, void* box2dContext, void* userContext )\n{\n\tMAYBE_UNUSED( userContext );\n\n\tif ( taskCount < e_maxTasks )\n\t{\n\t\tenkiTaskSet* task = tasks[taskCount];\n\t\tTaskData* data = taskData + taskCount;\n\t\tdata->box2dTask = box2dTask;\n\t\tdata->box2dContext = box2dContext;\n\n\t\tstruct enkiParamsTaskSet params;\n\t\tparams.minRange = minRange;\n\t\tparams.setSize = itemCount;\n\t\tparams.pArgs = data;\n\t\tparams.priority = 0;\n\n\t\tenkiSetParamsTaskSet( task, params );\n\t\tenkiAddTaskSet( scheduler, task );\n\n\t\t++taskCount;\n\n\t\treturn task;\n\t}\n\n\tbox2dTask( 0, itemCount, 0, box2dContext );\n\treturn NULL;\n}\n\nstatic void FinishTask( void* userTask, void* userContext )\n{\n\tMAYBE_UNUSED( userContext );\n\n\tenkiTaskSet* task = userTask;\n\tenkiWaitForTaskSet( scheduler, task );\n}\n\nstatic int SingleMultithreadingTest( int workerCount )\n{\n\tscheduler = enkiNewTaskScheduler();\n\tstruct enkiTaskSchedulerConfig config = enkiGetTaskSchedulerConfig( scheduler );\n\tconfig.numTaskThreadsToCreate = workerCount - 1;\n\tenkiInitTaskSchedulerWithConfig( scheduler, config );\n\n\tfor ( int i = 0; i < e_maxTasks; ++i )\n\t{\n\t\ttasks[i] = enkiCreateTaskSet( scheduler, ExecuteRangeTask );\n\t}\n\n\tb2WorldDef worldDef = b2DefaultWorldDef();\n\tworldDef.enqueueTask = EnqueueTask;\n\tworldDef.finishTask = FinishTask;\n\tworldDef.workerCount = workerCount;\n\n\tb2WorldId worldId = b2CreateWorld( &worldDef );\n\n\tFallingHingeData data = CreateFallingHinges( worldId );\n\n\tfloat timeStep = 1.0f / 60.0f;\n\n\tbool done = false;\n\twhile ( done == false )\n\t{\n\t\tint subStepCount = 4;\n\t\tb2World_Step( worldId, timeStep, subStepCount );\n\t\tTracyCFrameMark;\n\n\t\tdone = UpdateFallingHinges( worldId, &data );\n\t}\n\n\tb2DestroyWorld( worldId );\n\n\tfor ( int i = 0; i < e_maxTasks; ++i )\n\t{\n\t\tenkiDeleteTaskSet( scheduler, tasks[i] );\n\t}\n\n\tenkiDeleteTaskScheduler( scheduler );\n\n\tENSURE( data.sleepStep == EXPECTED_SLEEP_STEP );\n\tENSURE( data.hash == EXPECTED_HASH );\n\n\tDestroyFallingHinges( &data );\n\n\treturn 0;\n}\n\n// Test multithreaded determinism.\nstatic int MultithreadingTest( void )\n{\n\tfor ( int workerCount = 1; workerCount < 6; ++workerCount )\n\t{\n\t\tint result = SingleMultithreadingTest( workerCount );\n\t\tENSURE( result == 0 );\n\t}\n\n\treturn 0;\n}\n\n// Test cross-platform determinism.\nstatic int CrossPlatformTest( void )\n{\n\tb2WorldDef worldDef = b2DefaultWorldDef();\n\tb2WorldId worldId = b2CreateWorld( &worldDef );\n\n\tFallingHingeData data = CreateFallingHinges( worldId );\n\n\tfloat timeStep = 1.0f / 60.0f;\n\n\tbool done = false;\n\twhile ( done == false )\n\t{\n\t\tint subStepCount = 4;\n\t\tb2World_Step( worldId, timeStep, subStepCount );\n\t\tTracyCFrameMark;\n\n\t\tdone = UpdateFallingHinges( worldId, &data );\n\t}\n\n\tENSURE( data.sleepStep == EXPECTED_SLEEP_STEP );\n\tENSURE( data.hash == EXPECTED_HASH );\n\n\tDestroyFallingHinges( &data );\n\n\tb2DestroyWorld( worldId );\n\n\treturn 0;\n}\n\nint DeterminismTest( void )\n{\n\tRUN_SUBTEST( MultithreadingTest );\n\tRUN_SUBTEST( CrossPlatformTest );\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_distance.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"test_macros.h\"\n\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n\nstatic int SegmentDistanceTest( void )\n{\n\tb2Vec2 p1 = { -1.0f, -1.0f };\n\tb2Vec2 q1 = { -1.0f, 1.0f };\n\tb2Vec2 p2 = { 2.0f, 0.0f };\n\tb2Vec2 q2 = { 1.0f, 0.0f };\n\n\tb2SegmentDistanceResult result = b2SegmentDistance( p1, q1, p2, q2 );\n\n\tENSURE_SMALL( result.fraction1 - 0.5f, FLT_EPSILON );\n\tENSURE_SMALL( result.fraction2 - 1.0f, FLT_EPSILON );\n\tENSURE_SMALL( result.closest1.x + 1.0f, FLT_EPSILON );\n\tENSURE_SMALL( result.closest1.y, FLT_EPSILON );\n\tENSURE_SMALL( result.closest2.x - 1.0f, FLT_EPSILON );\n\tENSURE_SMALL( result.closest2.y, FLT_EPSILON );\n\tENSURE_SMALL( result.distanceSquared - 4.0f, FLT_EPSILON );\n\n\treturn 0;\n}\n\nstatic int ShapeDistanceTest( void )\n{\n\tb2Vec2 vas[] = { ( b2Vec2 ){ -1.0f, -1.0f }, ( b2Vec2 ){ 1.0f, -1.0f }, ( b2Vec2 ){ 1.0f, 1.0f }, ( b2Vec2 ){ -1.0f, 1.0f } };\n\n\tb2Vec2 vbs[] = {\n\t\t( b2Vec2 ){ 2.0f, -1.0f },\n\t\t( b2Vec2 ){ 2.0f, 1.0f },\n\t};\n\n\tb2DistanceInput input;\n\tinput.proxyA = b2MakeProxy( vas, ARRAY_COUNT( vas ), 0.0f );\n\tinput.proxyB = b2MakeProxy( vbs, ARRAY_COUNT( vbs ), 0.0f );\n\tinput.transformA = b2Transform_identity;\n\tinput.transformB = b2Transform_identity;\n\tinput.useRadii = false;\n\n\tb2SimplexCache cache = { 0 };\n\tb2DistanceOutput output = b2ShapeDistance(&input,  &cache, NULL, 0 );\n\n\tENSURE_SMALL( output.distance - 1.0f, FLT_EPSILON );\n\n\treturn 0;\n}\n\nstatic int ShapeCastTest( void )\n{\n\tb2Vec2 vas[] = { ( b2Vec2 ){ -1.0f, -1.0f }, ( b2Vec2 ){ 1.0f, -1.0f }, ( b2Vec2 ){ 1.0f, 1.0f }, ( b2Vec2 ){ -1.0f, 1.0f } };\n\n\tb2Vec2 vbs[] = {\n\t\t( b2Vec2 ){ 2.0f, -1.0f },\n\t\t( b2Vec2 ){ 2.0f, 1.0f },\n\t};\n\n\tb2ShapeCastPairInput input;\n\tinput.proxyA = b2MakeProxy( vas, ARRAY_COUNT( vas ), 0.0f );\n\tinput.proxyB = b2MakeProxy( vbs, ARRAY_COUNT( vbs ), 0.0f );\n\tinput.transformA = b2Transform_identity;\n\tinput.transformB = b2Transform_identity;\n\tinput.translationB = ( b2Vec2 ){ -2.0f, 0.0f };\n\tinput.maxFraction = 1.0f;\n\n\tb2CastOutput output = b2ShapeCast( &input );\n\n\tENSURE( output.hit );\n\tENSURE_SMALL( output.fraction - 0.5f, 0.005f );\n\n\treturn 0;\n}\n\nstatic int TimeOfImpactTest( void )\n{\n\tb2Vec2 vas[] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { 1.0f, 1.0f }, { -1.0f, 1.0f } };\n\n\tb2Vec2 vbs[] = {\n\t\t{ 2.0f, -1.0f },\n\t\t{ 2.0f, 1.0f },\n\t};\n\n\tb2TOIInput input;\n\tinput.proxyA = b2MakeProxy( vas, ARRAY_COUNT( vas ), 0.0f );\n\tinput.proxyB = b2MakeProxy( vbs, ARRAY_COUNT( vbs ), 0.0f );\n\tinput.sweepA = ( b2Sweep ){ b2Vec2_zero, b2Vec2_zero, b2Vec2_zero, b2Rot_identity, b2Rot_identity };\n\tinput.sweepB = ( b2Sweep ){ b2Vec2_zero, b2Vec2_zero, ( b2Vec2 ){ -2.0f, 0.0f }, b2Rot_identity, b2Rot_identity };\n\tinput.maxFraction = 1.0f;\n\n\tb2TOIOutput output = b2TimeOfImpact( &input );\n\n\tENSURE( output.state == b2_toiStateHit );\n\tENSURE_SMALL( output.fraction - 0.5f, 0.005f );\n\n\treturn 0;\n}\n\nint DistanceTest( void )\n{\n\tRUN_SUBTEST( SegmentDistanceTest );\n\tRUN_SUBTEST( ShapeDistanceTest );\n\tRUN_SUBTEST( ShapeCastTest );\n\tRUN_SUBTEST( TimeOfImpactTest );\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_dynamic_tree.c",
    "content": "// SPDX-FileCopyrightText: 2025 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"test_macros.h\"\n\n#include \"box2d/collision.h\"\n\nstatic int TreeCreateDestroy( void )\n{\n\tb2AABB a = {\n\t\t.lowerBound = { -1.0f, -1.0f },\n\t\t.upperBound = { 2.0f, 2.0f },\n\t};\n\n\tb2DynamicTree tree = b2DynamicTree_Create();\n\tb2DynamicTree_CreateProxy( &tree, a, 1, 0 );\n\n\tENSURE( tree.nodeCount > 0 );\n\tENSURE( tree.proxyCount == 1 );\n\n\tb2DynamicTree_Destroy( &tree );\n\n\tENSURE( tree.nodeCount == 0 );\n\tENSURE( tree.proxyCount == 0 );\n\n\treturn 0;\n}\n\nfloat RayCastCallbackFcn( const b2RayCastInput* input, int proxyId, uint64_t userData, void* context )\n{\n\t(void)input;\n\t(void)userData;\n\n\tint* proxyHit = context;\n\t*proxyHit = proxyId;\n\treturn 0.0f;\n}\n\nstatic int TreeRayCastTest( void )\n{\n\t// Test AABB centered at origin with bounds [-1, -1] to [1, 1]\n\tb2AABB a = { .lowerBound = { -1.0f, -1.0f }, .upperBound = { 1.0f, 1.0f }, };\n\tb2DynamicTree tree = b2DynamicTree_Create();\n\tint proxyId = b2DynamicTree_CreateProxy( &tree, a, 1, 0 );\n\n\tb2RayCastInput input = {};\n\tinput.maxFraction = 1.0f;\n\n\t// Test 1: Ray hits AABB from left side\n\t{\n\t\tb2Vec2 p1 = { -3.0f, 0.0f };\n\t\tb2Vec2 p2 = { 3.0f, 0.0f };\n\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == proxyId );\n\t}\n\n\t// Test 2: Ray hits AABB from right side\n\t{\n\t\tb2Vec2 p1 = { 3.0f, 0.0f };\n\t\tb2Vec2 p2 = { -3.0f, 0.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == proxyId );\n\t}\n\n\t// Test 3: Ray hits AABB from bottom\n\t{\n\t\tb2Vec2 p1 = { 0.0f, -3.0f };\n\t\tb2Vec2 p2 = { 0.0f, 3.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == proxyId );\n\t}\n\n\t// Test 4: Ray hits AABB from top\n\t{\n\t\tb2Vec2 p1 = { 0.0f, 3.0f };\n\t\tb2Vec2 p2 = { 0.0f, -3.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == proxyId );\n\t}\n\n\t// Test 5: Ray misses AABB completely (parallel to x-axis)\n\t{\n\t\tb2Vec2 p1 = { -3.0f, 2.0f };\n\t\tb2Vec2 p2 = { 3.0f, 2.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == -1 );\n\t}\n\n\t// Test 6: Ray misses AABB completely (parallel to y-axis)\n\t{\n\t\tb2Vec2 p1 = { 2.0f, -3.0f };\n\t\tb2Vec2 p2 = { 2.0f, 3.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == -1 );\n\t}\n\n\t// Test 7: Ray starts inside AABB\n\t{\n\t\tb2Vec2 p1 = { 0.0f, 0.0f };\n\t\tb2Vec2 p2 = { 2.0f, 0.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == proxyId );\n\t}\n\n\t// Test 8: Ray hits corner of AABB (diagonal ray)\n\t{\n\t\tb2Vec2 p1 = { -2.0f, -2.0f };\n\t\tb2Vec2 p2 = { 2.0f, 2.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == proxyId );\n\t}\n\n\t// Test 9: Ray parallel to AABB edge but outside\n\t{\n\t\tb2Vec2 p1 = { -2.0f, 1.5f };\n\t\tb2Vec2 p2 = { 2.0f, 1.5f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == -1 );\n\t}\n\n\t// Test 10: Ray parallel to AABB edge and exactly on boundary\n\t{\n\t\tb2Vec2 p1 = { -2.0f, 1.0f };\n\t\tb2Vec2 p2 = { 2.0f, 1.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == proxyId );\n\t}\n\n\t// Test 11: Very short ray that doesn't reach AABB\n\t{\n\t\tb2Vec2 p1 = { -3.0f, 0.0f };\n\t\tb2Vec2 p2 = { -2.5f, 0.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == -1 );\n\t}\n\n\t// Test 12: Zero-length ray (degenerate case)\n\t{\n\t\tb2Vec2 p1 = { 0.0f, 0.0f };\n\t\tb2Vec2 p2 = { 0.0f, 0.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == proxyId );\n\t}\n\n\t// Test 13: Ray hits AABB at exact boundary condition (t = 1.0)\n\t{\n\t\tb2Vec2 p1 = { -2.0f, 0.0f };\n\t\tb2Vec2 p2 = { -1.0f, 0.0f };\n\t\tinput.origin = p1;\n\t\tinput.translation = b2Sub( p2, p1 );\n\n\t\tint proxyHit = -1;\n\t\tb2DynamicTree_RayCast( &tree, &input, 1, RayCastCallbackFcn, &proxyHit );\n\n\t\tENSURE( proxyHit == proxyId );\n\t}\n\n\tb2DynamicTree_Destroy( &tree );\n\n\treturn 0;\n}\n\nstatic bool QueryCollectCallback( int proxyId, uint64_t userData, void* context )\n{\n\t(void)userData;\n\tint* out = context;\n\tout[proxyId] = 1;\n\treturn true; // continue the query\n}\n\nstatic bool QueryCollectListCallback( int proxyId, uint64_t userData, void* context )\n{\n\t(void)userData;\n\tint* list = context;\n\tint count = list[0];\n\tlist[count + 1] = proxyId;\n\tlist[0] = count + 1;\n\treturn true;\n}\n\nstatic int TreeMultipleProxiesTest( void )\n{\n\tb2DynamicTree tree = b2DynamicTree_Create();\n\n\tb2AABB a1 = { .lowerBound = { -5.0f, -1.0f }, .upperBound = { -3.0f, 1.0f } };\n\tb2AABB a2 = { .lowerBound = { -1.0f, -1.0f }, .upperBound = { 1.0f, 1.0f } };\n\tb2AABB a3 = { .lowerBound = { 3.0f, -1.0f }, .upperBound = { 5.0f, 1.0f } };\n\n\tint id1 = b2DynamicTree_CreateProxy( &tree, a1, 0x1ull, 42 );\n\tint id2 = b2DynamicTree_CreateProxy( &tree, a2, 0x2ull, 43 );\n\tint id3 = b2DynamicTree_CreateProxy( &tree, a3, 0x4ull, 44 );\n\n\tENSURE( b2DynamicTree_GetProxyCount( &tree ) == 3 );\n\n\tENSURE( b2DynamicTree_GetUserData( &tree, id1 ) == 42 );\n\tENSURE( b2DynamicTree_GetUserData( &tree, id2 ) == 43 );\n\tENSURE( b2DynamicTree_GetUserData( &tree, id3 ) == 44 );\n\n\tENSURE( b2DynamicTree_GetCategoryBits( &tree, id1 ) == 0x1ull );\n\tENSURE( b2DynamicTree_GetCategoryBits( &tree, id2 ) == 0x2ull );\n\tENSURE( b2DynamicTree_GetCategoryBits( &tree, id3 ) == 0x4ull );\n\n\tb2DynamicTree_Destroy( &tree );\n\treturn 0;\n}\n\nstatic int TreeQueryTest( void )\n{\n\tb2DynamicTree tree = b2DynamicTree_Create();\n\n\tb2AABB a1 = { .lowerBound = { -5.0f, -1.0f }, .upperBound = { -3.0f, 1.0f } };\n\tb2AABB a2 = { .lowerBound = { -1.0f, -1.0f }, .upperBound = { 1.0f, 1.0f } };\n\tb2AABB a3 = { .lowerBound = { 3.0f, -1.0f }, .upperBound = { 5.0f, 1.0f } };\n\n\tint id1 = b2DynamicTree_CreateProxy( &tree, a1, 0xFFull, 0 );\n\tint id2 = b2DynamicTree_CreateProxy( &tree, a2, 0xFFull, 0 );\n\tint id3 = b2DynamicTree_CreateProxy( &tree, a3, 0xFFull, 0 );\n\n\tb2AABB queryA = { .lowerBound = { -2.0f, -2.0f }, .upperBound = { 2.0f, 2.0f } };\n\n\tint foundFlags[32] = { 0 };\n\tb2TreeStats stats = b2DynamicTree_Query( &tree, queryA, 0xFFFFFFFFull, QueryCollectCallback, foundFlags );\n\n\t// We expect at least the middle proxy to be visited.\n\tENSURE( foundFlags[id2] == 1 );\n\tENSURE( stats.leafVisits >= 1 );\n\n\t// Test QueryAll using list collector\n\tint list[16] = { 0 }; // list[0] holds count, following entries are ids\n\tb2TreeStats allStats = b2DynamicTree_QueryAll( &tree, queryA, QueryCollectListCallback, list );\n\tENSURE( list[0] >= 1 ); // at least one proxy should be collected\n\tENSURE( allStats.leafVisits >= 1 );\n\n\tb2DynamicTree_Destroy( &tree );\n\t(void)id1; (void)id3;\n\treturn 0;\n}\n\nstatic int TreeMoveAndEnlargeTest( void )\n{\n\tb2DynamicTree tree = b2DynamicTree_Create();\n\n\tb2AABB a = { .lowerBound = { 0.0f, 0.0f }, .upperBound = { 1.0f, 1.0f } };\n\tint id = b2DynamicTree_CreateProxy( &tree, a, 0x1ull, 100 );\n\n\t// Move proxy to a new place\n\tb2AABB moved = { .lowerBound = { 10.0f, 10.0f }, .upperBound = { 11.0f, 11.0f } };\n\tb2DynamicTree_MoveProxy( &tree, id, moved );\n\n\tb2AABB got = b2DynamicTree_GetAABB( &tree, id );\n\tENSURE( got.lowerBound.x == moved.lowerBound.x );\n\tENSURE( got.lowerBound.y == moved.lowerBound.y );\n\tENSURE( got.upperBound.x == moved.upperBound.x );\n\tENSURE( got.upperBound.y == moved.upperBound.y );\n\n\t// Now enlarge the proxy\n\tb2AABB enlarge = { .lowerBound = { 9.5f, 9.5f }, .upperBound = { 11.5f, 11.5f } };\n\tb2DynamicTree_EnlargeProxy( &tree, id, enlarge );\n\n\tb2AABB got2 = b2DynamicTree_GetAABB( &tree, id );\n\tENSURE( got2.lowerBound.x <= enlarge.lowerBound.x + 1e-6f );\n\tENSURE( got2.upperBound.x >= enlarge.upperBound.x - 1e-6f );\n\n\tb2DynamicTree_Destroy( &tree );\n\treturn 0;\n}\n\nstatic int TreeRebuildAndValidateTest( void )\n{\n\tb2DynamicTree tree = b2DynamicTree_Create();\n\n\t// Create a number of proxies to make rebuild meaningful\n\tfor ( int i = 0; i < 12; ++i )\n\t{\n\t\tfloat x = (float)i * 2.0f;\n\t\tb2AABB a = { .lowerBound = { x - 0.5f, -0.5f }, .upperBound = { x + 0.5f, 0.5f } };\n\t\tb2DynamicTree_CreateProxy( &tree, a, 0xFFull, (uint64_t)i );\n\t}\n\n\tint sorted = b2DynamicTree_Rebuild( &tree, true );\n\t\n\tENSURE( sorted >= 0 );\n\tENSURE( b2DynamicTree_GetByteCount( &tree ) > 0 );\n\tENSURE( b2DynamicTree_GetHeight( &tree ) > 0 );\n\n\tb2DynamicTree_Destroy( &tree );\n\treturn 0;\n}\n\nstatic int TreeRowHeightTest( void )\n{\n\tb2DynamicTree tree = b2DynamicTree_Create();\n\n\tint columnCount = 200;\n\tfor (int i = 0; i < columnCount; ++i)\n\t{\n\t\tfloat x = 1.0f * i;\n\t\tb2AABB a = { .lowerBound = { x, 0.0f }, .upperBound = { x + 1.0f, 1.0f } };\n\t\tb2DynamicTree_CreateProxy( &tree, a, 1, (uint64_t)i );\n\t}\n\n\tfloat minHeight = log2f((float)columnCount);\n\n\tENSURE( b2DynamicTree_GetHeight( &tree ) < 2.0f * minHeight );\n\n\tb2DynamicTree_Destroy( &tree );\n\treturn 0;\n}\n\nstatic int TreeGridHeightTest( void )\n{\n\tb2DynamicTree tree = b2DynamicTree_Create();\n\n\tint columnCount = 20;\n\tint rowCount = 20;\n\tfor (int i = 0; i < columnCount; ++i)\n\t{\n\t\tfloat x = 1.0f * i;\n\t\tfor (int j = 0; j < rowCount; ++j)\n\t\t{\n\t\t\tfloat y = 1.0f * j;\n\t\t\tb2AABB a = { .lowerBound = { x, y }, .upperBound = { x + 1.0f, y + 1.0f } };\n\t\t\tb2DynamicTree_CreateProxy( &tree, a, 1, (uint64_t)i );\n\t\t}\n\t}\n\n\tfloat minHeight = log2f( (float)(rowCount * columnCount) );\n\n\tENSURE( b2DynamicTree_GetHeight( &tree ) < 2.0f * minHeight );\n\n\tb2DynamicTree_Destroy( &tree );\n\treturn 0;\n}\n\n#define GRID_COUNT 20\n\nstatic int TreeGridMovementTest( void )\n{\n\tb2DynamicTree tree = b2DynamicTree_Create();\n\n\tint proxyIds[GRID_COUNT * GRID_COUNT];\n\tint index = 0;\n\tfor (int i = 0; i < GRID_COUNT; ++i)\n\t{\n\t\tfloat x = 1.0f * i;\n\t\tfor (int j = 0; j < GRID_COUNT; ++j)\n\t\t{\n\t\t\tfloat y = 1.0f * j;\n\t\t\tb2AABB a = { .lowerBound = { x, y }, .upperBound = { x + 1.0f, y + 1.0f } };\n\t\t\tproxyIds[index] = b2DynamicTree_CreateProxy( &tree, a, 1, (uint64_t)i );\n\t\t\tindex += 1;\n\t\t}\n\t}\n\n\tENSURE( index == GRID_COUNT * GRID_COUNT );\n\n\tfloat minHeight = log2f( (float)( GRID_COUNT * GRID_COUNT ) );\n\n\tint height1 = b2DynamicTree_GetHeight( &tree );\n\tENSURE( height1 < 2.0f * minHeight );\n\n\tb2Vec2 offset = {10.0f, 20.0f};\n\tindex = 0;\n\tfor (int i = 0; i < GRID_COUNT; ++i)\n\t{\n\t\tfor (int j = 0; j < GRID_COUNT; ++j)\n\t\t{\n\t\t\tb2AABB a = b2DynamicTree_GetAABB( &tree, proxyIds[index] );\n\t\t\ta.lowerBound = b2Add( a.lowerBound, offset );\n\t\t\ta.upperBound = b2Add( a.upperBound, offset );\n\t\t\tb2DynamicTree_MoveProxy( &tree, proxyIds[index], a );\n\t\t\tindex += 1;\n\t\t}\n\t}\n\n\tint height2 = b2DynamicTree_GetHeight( &tree );\n\tENSURE( height2 < 3.0f * minHeight );\n\n\tb2DynamicTree_Rebuild( &tree, true );\n\n\tint height3 = b2DynamicTree_GetHeight( &tree );\n\tENSURE( height3 < 2.0f * minHeight );\n\n\tb2DynamicTree_Destroy( &tree );\n\treturn 0;\n}\n\nint DynamicTreeTest( void )\n{\n\tRUN_SUBTEST( TreeCreateDestroy );\n\tRUN_SUBTEST( TreeRayCastTest );\n\tRUN_SUBTEST( TreeMultipleProxiesTest );\n\tRUN_SUBTEST( TreeQueryTest );\n\tRUN_SUBTEST( TreeMoveAndEnlargeTest );\n\tRUN_SUBTEST( TreeRebuildAndValidateTest );\n\tRUN_SUBTEST( TreeRowHeightTest );\n\tRUN_SUBTEST( TreeGridHeightTest );\n\tRUN_SUBTEST( TreeGridMovementTest );\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_id.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"test_macros.h\"\n\n#include \"box2d/id.h\"\n\nint IdTest( void )\n{\n\tuint32_t a = 0x01234567;\n\n\t{\n\t\tb2WorldId id = b2LoadWorldId( a );\n\t\tuint32_t b = b2StoreWorldId( id );\n\t\tENSURE( b == a );\n\t}\n\n\tuint64_t x = 0x0123456789ABCDEFull;\n\n\t{\n\t\tb2BodyId id = b2LoadBodyId( x );\n\t\tuint64_t y = b2StoreBodyId( id );\n\t\tENSURE( x == y );\n\t}\n\n\t{\n\t\tb2ShapeId id = b2LoadShapeId( x );\n\t\tuint64_t y = b2StoreShapeId( id );\n\t\tENSURE( x == y );\n\t}\n\n\t{\n\t\tb2ChainId id = b2LoadChainId( x );\n\t\tuint64_t y = b2StoreChainId( id );\n\t\tENSURE( x == y );\n\t}\n\n\t{\n\t\tb2JointId id = b2LoadJointId( x );\n\t\tuint64_t y = b2StoreJointId( id );\n\t\tENSURE( x == y );\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_macros.h",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <assert.h>\n#include <stdbool.h>\n#include <stdio.h>\n\n#define RUN_TEST( T )                                                                                                            \\\n\tdo                                                                                                                           \\\n\t{                                                                                                                            \\\n\t\tint result = T();                                                                                                        \\\n\t\tif ( result == 1 )                                                                                                       \\\n\t\t{                                                                                                                        \\\n\t\t\tprintf( \"test failed: \" #T \"\\n\" );                                                                                   \\\n\t\t\treturn 1;                                                                                                            \\\n\t\t}                                                                                                                        \\\n\t\telse                                                                                                                     \\\n\t\t{                                                                                                                        \\\n\t\t\tprintf( \"test passed: \" #T \"\\n\" );                                                                                   \\\n\t\t}                                                                                                                        \\\n\t}                                                                                                                            \\\n\twhile ( false )\n\n#define RUN_SUBTEST( T )                                                                                                         \\\n\tdo                                                                                                                           \\\n\t{                                                                                                                            \\\n\t\tint result = T();                                                                                                        \\\n\t\tif ( result == 1 )                                                                                                       \\\n\t\t{                                                                                                                        \\\n\t\t\tprintf( \"  subtest failed: \" #T \"\\n\" );                                                                              \\\n\t\t\treturn 1;                                                                                                            \\\n\t\t}                                                                                                                        \\\n\t\telse                                                                                                                     \\\n\t\t{                                                                                                                        \\\n\t\t\tprintf( \"  subtest passed: \" #T \"\\n\" );                                                                              \\\n\t\t}                                                                                                                        \\\n\t}                                                                                                                            \\\n\twhile ( false )\n\n#define ENSURE( C )                                                                                                              \\\n\tdo                                                                                                                           \\\n\t{                                                                                                                            \\\n\t\tif ( ( C ) == false )                                                                                                    \\\n\t\t{                                                                                                                        \\\n\t\t\tprintf( \"condition false: \" #C \"\\n\" );                                                                               \\\n\t\t\tassert( false );                                                                                                     \\\n\t\t\treturn 1;                                                                                                            \\\n\t\t}                                                                                                                        \\\n\t}                                                                                                                            \\\n\twhile ( false )\n\n#define ENSURE_SMALL( C, tol )                                                                                                   \\\n\tdo                                                                                                                           \\\n\t{                                                                                                                            \\\n\t\tif ( ( C ) < -( tol ) || ( tol ) < ( C ) )                                                                               \\\n\t\t{                                                                                                                        \\\n\t\t\tprintf( \"condition false: abs(\" #C \") < %g\\n\", tol );                                                                \\\n\t\t\tassert( false );                                                                                                     \\\n\t\t\treturn 1;                                                                                                            \\\n\t\t}                                                                                                                        \\\n\t}                                                                                                                            \\\n\twhile ( false )\n\n#define ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) )\n\n/// Used to prevent the compiler from warning about unused variables\n#define MAYBE_UNUSED( x ) ( (void)( x ) )\n"
  },
  {
    "path": "test/test_math.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"test_macros.h\"\n\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n#include <stdio.h>\n\n// 0.0023 degrees\n#define ATAN_TOL 0.00004f\n\nint MathTest( void )\n{\n\tfor ( float t = -10.0f; t < 10.0f; t += 0.01f )\n\t{\n\t\tfloat angle = B2_PI * t;\n\t\tb2Rot r = b2MakeRot( angle );\n\t\tfloat c = cosf( angle );\n\t\tfloat s = sinf( angle );\n\n\t\t// The cosine and sine approximations are accurate to about 0.1 degrees (0.002 radians)\n\t\t// printf( \"%g %g\\n\", r.c - c, r.s - s );\n\t\tENSURE_SMALL( r.c - c, 0.002f );\n\t\tENSURE_SMALL( r.s - s, 0.002f );\n\n\t\tfloat xn = b2UnwindAngle( angle );\n\t\tENSURE( -B2_PI <= xn && xn <= B2_PI );\n\n\t\tfloat a = b2Atan2( s, c );\n\t\tENSURE( b2IsValidFloat( a ) );\n\n\t\tfloat diff = b2AbsFloat( a - xn );\n\n\t\t// The two results can be off by 360 degrees (-pi and pi)\n\t\tif ( diff > B2_PI )\n\t\t{\n\t\t\tdiff -= 2.0f * B2_PI;\n\t\t}\n\n\t\t// The approximate atan2 is quite accurate\n\t\tENSURE_SMALL( diff, ATAN_TOL );\n\t}\n\n\tfor ( float y = -1.0f; y <= 1.0f; y += 0.01f )\n\t{\n\t\tfor ( float x = -1.0f; x <= 1.0f; x += 0.01f )\n\t\t{\n\t\t\tfloat a1 = b2Atan2( y, x );\n\t\t\tfloat a2 = atan2f( y, x );\n\t\t\tfloat diff = b2AbsFloat( a1 - a2 );\n\t\t\tENSURE( b2IsValidFloat( a1 ) );\n\t\t\tENSURE_SMALL( diff, ATAN_TOL );\n\t\t}\n\t}\n\n\t{\n\t\tfloat a1 = b2Atan2( 1.0f, 0.0f );\n\t\tfloat a2 = atan2f( 1.0f, 0.0f );\n\t\tfloat diff = b2AbsFloat( a1 - a2 );\n\t\tENSURE( b2IsValidFloat( a1 ) );\n\t\tENSURE_SMALL( diff, ATAN_TOL );\n\t}\n\n\t{\n\t\tfloat a1 = b2Atan2( -1.0f, 0.0f );\n\t\tfloat a2 = atan2f( -1.0f, 0.0f );\n\t\tfloat diff = b2AbsFloat( a1 - a2 );\n\t\tENSURE( b2IsValidFloat( a1 ) );\n\t\tENSURE_SMALL( diff, ATAN_TOL );\n\t}\n\n\t{\n\t\tfloat a1 = b2Atan2( 0.0f, 1.0f );\n\t\tfloat a2 = atan2f( 0.0f, 1.0f );\n\t\tfloat diff = b2AbsFloat( a1 - a2 );\n\t\tENSURE( b2IsValidFloat( a1 ) );\n\t\tENSURE_SMALL( diff, ATAN_TOL );\n\t}\n\n\t{\n\t\tfloat a1 = b2Atan2( 0.0f, -1.0f );\n\t\tfloat a2 = atan2f( 0.0f, -1.0f );\n\t\tfloat diff = b2AbsFloat( a1 - a2 );\n\t\tENSURE( b2IsValidFloat( a1 ) );\n\t\tENSURE_SMALL( diff, ATAN_TOL );\n\t}\n\n\t{\n\t\tfloat a1 = b2Atan2( 0.0f, 0.0f );\n\t\tfloat a2 = atan2f( 0.0f, 0.0f );\n\t\tfloat diff = b2AbsFloat( a1 - a2 );\n\t\tENSURE( b2IsValidFloat( a1 ) );\n\t\tENSURE_SMALL( diff, ATAN_TOL );\n\t}\n\n\tb2Vec2 zero = b2Vec2_zero;\n\tb2Vec2 one = { 1.0f, 1.0f };\n\tb2Vec2 two = { 2.0f, 2.0f };\n\n\tb2Vec2 v = b2Add( one, two );\n\tENSURE( v.x == 3.0f && v.y == 3.0f );\n\n\tv = b2Sub( zero, two );\n\tENSURE( v.x == -2.0f && v.y == -2.0f );\n\n\tv = b2Add( two, two );\n\tENSURE( v.x != 5.0f && v.y != 5.0f );\n\n\tb2Transform transform1 = { { -2.0f, 3.0f }, b2MakeRot( 1.0f ) };\n\tb2Transform transform2 = { { 1.0f, 0.0f }, b2MakeRot( -2.0f ) };\n\n\tb2Transform transform = b2MulTransforms( transform2, transform1 );\n\n\tv = b2TransformPoint( transform2, b2TransformPoint( transform1, two ) );\n\n\tb2Vec2 u = b2TransformPoint( transform, two );\n\n\tENSURE_SMALL( u.x - v.x, 10.0f * FLT_EPSILON );\n\tENSURE_SMALL( u.y - v.y, 10.0f * FLT_EPSILON );\n\n\tv = b2TransformPoint( transform1, two );\n\tv = b2InvTransformPoint( transform1, v );\n\n\tENSURE_SMALL( v.x - two.x, 8.0f * FLT_EPSILON );\n\tENSURE_SMALL( v.y - two.y, 8.0f * FLT_EPSILON );\n\n\tv = b2Normalize(( b2Vec2 ){ 0.2f, -0.5f });\n\tfor ( float y = -1.0f; y <= 1.0f; y += 0.01f )\n\t{\n\t\tfor ( float x = -1.0f; x <= 1.0f; x += 0.01f )\n\t\t{\n\t\t\tif (x == 0.0f && y == 0.0f)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tu = b2Normalize( ( b2Vec2 ){ x, y } );\n\n\t\t\tb2Rot r = b2ComputeRotationBetweenUnitVectors( v, u );\n\n\t\t\tb2Vec2 w = b2RotateVector( r, v );\n\t\t\tENSURE_SMALL( w.x - u.x, 4.0f * FLT_EPSILON );\n\t\t\tENSURE_SMALL( w.y - u.y, 4.0f * FLT_EPSILON );\n\t\t}\n\t}\n\n\t// NLerp of b2Rot has an error of over 4 degrees.\n\t// 2D quaternions should have an error under 1 degree.\n\tb2Rot q1 = b2Rot_identity;\n\tb2Rot q2 = b2MakeRot(0.5f * B2_PI);\n\tint n = 100;\n\tfor (int i = 0; i <= n; ++i)\n\t{\n\t\tfloat alpha = (float)i / (float)n;\n\t\tb2Rot q = b2NLerp(q1, q2, alpha);\n\t\tfloat angle = b2Rot_GetAngle(q);\n\t\tENSURE_SMALL( alpha * 0.5f * B2_PI - angle, 5.0f * B2_PI / 180.0f );\n\t\t//printf(\"angle = [%g %g %g]\\n\", alpha, alpha * 0.5f * B2_PI, angle);\n\t}\n\t\n\t// Test relative angle\n\tfloat baseAngle = 0.75f * B2_PI;\n\tq1 = b2MakeRot(baseAngle);\n\tfor ( float t = -10.0f; t < 10.0f; t += 0.01f )\n\t{\n\t\tfloat angle = B2_PI * t;\n\t\tq2 = b2MakeRot(angle);\n\n\t\tfloat relativeAngle = b2RelativeAngle( q1, q2 );\n\t\tfloat unwoundAngle = b2UnwindAngle( angle - baseAngle );\n\t\tfloat tolerance = 0.1f * B2_PI / 180.0f;\n\t\tENSURE_SMALL( relativeAngle - unwoundAngle, tolerance );\n\t}\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_shape.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"test_macros.h\"\n\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#include <float.h>\n\nstatic b2Capsule capsule = { { -1.0f, 0.0f }, { 1.0f, 0.0f }, 1.0f };\nstatic b2Circle circle = { { 1.0f, 0.0f }, 1.0f };\nstatic b2Polygon box;\nstatic b2Segment segment = { { 0.0f, 1.0f }, { 0.0f, -1.0f } };\n\n#define N 4\n\nstatic int ShapeMassTest( void )\n{\n\t{\n\t\tb2MassData md = b2ComputeCircleMass( &circle, 1.0f );\n\t\tENSURE_SMALL( md.mass - B2_PI, FLT_EPSILON );\n\t\tENSURE( md.center.x == 1.0f && md.center.y == 0.0f );\n\t\tENSURE_SMALL( md.rotationalInertia - 0.5f * B2_PI, FLT_EPSILON );\n\t}\n\n\t{\n\t\tfloat radius = capsule.radius;\n\t\tfloat length = b2Distance( capsule.center1, capsule.center2 );\n\n\t\tb2MassData md = b2ComputeCapsuleMass( &capsule, 1.0f );\n\n\t\t// Box that full contains capsule\n\t\tb2Polygon r = b2MakeBox( radius, radius + 0.5f * length );\n\t\tb2MassData mdr = b2ComputePolygonMass( &r, 1.0f );\n\n\t\t// Approximate capsule using convex hull\n\t\tb2Vec2 points[2 * N];\n\t\tfloat d = B2_PI / ( N - 1.0f );\n\t\tfloat angle = -0.5f * B2_PI;\n\t\tfor ( int i = 0; i < N; ++i )\n\t\t{\n\t\t\tpoints[i].x = 1.0f + radius * cosf( angle );\n\t\t\tpoints[i].y = radius * sinf( angle );\n\t\t\tangle += d;\n\t\t}\n\n\t\tangle = 0.5f * B2_PI;\n\t\tfor ( int i = N; i < 2 * N; ++i )\n\t\t{\n\t\t\tpoints[i].x = -1.0f + radius * cosf( angle );\n\t\t\tpoints[i].y = radius * sinf( angle );\n\t\t\tangle += d;\n\t\t}\n\n\t\tb2Hull hull = b2ComputeHull( points, 2 * N );\n\t\tb2Polygon ac = b2MakePolygon( &hull, 0.0f );\n\t\tb2MassData ma = b2ComputePolygonMass( &ac, 1.0f );\n\n\t\tENSURE( ma.mass < md.mass && md.mass < mdr.mass );\n\t\tENSURE( ma.rotationalInertia < md.rotationalInertia && md.rotationalInertia < mdr.rotationalInertia );\n\t}\n\n\t{\n\t\tb2MassData md = b2ComputePolygonMass( &box, 1.0f );\n\t\tENSURE_SMALL( md.mass - 4.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( md.center.x, FLT_EPSILON );\n\t\tENSURE_SMALL( md.center.y, FLT_EPSILON );\n\t\tENSURE_SMALL( md.rotationalInertia - 8.0f / 3.0f, 2.0f * FLT_EPSILON );\n\t}\n\n\treturn 0;\n}\n\nstatic int ShapeAABBTest( void )\n{\n\t{\n\t\tb2AABB b = b2ComputeCircleAABB( &circle, b2Transform_identity );\n\t\tENSURE_SMALL( b.lowerBound.x, FLT_EPSILON );\n\t\tENSURE_SMALL( b.lowerBound.y + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( b.upperBound.x - 2.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( b.upperBound.y - 1.0f, FLT_EPSILON );\n\t}\n\n\t{\n\t\tb2AABB b = b2ComputePolygonAABB( &box, b2Transform_identity );\n\t\tENSURE_SMALL( b.lowerBound.x + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( b.lowerBound.y + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( b.upperBound.x - 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( b.upperBound.y - 1.0f, FLT_EPSILON );\n\t}\n\n\t{\n\t\tb2AABB b = b2ComputeSegmentAABB( &segment, b2Transform_identity );\n\t\tENSURE_SMALL( b.lowerBound.x, FLT_EPSILON );\n\t\tENSURE_SMALL( b.lowerBound.y + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( b.upperBound.x, FLT_EPSILON );\n\t\tENSURE_SMALL( b.upperBound.y - 1.0f, FLT_EPSILON );\n\t}\n\n\treturn 0;\n}\n\nstatic int PointInShapeTest( void )\n{\n\tb2Vec2 p1 = { 0.5f, 0.5f };\n\tb2Vec2 p2 = { 4.0f, -4.0f };\n\n\t{\n\t\tbool hit;\n\t\thit = b2PointInCircle(&circle,  p1 );\n\t\tENSURE( hit == true );\n\t\thit = b2PointInCircle( &circle, p2 );\n\t\tENSURE( hit == false );\n\t}\n\n\t{\n\t\tbool hit;\n\t\thit = b2PointInPolygon( &box,  p1);\n\t\tENSURE( hit == true );\n\t\thit = b2PointInPolygon(&box,  p2);\n\t\tENSURE( hit == false );\n\t}\n\n\treturn 0;\n}\n\nstatic int RayCastShapeTest( void )\n{\n\tb2RayCastInput input = { { -4.0f, 0.0f }, { 8.0f, 0.0f }, 1.0f };\n\n\t{\n\t\tb2CastOutput output = b2RayCastCircle( &circle, & input );\n\t\tENSURE( output.hit );\n\t\tENSURE_SMALL( output.normal.x + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y, FLT_EPSILON );\n\t\tENSURE_SMALL( output.fraction - 0.5f, FLT_EPSILON );\n\t}\n\n\t{\n\t\tb2CastOutput output = b2RayCastPolygon( &box, & input);\n\t\tENSURE( output.hit );\n\t\tENSURE_SMALL( output.normal.x + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y, FLT_EPSILON );\n\t\tENSURE_SMALL( output.fraction - 3.0f / 8.0f, FLT_EPSILON );\n\t}\n\n\t{\n\t\tb2CastOutput output = b2RayCastSegment( &segment, &input, true );\n\t\tENSURE( output.hit );\n\t\tENSURE_SMALL( output.normal.x + 1.0f, FLT_EPSILON );\n\t\tENSURE_SMALL( output.normal.y, FLT_EPSILON );\n\t\tENSURE_SMALL( output.fraction - 0.5f, FLT_EPSILON );\n\t}\n\n\treturn 0;\n}\n\nint ShapeTest( void )\n{\n\tbox = b2MakeBox( 1.0f, 1.0f );\n\n\tRUN_SUBTEST( ShapeMassTest );\n\tRUN_SUBTEST( ShapeAABBTest );\n\tRUN_SUBTEST( PointInShapeTest );\n\tRUN_SUBTEST( RayCastShapeTest );\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_table.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"atomic.h\"\n#include \"core.h\"\n#include \"ctz.h\"\n#include \"table.h\"\n#include \"test_macros.h\"\n\n#include \"box2d/base.h\"\n\n#define SET_SPAN 317\n#define ITEM_COUNT ( ( SET_SPAN * SET_SPAN - SET_SPAN ) / 2 )\n\nstatic int BasicHashSetTest( void )\n{\n\t// Test basic creation and destruction\n\tb2HashSet set = b2CreateSet( 16 );\n\tENSURE( b2GetSetCount( &set ) == 0 );\n\tENSURE( b2GetSetCapacity( &set ) == 16 );\n\n\tb2DestroySet( &set );\n\tENSURE( set.items == NULL );\n\tENSURE( set.count == 0 );\n\tENSURE( set.capacity == 0 );\n\n\treturn 0;\n}\n\nstatic int HashSetCapacityTest( void )\n{\n\t// Test capacity adjustments - capacity should be power of 2\n\t{\n\t\tb2HashSet set = b2CreateSet( 1 );\n\t\tENSURE( b2GetSetCapacity( &set ) == 16 ); // Minimum capacity\n\t\tb2DestroySet( &set );\n\t}\n\n\t{\n\t\tb2HashSet set = b2CreateSet( 15 );\n\t\tENSURE( b2GetSetCapacity( &set ) == 16 ); // Should round up to 16\n\t\tb2DestroySet( &set );\n\t}\n\n\t{\n\t\tb2HashSet set = b2CreateSet( 32 );\n\t\tENSURE( b2GetSetCapacity( &set ) == 32 ); // Should stay at 32\n\t\tb2DestroySet( &set );\n\t}\n\n\t{\n\t\tb2HashSet set = b2CreateSet( 33 );\n\t\tENSURE( b2GetSetCapacity( &set ) == 64 ); // Should round up to 64\n\t\tb2DestroySet( &set );\n\t}\n\n\treturn 0;\n}\n\nstatic int HashSetAddRemoveTest( void )\n{\n\tb2HashSet set = b2CreateSet( 16 );\n\n\t// Test adding new keys\n\tbool found = b2AddKey( &set, 42 );\n\tENSURE( found == false ); // Should be new\n\tENSURE( b2GetSetCount( &set ) == 1 );\n\n\tfound = b2AddKey( &set, 123 );\n\tENSURE( found == false ); // Should be new\n\tENSURE( b2GetSetCount( &set ) == 2 );\n\n\t// Test adding duplicate key\n\tfound = b2AddKey( &set, 42 );\n\tENSURE( found == true );\t\t\t  // Should already exist\n\tENSURE( b2GetSetCount( &set ) == 2 ); // Count shouldn't change\n\n\t// Test contains\n\tENSURE( b2ContainsKey( &set, 42 ) == true );\n\tENSURE( b2ContainsKey( &set, 123 ) == true );\n\tENSURE( b2ContainsKey( &set, 999 ) == false );\n\n\t// Test removal\n\tbool removed = b2RemoveKey( &set, 42 );\n\tENSURE( removed == true );\n\tENSURE( b2GetSetCount( &set ) == 1 );\n\tENSURE( b2ContainsKey( &set, 42 ) == false );\n\tENSURE( b2ContainsKey( &set, 123 ) == true );\n\n\t// Test removing non-existent key\n\tremoved = b2RemoveKey( &set, 999 );\n\tENSURE( removed == false );\n\tENSURE( b2GetSetCount( &set ) == 1 );\n\n\t// Test removing same key twice\n\tremoved = b2RemoveKey( &set, 42 );\n\tENSURE( removed == false );\n\tENSURE( b2GetSetCount( &set ) == 1 );\n\n\tb2DestroySet( &set );\n\treturn 0;\n}\n\nstatic int HashSetClearTest( void )\n{\n\tb2HashSet set = b2CreateSet( 16 );\n\n\t// Add some keys\n\tb2AddKey( &set, 10 );\n\tb2AddKey( &set, 20 );\n\tb2AddKey( &set, 30 );\n\tENSURE( b2GetSetCount( &set ) == 3 );\n\n\t// Clear the set\n\tb2ClearSet( &set );\n\tENSURE( b2GetSetCount( &set ) == 0 );\n\tENSURE( b2ContainsKey( &set, 10 ) == false );\n\tENSURE( b2ContainsKey( &set, 20 ) == false );\n\tENSURE( b2ContainsKey( &set, 30 ) == false );\n\n\t// Test that we can add keys after clearing\n\tb2AddKey( &set, 40 );\n\tENSURE( b2GetSetCount( &set ) == 1 );\n\tENSURE( b2ContainsKey( &set, 40 ) == true );\n\n\tb2DestroySet( &set );\n\treturn 0;\n}\n\nstatic int HashSetGrowthTest( void )\n{\n\tb2HashSet set = b2CreateSet( 16 );\n\tint initialCapacity = b2GetSetCapacity( &set );\n\n\t// Add enough keys to trigger growth (load factor is 0.5)\n\t// With capacity 16, growth should happen when count reaches 8\n\tfor ( uint64_t i = 0; i < 8; ++i )\n\t{\n\t\tb2AddKey( &set, i + 1);\n\t}\n\n\t// Should have grown\n\tint newCapacity = b2GetSetCapacity( &set );\n\tENSURE( newCapacity >= initialCapacity );\n\tENSURE( b2GetSetCount( &set ) == 8 );\n\n\t// Verify all keys are still present after growth\n\tfor ( uint64_t i = 1; i <= 8; ++i )\n\t{\n\t\tENSURE( b2ContainsKey( &set, i ) == true );\n\t}\n\n\tb2DestroySet( &set );\n\treturn 0;\n}\n\nstatic int HashSetEdgeCasesTest( void )\n{\n\tb2HashSet set = b2CreateSet( 16 );\n\n\t// Test large key values\n\tuint64_t largeKey = 0xFFFFFFFFFFFFFFFFULL - 1; // Max value minus 1 (since 0 is sentinel)\n\tb2AddKey( &set, largeKey );\n\tENSURE( b2ContainsKey( &set, largeKey ) == true );\n\tENSURE( b2GetSetCount( &set ) == 1 );\n\n\t// Test keys that might cause hash collisions\n\tuint64_t key1 = 0x123456789ABCDEFULL;\n\tuint64_t key2 = 0x987654321FEDCBAULL;\n\tb2AddKey( &set, key1 );\n\tb2AddKey( &set, key2 );\n\tENSURE( b2ContainsKey( &set, key1 ) == true );\n\tENSURE( b2ContainsKey( &set, key2 ) == true );\n\n\t// Test pattern that could cause clustering\n\tfor ( uint64_t i = 0x1000; i < 0x1010; ++i )\n\t{\n\t\tb2AddKey( &set, i );\n\t}\n\n\tfor ( uint64_t i = 0x1000; i < 0x1010; ++i )\n\t{\n\t\tENSURE( b2ContainsKey( &set, i ) == true );\n\t}\n\n\tb2DestroySet( &set );\n\treturn 0;\n}\n\nstatic int HashSetRemovalReorganizationTest( void )\n{\n\tb2HashSet set = b2CreateSet( 16 );\n\n\t// Add keys that might cluster together\n\tuint64_t keys[] = { 100, 116, 132, 148, 164 }; // These might hash to similar slots\n\tint keyCount = sizeof( keys ) / sizeof( keys[0] );\n\n\tfor ( int i = 0; i < keyCount; ++i )\n\t{\n\t\tb2AddKey( &set, keys[i] );\n\t}\n\n\t// Verify all keys are present\n\tfor ( int i = 0; i < keyCount; ++i )\n\t{\n\t\tENSURE( b2ContainsKey( &set, keys[i] ) == true );\n\t}\n\n\t// Remove a key from the middle\n\tb2RemoveKey( &set, keys[2] );\n\tENSURE( b2ContainsKey( &set, keys[2] ) == false );\n\n\t// Verify other keys are still present (tests reorganization)\n\tfor ( int i = 0; i < keyCount; ++i )\n\t{\n\t\tif ( i != 2 )\n\t\t{\n\t\t\tENSURE( b2ContainsKey( &set, keys[i] ) == true );\n\t\t}\n\t}\n\n\tb2DestroySet( &set );\n\treturn 0;\n}\n\n#define TEST_SIZE 1000\n\nstatic int HashSetStressTest( void )\n{\n\tb2HashSet set = b2CreateSet( 32 );\n\n\tconst int testSize = TEST_SIZE;\n\tuint64_t keys[TEST_SIZE];\n\n\t// Generate test keys\n\tfor ( int i = 0; i < testSize; ++i )\n\t{\n\t\tkeys[i] = (uint64_t)( i * 7 + 13 ); // Some pattern to avoid zero\n\t}\n\n\t// Add all keys\n\tfor ( int i = 0; i < testSize; ++i )\n\t{\n\t\tbool found = b2AddKey( &set, keys[i] );\n\t\tENSURE( found == false );\n\t}\n\n\tENSURE( b2GetSetCount( &set ) == testSize );\n\n\t// Verify all keys are present\n\tfor ( int i = 0; i < testSize; ++i )\n\t{\n\t\tENSURE( b2ContainsKey( &set, keys[i] ) == true );\n\t}\n\n\t// Remove every other key\n\tint removedCount = 0;\n\tfor ( int i = 0; i < testSize; i += 2 )\n\t{\n\t\tbool removed = b2RemoveKey( &set, keys[i] );\n\t\tENSURE( removed == true );\n\t\tremovedCount++;\n\t}\n\n\tENSURE( b2GetSetCount( &set ) == testSize - removedCount );\n\n\t// Verify remaining keys are still present\n\tfor ( int i = 0; i < testSize; ++i )\n\t{\n\t\tbool shouldBePresent = ( i % 2 == 1 );\n\t\tENSURE( b2ContainsKey( &set, keys[i] ) == shouldBePresent );\n\t}\n\n\tb2DestroySet( &set );\n\treturn 0;\n}\n\nstatic int HashSetShapePairKeyTest( void )\n{\n\tb2HashSet set = b2CreateSet( 16 );\n\n\t// Test the B2_SHAPE_PAIR_KEY macro\n\tuint64_t key1 = B2_SHAPE_PAIR_KEY( 5, 10 );\n\tuint64_t key2 = B2_SHAPE_PAIR_KEY( 10, 5 ); // Should be same as key1\n\tENSURE( key1 == key2 );\n\n\tb2AddKey( &set, key1 );\n\tENSURE( b2ContainsKey( &set, key1 ) == true );\n\tENSURE( b2ContainsKey( &set, key2 ) == true ); // Should find same key\n\n\t// Test different pairs\n\tuint64_t key3 = B2_SHAPE_PAIR_KEY( 1, 2 );\n\tuint64_t key4 = B2_SHAPE_PAIR_KEY( 2, 3 );\n\tENSURE( key3 != key4 );\n\n\tb2AddKey( &set, key3 );\n\tb2AddKey( &set, key4 );\n\tENSURE( b2GetSetCount( &set ) == 3 );\n\n\tb2DestroySet( &set );\n\treturn 0;\n}\n\nstatic int HashSetBytesTest( void )\n{\n\tb2HashSet set = b2CreateSet( 32 );\n\n\tint bytes = b2GetHashSetBytes( &set );\n\tint expectedBytes = 32 * (int)sizeof( b2SetItem );\n\tENSURE( bytes == expectedBytes );\n\n\t// Add some items and verify bytes calculation doesn't change\n\tb2AddKey( &set, 100 );\n\tb2AddKey( &set, 200 );\n\n\tint bytesAfterAdd = b2GetHashSetBytes( &set );\n\tENSURE( bytesAfterAdd == expectedBytes );\n\n\tb2DestroySet( &set );\n\treturn 0;\n}\n\nstatic int HashSetTest( void )\n{\n\tconst int N = SET_SPAN;\n\tconst uint32_t itemCount = ITEM_COUNT;\n\tbool removed[ITEM_COUNT] = { 0 };\n\n\tfor ( int iter = 0; iter < 1; ++iter )\n\t{\n\t\tb2HashSet set = b2CreateSet( 16 );\n\n\t\t// Fill set\n\t\tfor ( int i = 0; i < N; ++i )\n\t\t{\n\t\t\tfor ( int j = i + 1; j < N; ++j )\n\t\t\t{\n\t\t\t\tuint64_t key = B2_SHAPE_PAIR_KEY( i, j );\n\t\t\t\tbool found = b2AddKey( &set, key );\n\t\t\t\tENSURE( found == false );\n\t\t\t}\n\t\t}\n\n\t\tENSURE( b2GetSetCount( &set ) == itemCount );\n\n\t\t// Remove a portion of the set\n\t\tint k = 0;\n\t\tuint32_t removeCount = 0;\n\t\tfor ( int i = 0; i < N; ++i )\n\t\t{\n\t\t\tfor ( int j = i + 1; j < N; ++j )\n\t\t\t{\n\t\t\t\tif ( j == i + 1 )\n\t\t\t\t{\n\t\t\t\t\tuint64_t key = B2_SHAPE_PAIR_KEY( i, j );\n\t\t\t\t\tint size1 = b2GetSetCount( &set );\n\t\t\t\t\tbool found = b2RemoveKey( &set, key );\n\t\t\t\t\tENSURE( found );\n\t\t\t\t\tint size2 = b2GetSetCount( &set );\n\t\t\t\t\tENSURE( size2 == size1 - 1 );\n\t\t\t\t\tremoved[k++] = true;\n\t\t\t\t\tremoveCount += 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tremoved[k++] = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tENSURE( b2GetSetCount( &set ) == ( itemCount - removeCount ) );\n\n#if B2_SNOOP_TABLE_COUNTERS\n\t\textern b2AtomicInt b2_probeCount;\n\t\tb2AtomicStoreInt( &b2_probeCount, 0 );\n#endif\n\n\t\t// Test key search\n\t\t// ~5ns per search on an AMD 7950x\n\t\tuint64_t ticks = b2GetTicks();\n\n\t\tk = 0;\n\t\tfor ( int i = 0; i < N; ++i )\n\t\t{\n\t\t\tfor ( int j = i + 1; j < N; ++j )\n\t\t\t{\n\t\t\t\tuint64_t key = B2_SHAPE_PAIR_KEY( j, i );\n\t\t\t\tbool found = b2ContainsKey( &set, key );\n\t\t\t\tENSURE( found || removed[k] );\n\t\t\t\tk += 1;\n\t\t\t}\n\t\t}\n\n\t\t// uint64_t ticks = b2GetTicks(&timer);\n\t\t// printf(\"set ticks = %llu\\n\", ticks);\n\n\t\tfloat ms = b2GetMilliseconds( ticks );\n\t\tprintf( \"set: count = %d, b2ContainsKey = %.5f ms, ave = %.5f us\\n\", itemCount, ms, 1000.0f * ms / itemCount );\n\n#if B2_SNOOP_TABLE_COUNTERS\n\t\tint probeCount = b2AtomicLoadInt( &b2_probeCount );\n\t\tfloat aveProbeCount = (float)probeCount / (float)itemCount;\n\t\tprintf( \"item count = %d, probe count = %d, ave probe count %.2f\\n\", itemCount, probeCount, aveProbeCount );\n#endif\n\n\t\t// Remove all keys from set\n\t\tfor ( int i = 0; i < N; ++i )\n\t\t{\n\t\t\tfor ( int j = i + 1; j < N; ++j )\n\t\t\t{\n\t\t\t\tuint64_t key = B2_SHAPE_PAIR_KEY( i, j );\n\t\t\t\tb2RemoveKey( &set, key );\n\t\t\t}\n\t\t}\n\n\t\tENSURE( b2GetSetCount( &set ) == 0 );\n\n\t\tb2DestroySet( &set );\n\t}\n\n\treturn 0;\n}\n\nint TableTest( void )\n{\n\t// Test helper functions first\n\tint power = b2BoundingPowerOf2( 3008 );\n\tENSURE( power == 12 );\n\n\tint nextPowerOf2 = b2RoundUpPowerOf2( 3008 );\n\tENSURE( nextPowerOf2 == ( 1 << power ) );\n\n\t// Run all hash set tests\n\tRUN_SUBTEST( BasicHashSetTest );\n\tRUN_SUBTEST( HashSetCapacityTest );\n\tRUN_SUBTEST( HashSetAddRemoveTest );\n\tRUN_SUBTEST( HashSetClearTest );\n\tRUN_SUBTEST( HashSetGrowthTest );\n\tRUN_SUBTEST( HashSetEdgeCasesTest );\n\tRUN_SUBTEST( HashSetRemovalReorganizationTest );\n\tRUN_SUBTEST( HashSetStressTest );\n\tRUN_SUBTEST( HashSetShapePairKeyTest );\n\tRUN_SUBTEST( HashSetBytesTest );\n\tRUN_SUBTEST( HashSetTest );\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/test_world.c",
    "content": "// SPDX-FileCopyrightText: 2023 Erin Catto\n// SPDX-License-Identifier: MIT\n\n#include \"constants.h\"\n#include \"core.h\"\n#include \"test_macros.h\"\n\n#include \"box2d/box2d.h\"\n#include \"box2d/collision.h\"\n#include \"box2d/math_functions.h\"\n\n#include <stdio.h>\n\n// This is a simple example of building and running a simulation\n// using Box2D. Here we create a large ground box and a small dynamic\n// box.\n// There are no graphics for this example. Box2D is meant to be used\n// with your rendering engine in your game engine.\nint HelloWorld( void )\n{\n\t// Construct a world object, which will hold and simulate the rigid bodies.\n\tb2WorldDef worldDef = b2DefaultWorldDef();\n\tworldDef.gravity = ( b2Vec2 ){ 0.0f, -10.0f };\n\n\tb2WorldId worldId = b2CreateWorld( &worldDef );\n\tENSURE( b2World_IsValid( worldId ) );\n\n\t// Define the ground body.\n\tb2BodyDef groundBodyDef = b2DefaultBodyDef();\n\tgroundBodyDef.position = ( b2Vec2 ){ 0.0f, -10.0f };\n\n\t// Call the body factory which allocates memory for the ground body\n\t// from a pool and creates the ground box shape (also from a pool).\n\t// The body is also added to the world.\n\tb2BodyId groundId = b2CreateBody( worldId, &groundBodyDef );\n\tENSURE( b2Body_IsValid( groundId ) );\n\n\t// Define the ground box shape. The extents are the half-widths of the box.\n\tb2Polygon groundBox = b2MakeBox( 50.0f, 10.0f );\n\n\t// Add the box shape to the ground body.\n\tb2ShapeDef groundShapeDef = b2DefaultShapeDef();\n\tb2CreatePolygonShape( groundId, &groundShapeDef, &groundBox );\n\n\t// Define the dynamic body. We set its position and call the body factory.\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tbodyDef.position = ( b2Vec2 ){ 0.0f, 4.0f };\n\n\tb2BodyId bodyId = b2CreateBody( worldId, &bodyDef );\n\n\t// Define another box shape for our dynamic body.\n\tb2Polygon dynamicBox = b2MakeBox( 1.0f, 1.0f );\n\n\t// Define the dynamic body shape\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\n\t// Set the box density to be non-zero, so it will be dynamic.\n\tshapeDef.density = 1.0f;\n\n\t// Override the default friction.\n\tshapeDef.material.friction = 0.3f;\n\n\t// Add the shape to the body.\n\tb2CreatePolygonShape( bodyId, &shapeDef, &dynamicBox );\n\n\t// Prepare for simulation. Typically we use a time step of 1/60 of a\n\t// second (60Hz) and 4 sub-steps. This provides a high quality simulation\n\t// in most game scenarios.\n\tfloat timeStep = 1.0f / 60.0f;\n\tint subStepCount = 4;\n\n\tb2Vec2 position = b2Body_GetPosition( bodyId );\n\tb2Rot rotation = b2Body_GetRotation( bodyId );\n\n\t// This is our little game loop.\n\tfor ( int i = 0; i < 90; ++i )\n\t{\n\t\t// Instruct the world to perform a single step of simulation.\n\t\t// It is generally best to keep the time step and iterations fixed.\n\t\tb2World_Step( worldId, timeStep, subStepCount );\n\n\t\t// Now print the position and angle of the body.\n\t\tposition = b2Body_GetPosition( bodyId );\n\t\trotation = b2Body_GetRotation( bodyId );\n\n\t\t// printf(\"%4.2f %4.2f %4.2f\\n\", position.x, position.y, b2Rot_GetAngle(rotation));\n\t}\n\n\t// When the world destructor is called, all bodies and joints are freed. This can\n\t// create orphaned ids, so be careful about your world management.\n\tb2DestroyWorld( worldId );\n\n\tENSURE( b2AbsFloat( position.x ) < 0.01f );\n\tENSURE( b2AbsFloat( position.y - 1.00f ) < 0.01f );\n\tENSURE( b2AbsFloat( b2Rot_GetAngle( rotation ) ) < 0.01f );\n\n\treturn 0;\n}\n\nint EmptyWorld( void )\n{\n\tb2WorldDef worldDef = b2DefaultWorldDef();\n\tb2WorldId worldId = b2CreateWorld( &worldDef );\n\tENSURE( b2World_IsValid( worldId ) == true );\n\n\tfloat timeStep = 1.0f / 60.0f;\n\tint32_t subStepCount = 1;\n\n\tfor ( int32_t i = 0; i < 60; ++i )\n\t{\n\t\tb2World_Step( worldId, timeStep, subStepCount );\n\t}\n\n\tb2DestroyWorld( worldId );\n\n\tENSURE( b2World_IsValid( worldId ) == false );\n\n\treturn 0;\n}\n\n#define BODY_COUNT 10\nint DestroyAllBodiesWorld( void )\n{\n\tb2WorldDef worldDef = b2DefaultWorldDef();\n\tb2WorldId worldId = b2CreateWorld( &worldDef );\n\tENSURE( b2World_IsValid( worldId ) == true );\n\n\tint count = 0;\n\tbool creating = true;\n\n\tb2BodyId bodyIds[BODY_COUNT];\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tb2Polygon square = b2MakeSquare( 0.5f );\n\n\tfor ( int32_t i = 0; i < 2 * BODY_COUNT + 10; ++i )\n\t{\n\t\tif ( creating )\n\t\t{\n\t\t\tif ( count < BODY_COUNT )\n\t\t\t{\n\t\t\t\tbodyIds[count] = b2CreateBody( worldId, &bodyDef );\n\n\t\t\t\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\t\t\t\tb2CreatePolygonShape( bodyIds[count], &shapeDef, &square );\n\t\t\t\tcount += 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcreating = false;\n\t\t\t}\n\t\t}\n\t\telse if ( count > 0 )\n\t\t{\n\t\t\tb2DestroyBody( bodyIds[count - 1] );\n\t\t\tbodyIds[count - 1] = b2_nullBodyId;\n\t\t\tcount -= 1;\n\t\t}\n\n\t\tb2World_Step( worldId, 1.0f / 60.0f, 3 );\n\t}\n\n\tb2Counters counters = b2World_GetCounters( worldId );\n\tENSURE( counters.bodyCount == 0 );\n\n\tb2DestroyWorld( worldId );\n\n\tENSURE( b2World_IsValid( worldId ) == false );\n\n\treturn 0;\n}\n\nstatic int TestIsValid( void )\n{\n\tb2WorldDef worldDef = b2DefaultWorldDef();\n\tb2WorldId worldId = b2CreateWorld( &worldDef );\n\tENSURE( b2World_IsValid( worldId ) );\n\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\n\tb2BodyId bodyId1 = b2CreateBody( worldId, &bodyDef );\n\tENSURE( b2Body_IsValid( bodyId1 ) == true );\n\n\tb2BodyId bodyId2 = b2CreateBody( worldId, &bodyDef );\n\tENSURE( b2Body_IsValid( bodyId2 ) == true );\n\n\tb2DestroyBody( bodyId1 );\n\tENSURE( b2Body_IsValid( bodyId1 ) == false );\n\n\tb2DestroyBody( bodyId2 );\n\tENSURE( b2Body_IsValid( bodyId2 ) == false );\n\n\tb2DestroyWorld( worldId );\n\n\tENSURE( b2World_IsValid( worldId ) == false );\n\tENSURE( b2Body_IsValid( bodyId2 ) == false );\n\tENSURE( b2Body_IsValid( bodyId1 ) == false );\n\n\treturn 0;\n}\n\n#define WORLD_COUNT ( B2_MAX_WORLDS / 2 )\n\nint TestWorldRecycle( void )\n{\n\t_Static_assert( WORLD_COUNT > 0, \"world count\" );\n\n\tint count = 100;\n\n\tb2WorldId worldIds[WORLD_COUNT];\n\n\tfor ( int i = 0; i < count; ++i )\n\t{\n\t\tb2WorldDef worldDef = b2DefaultWorldDef();\n\t\tfor ( int j = 0; j < WORLD_COUNT; ++j )\n\t\t{\n\t\t\tworldIds[j] = b2CreateWorld( &worldDef );\n\t\t\tENSURE( b2World_IsValid( worldIds[j] ) == true );\n\n\t\t\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\t\t\tb2CreateBody( worldIds[j], &bodyDef );\n\t\t}\n\n\t\tfor ( int j = 0; j < WORLD_COUNT; ++j )\n\t\t{\n\t\t\tfloat timeStep = 1.0f / 60.0f;\n\t\t\tint subStepCount = 1;\n\n\t\t\tfor ( int k = 0; k < 10; ++k )\n\t\t\t{\n\t\t\t\tb2World_Step( worldIds[j], timeStep, subStepCount );\n\t\t\t}\n\t\t}\n\n\t\tfor ( int j = WORLD_COUNT - 1; j >= 0; --j )\n\t\t{\n\t\t\tb2DestroyWorld( worldIds[j] );\n\t\t\tENSURE( b2World_IsValid( worldIds[j] ) == false );\n\t\t\tworldIds[j] = b2_nullWorldId;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nstatic bool CustomFilter( b2ShapeId shapeIdA, b2ShapeId shapeIdB, void* context )\n{\n\t(void)shapeIdA;\n\t(void)shapeIdB;\n\tENSURE( context == NULL );\n\treturn true;\n}\n\nstatic bool PreSolveStatic( b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Vec2 point, b2Vec2 normal, void* context )\n{\n\t(void)shapeIdA;\n\t(void)shapeIdB;\n\t(void)point;\n\t(void)normal;\n\tENSURE( context == NULL );\n\treturn false;\n}\n\n// This test is here to ensure all API functions link correctly.\nint TestWorldCoverage( void )\n{\n\tb2WorldDef worldDef = b2DefaultWorldDef();\n\n\tb2WorldId worldId = b2CreateWorld( &worldDef );\n\tENSURE( b2World_IsValid( worldId ) );\n\n\tb2World_EnableSleeping( worldId, true );\n\tb2World_EnableSleeping( worldId, false );\n\tbool flag = b2World_IsSleepingEnabled( worldId );\n\tENSURE( flag == false );\n\n\tb2World_EnableContinuous( worldId, false );\n\tb2World_EnableContinuous( worldId, true );\n\tflag = b2World_IsContinuousEnabled( worldId );\n\tENSURE( flag == true );\n\n\tb2World_SetRestitutionThreshold( worldId, 0.0f );\n\tb2World_SetRestitutionThreshold( worldId, 2.0f );\n\tfloat value = b2World_GetRestitutionThreshold( worldId );\n\tENSURE( value == 2.0f );\n\n\tb2World_SetHitEventThreshold( worldId, 0.0f );\n\tb2World_SetHitEventThreshold( worldId, 100.0f );\n\tvalue = b2World_GetHitEventThreshold( worldId );\n\tENSURE( value == 100.0f );\n\n\tb2World_SetCustomFilterCallback( worldId, CustomFilter, NULL );\n\tb2World_SetPreSolveCallback( worldId, PreSolveStatic, NULL );\n\n\tb2Vec2 g = { 1.0f, 2.0f };\n\tb2World_SetGravity( worldId, g );\n\tb2Vec2 v = b2World_GetGravity( worldId );\n\tENSURE( v.x == g.x );\n\tENSURE( v.y == g.y );\n\n\tb2ExplosionDef explosionDef = b2DefaultExplosionDef();\n\tb2World_Explode( worldId, &explosionDef );\n\n\tb2World_SetContactTuning( worldId, 10.0f, 2.0f, 4.0f );\n\n\tb2World_SetMaximumLinearSpeed( worldId, 10.0f );\n\tvalue = b2World_GetMaximumLinearSpeed( worldId );\n\tENSURE( value == 10.0f );\n\n\tb2World_EnableWarmStarting( worldId, true );\n\tflag = b2World_IsWarmStartingEnabled( worldId );\n\tENSURE( flag == true );\n\n\tint count = b2World_GetAwakeBodyCount( worldId );\n\tENSURE( count == 0 );\n\n\tb2World_SetUserData( worldId, &value );\n\tvoid* userData = b2World_GetUserData( worldId );\n\tENSURE( userData == &value );\n\n\tb2World_Step( worldId, 1.0f, 1 );\n\n\tb2DestroyWorld( worldId );\n\n\treturn 0;\n}\n\nstatic int TestSensor( void )\n{\n\tb2WorldDef worldDef = b2DefaultWorldDef();\n\tb2WorldId worldId = b2CreateWorld( &worldDef );\n\n\t// Wall from x = 1 to x = 2\n\tb2BodyDef bodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_staticBody;\n\tbodyDef.position.x = 1.5f;\n\tbodyDef.position.y = 11.0f;\n\tb2BodyId wallId = b2CreateBody( worldId, &bodyDef );\n\tb2Polygon box = b2MakeBox( 0.5f, 10.0f );\n\tb2ShapeDef shapeDef = b2DefaultShapeDef();\n\tshapeDef.enableSensorEvents = true;\n\tb2CreatePolygonShape( wallId, &shapeDef, &box );\n\n\t// Bullet fired towards the wall\n\tbodyDef = b2DefaultBodyDef();\n\tbodyDef.type = b2_dynamicBody;\n\tbodyDef.isBullet = true;\n\tbodyDef.gravityScale = 0.0f;\n\tbodyDef.position = (b2Vec2){ 7.39814f, 4.0f };\n\tbodyDef.linearVelocity = (b2Vec2){ -20.0f, 0.0f };\n\tb2BodyId bulletId = b2CreateBody( worldId, &bodyDef );\n\tshapeDef = b2DefaultShapeDef();\n\tshapeDef.isSensor = true;\n\tshapeDef.enableSensorEvents = true;\n\tb2Circle circle = { { 0.0f, 0.0f }, 0.1f };\n\tb2CreateCircleShape( bulletId, &shapeDef, &circle );\n\n\tint beginCount = 0;\n\tint endCount = 0;\n\n\twhile ( true )\n\t{\n\t\tfloat timeStep = 1.0f / 60.0f;\n\t\tint subStepCount = 4;\n\t\tb2World_Step( worldId, timeStep, subStepCount );\n\n\t\tb2Vec2 bulletPos = b2Body_GetPosition( bulletId );\n\t\t//printf( \"Bullet pos: %g %g\\n\", bulletPos.x, bulletPos.y );\n\n\t\tb2SensorEvents events = b2World_GetSensorEvents( worldId );\n\n\t\tif ( events.beginCount > 0 )\n\t\t{\n\t\t\tbeginCount += 1;\n\t\t}\n\n\t\tif ( events.endCount > 0 )\n\t\t{\n\t\t\tendCount += 1;\n\t\t}\n\n\t\tif ( bulletPos.x < -1.0f )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tb2DestroyWorld( worldId );\n\n\tENSURE( beginCount == 1 );\n\tENSURE( endCount == 1 );\n\n\treturn 0;\n}\n\nint WorldTest( void )\n{\n\tRUN_SUBTEST( HelloWorld );\n\tRUN_SUBTEST( EmptyWorld );\n\tRUN_SUBTEST( DestroyAllBodiesWorld );\n\tRUN_SUBTEST( TestIsValid );\n\tRUN_SUBTEST( TestWorldRecycle );\n\tRUN_SUBTEST( TestWorldCoverage );\n\tRUN_SUBTEST( TestSensor );\n\n\treturn 0;\n}\n"
  }
]