diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3041295b..9b9eea7a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -136,6 +136,7 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Prepare Linux if: ${{ runner.os == 'Linux' }} run: > @@ -148,12 +149,15 @@ jobs: libxrandr-dev libxi-dev ninja-build + - name: Prepare macOS if: ${{ runner.os == 'macOS' }} run: brew install ninja + - uses: actions/setup-python@v4 with: python-version: "3.11" + - name: Prepare Python shell: bash run: | @@ -169,6 +173,26 @@ jobs: source venv/bin/activate python -m pip install --upgrade --require-hashes -r "${repo}/python/build_requirements.txt" python -m pip install --upgrade --require-hashes -r "${repo}/python/build_requirements_usd.txt" + + - name: Setup Node.js for WASM + if: ${{ runner.os == 'Linux' }} + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install NPM Dependencies for WASM + if: ${{ runner.os == 'Linux' }} + working-directory: wasm + run: | + npm ci + + - name: Setup Emscripten + if: ${{ runner.os == 'Linux' }} + run: | + git clone https://github.com/emscripten-core/emsdk.git + ./emsdk/emsdk install 4.0.10 + ./emsdk/emsdk activate 4.0.10 + - name: Configure MuJoCo run: > mkdir build && @@ -179,9 +203,11 @@ jobs: -DCMAKE_INSTALL_PREFIX:STRING=${{ matrix.tmpdir }}/mujoco_install -DMUJOCO_BUILD_EXAMPLES:BOOL=OFF ${{ matrix.cmake_args }} + - name: Build MuJoCo working-directory: build run: cmake --build . --config=Release ${{ matrix.cmake_build_args }} + - name: Copy in the correct VC runtime DLLs (workaround for actions/runner-images#10004) if: ${{ runner.os == 'Windows' }} working-directory: build @@ -192,12 +218,15 @@ jobs: -Path "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.*" | Sort -Descending | Select-Object -First 1).FullName ) 'x64\Microsoft.VC143.CRT\*.dll') "bin\Release" + - name: Test MuJoCo working-directory: build run: ctest -C Release --output-on-failure . + - name: Install MuJoCo working-directory: build run: cmake --install . + - name: Copy plugins (POSIX) if: ${{ runner.os != 'Windows' }} working-directory: build @@ -206,6 +235,7 @@ jobs: cp lib/libelasticity.* ${{ matrix.tmpdir }}/mujoco_install/mujoco_plugin && cp lib/libsensor.* ${{ matrix.tmpdir }}/mujoco_install/mujoco_plugin && cp lib/libsdf_plugin.* ${{ matrix.tmpdir }}/mujoco_install/mujoco_plugin + - name: Copy plugins (Windows) if: ${{ runner.os == 'Windows' }} working-directory: build @@ -213,6 +243,7 @@ jobs: cp bin/Release/actuator.dll ${{ matrix.tmpdir }}/mujoco_install/mujoco_plugin && cp bin/Release/elasticity.dll ${{ matrix.tmpdir }}/mujoco_install/mujoco_plugin && cp bin/Release/sensor.dll ${{ matrix.tmpdir }}/mujoco_install/mujoco_plugin + - name: Configure samples working-directory: sample run: > @@ -223,9 +254,11 @@ jobs: -DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF -Dmujoco_ROOT:STRING=${{ matrix.tmpdir }}/mujoco_install ${{ matrix.cmake_args }} + - name: Build samples working-directory: sample/build run: cmake --build . --config=Release ${{ matrix.cmake_build_args }} + - name: Configure simulate working-directory: simulate run: > @@ -236,15 +269,18 @@ jobs: -DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF -Dmujoco_ROOT:STRING=${{ matrix.tmpdir }}/mujoco_install ${{ matrix.cmake_args }} + - name: Build simulate working-directory: simulate/build run: cmake --build . --config=Release ${{ matrix.cmake_build_args }} + - name: Make Python sdist shell: bash working-directory: python run: > source ${{ matrix.tmpdir }}/venv/bin/activate && ./make_sdist.sh + - name: Build Python bindings if: ${{ runner.os != 'Windows' }} shell: bash @@ -255,6 +291,7 @@ jobs: MUJOCO_PLUGIN_PATH="${{ matrix.tmpdir }}/mujoco_install/mujoco_plugin" MUJOCO_CMAKE_ARGS="-DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF ${{ matrix.cmake_args }}" pip wheel -v --no-deps mujoco-*.tar.gz + - name: Install Python bindings if: ${{ runner.os != 'Windows' }} shell: bash @@ -262,6 +299,7 @@ jobs: run: > source ${{ matrix.tmpdir }}/venv/bin/activate && pip install --no-index mujoco-*.whl + - name: Test Python bindings if: ${{ runner.os != 'Windows' }} shell: bash @@ -270,6 +308,19 @@ jobs: run: > source ${{ matrix.tmpdir }}/venv/bin/activate && pytest -v --pyargs mujoco + + - name: Build and Test WASM bindings + if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + source emsdk/emsdk_env.sh + export PATH="$(pwd)/node_modules/.bin:$PATH" + + emcmake cmake -B build_wasm -DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF + cmake --build build_wasm + + npm run test --prefix ./wasm + - name: Package MJX if: ${{ runner.os != 'Windows' }} shell: bash @@ -277,6 +328,7 @@ jobs: run: source ${{ matrix.tmpdir }}/venv/bin/activate && python -m build . + - name: Install MJX if: ${{ runner.os != 'Windows' }} shell: bash @@ -285,6 +337,7 @@ jobs: source ${{ matrix.tmpdir }}/venv/bin/activate && pip install --require-hashes -r requirements.txt && pip install --no-index dist/mujoco_mjx-*.whl + - name: Test MJX if: ${{ runner.os != 'Windows' }} shell: bash @@ -292,6 +345,7 @@ jobs: run: source ${{ matrix.tmpdir }}/venv/bin/activate && pytest -n auto -v -k 'not IntegrationTest' --pyargs mujoco.mjx + - name: Notify team chat shell: bash env: @@ -324,3 +378,4 @@ jobs: -X POST \ -H "Content-Type: application/json" \ --data-raw "${CHATMSG}" + diff --git a/.gitignore b/.gitignore index ba97a7c9..68e22178 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,8 @@ MUJOCO_LOG.TXT # Clang cache .cache/ + +# JavaScript bindings build +wasm/**/dist/ +**/node_modules/ + diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dfee902..4dec5671 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,14 +39,20 @@ enable_language(CXX) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") -option(MUJOCO_BUILD_EXAMPLES "Build samples for MuJoCo" ON) -option(MUJOCO_BUILD_SIMULATE "Build simulate library for MuJoCo" ON) -option(MUJOCO_BUILD_STUDIO "Build studio library for MuJoCo" OFF) -option(MUJOCO_BUILD_TESTS "Build tests for MuJoCo" ON) -option(MUJOCO_TEST_PYTHON_UTIL "Build and test utility libraries for Python bindings" ON) -option(MUJOCO_WITH_USD "Build with OpenUSD" OFF) -option(MUJOCO_USE_FILAMENT "Use filament rendering" OFF) -option(MUJOCO_USE_FILAMENT_VULKAN "Use vulkan backend for filament rendering" OFF) +if(NOT EMSCRIPTEN) + option(MUJOCO_BUILD_EXAMPLES "Build samples for MuJoCo" ON) + option(MUJOCO_BUILD_SIMULATE "Build simulate library for MuJoCo" ON) + option(MUJOCO_BUILD_STUDIO "Build studio library for MuJoCo" OFF) + option(MUJOCO_BUILD_TESTS "Build tests for MuJoCo" ON) + option(MUJOCO_TEST_PYTHON_UTIL "Build and test utility libraries for Python bindings" ON) + option(MUJOCO_WITH_USD "Build with OpenUSD" OFF) + option(MUJOCO_USE_FILAMENT "Use filament rendering" OFF) + option(MUJOCO_USE_FILAMENT_VULKAN "Use vulkan backend for filament rendering" OFF) +endif() + +if(EMSCRIPTEN) + option(MUJOCO_BUILD_TESTS_WASM "Build tests for WASM bindings" ON) +endif() # Option to provide a path to an existing USD build directory or to Houdini HFS directory. set(USD_DIR "" CACHE PATH "Path to an existing USD build directory.") @@ -100,7 +106,13 @@ else() set(MUJOCO_RESOURCE_FILES "") endif() -add_library(mujoco SHARED ${MUJOCO_RESOURCE_FILES}) +# Emscripten does not support SHARED libs +if(NOT EMSCRIPTEN) + add_library(mujoco SHARED ${MUJOCO_RESOURCE_FILES}) +else() + add_library(mujoco STATIC ${MUJOCO_RESOURCE_FILES}) +endif() + target_include_directories( mujoco PUBLIC $ @@ -108,27 +120,36 @@ target_include_directories( PRIVATE src ) -add_subdirectory(plugin/elasticity) -add_subdirectory(plugin/actuator) -add_subdirectory(plugin/sensor) -add_subdirectory(plugin/sdf) +if(NOT EMSCRIPTEN) + add_subdirectory(plugin/elasticity) + add_subdirectory(plugin/actuator) + add_subdirectory(plugin/sensor) + add_subdirectory(plugin/sdf) +endif() add_subdirectory(src/engine) add_subdirectory(src/user) add_subdirectory(src/xml) add_subdirectory(src/thread) -if(MUJOCO_USE_FILAMENT) +if(MUJOCO_USE_FILAMENT AND NOT EMSCRIPTEN) # Note that, by default, the "src/render" and "src/ui" code is added directly # into the "mujoco" target. However, "mujoco::filament" is a separate, # explicit target. Therefore, if you want to use MuJoCo with Filament, you # will need to explicitly add the "mujoco::filament" target as a link # dependency in your project. add_subdirectory(src/experimental/filament) -else() +elseif(NOT EMSCRIPTEN) add_subdirectory(src/render) add_subdirectory(src/ui) endif() +if(EMSCRIPTEN) + add_subdirectory(wasm) + if(MUJOCO_BUILD_TESTS_WASM) + add_subdirectory(wasm/tests) + endif() +endif() + target_compile_definitions(mujoco PRIVATE _GNU_SOURCE CCD_STATIC_DEFINE MUJOCO_DLL_EXPORTS -DMC_IMPLEM_ENABLE) if(MUJOCO_ENABLE_AVX_INTRINSICS) @@ -235,9 +256,14 @@ if(BUILD_TESTING AND MUJOCO_BUILD_TESTS) endif() if(NOT (APPLE AND MUJOCO_BUILD_MACOS_FRAMEWORKS)) + set(MUJOCO_TARGETS mujoco) + if (EMSCRIPTEN) + list(APPEND MUJOCO_TARGETS lodepng) + endif() + # Install the libraries. install( - TARGETS mujoco + TARGETS ${MUJOCO_TARGETS} EXPORT ${PROJECT_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT runtime diff --git a/README.md b/README.md index a03cc354..3bebcff3 100644 --- a/README.md +++ b/README.md @@ -131,15 +131,11 @@ These packages give users of various languages access to MuJoCo functionality: DeepMind's related environment stack, includes [PyMJCF](https://github.com/google-deepmind/dm_control/blob/main/dm_control/mjcf/README.md), a module for procedural manipulation of MuJoCo models. +- [JavaScript bindings and WebAssembly support](/wasm/README.md) (inspired [stillonearth](https://github.com/stillonearth) and [zalo](https://github.com/zalo)'s community projects). - [C# bindings and Unity plug-in](https://mujoco.readthedocs.io/en/stable/unity.html) #### Third-party bindings: -- **WebAssembly**: [mujoco_wasm](https://github.com/zalo/mujoco_wasm) by [@zalo](https://github.com/zalo) with contributions by - [@kevinzakka](https://github.com/kevinzakka), based on the [emscripten build](https://github.com/stillonearth/MuJoCo-WASM) by - [@stillonearth](https://github.com/stillonearth). - - :arrow_right: [Click here](https://zalo.github.io/mujoco_wasm/) for a live demo of MuJoCo running in your browser. - **MATLAB Simulink**: [Simulink Blockset for MuJoCo Simulator](https://github.com/mathworks-robotics/mujoco-simulink-blockset) by [Manoj Velmurugan](https://github.com/vmanoj1996). - **Swift**: [swift-mujoco](https://github.com/liuliu/swift-mujoco) diff --git a/cmake/MujocoDependencies.cmake b/cmake/MujocoDependencies.cmake index b11f9915..750d450f 100644 --- a/cmake/MujocoDependencies.cmake +++ b/cmake/MujocoDependencies.cmake @@ -109,7 +109,11 @@ if(NOT TARGET lodepng) add_library(lodepng STATIC ${LODEPNG_HEADERS} ${LODEPNG_SRCS}) target_compile_options(lodepng PRIVATE ${MUJOCO_MACOS_COMPILE_OPTIONS}) target_link_options(lodepng PRIVATE ${MUJOCO_MACOS_LINK_OPTIONS}) - target_include_directories(lodepng PUBLIC ${lodepng_SOURCE_DIR}) + if(NOT EMSCRIPTEN) + target_include_directories(lodepng PUBLIC ${lodepng_SOURCE_DIR}) + else() + target_include_directories(lodepng PUBLIC $ $) + endif() endif() endif() @@ -128,6 +132,10 @@ if(NOT TARGET marchingcubecpp) endif() set(QHULL_ENABLE_TESTING OFF) +# Patch changes in https://github.com/qhull/qhull/pull/173.patch +set(QHULL_PATCH_COMMAND + git apply --reject --whitespace=fix ${mujoco_SOURCE_DIR}/cmake/qhull-support-emscripten.patch +) findorfetch( USE_SYSTEM_PACKAGE @@ -143,6 +151,7 @@ findorfetch( TARGETS qhull EXCLUDE_FROM_ALL + PATCH_COMMAND ${QHULL_PATCH_COMMAND} ) # MuJoCo includes a file from libqhull_r which is not exported by the qhull include directories. # Add it to the target. @@ -222,6 +231,12 @@ endif() set(ENABLE_DOUBLE_PRECISION ON) set(CCD_HIDE_ALL_SYMBOLS ON) + +# Patch changes in https://github.com/danfis/libccd/pull/83.patch +set(CCD_PATCH_COMMAND + git apply --reject --whitespace=fix ${mujoco_SOURCE_DIR}/cmake/ccd-support-emscripten.patch +) + # update cmake_minimum_required version for compatibility with newer version of cmake if(NOT DEFINED CMAKE_POLICY_VERSION_MINIMUM) set(CMAKE_POLICY_VERSION_MINIMUM ${MUJOCO_CMAKE_MIN_REQ}) @@ -241,6 +256,7 @@ findorfetch( TARGETS ccd EXCLUDE_FROM_ALL + PATCH_COMMAND ${CCD_PATCH_COMMAND} ) if(CMAKE_POLICY_VERSION_MINIMUM_LOCALLY_DEFINED) unset(CMAKE_POLICY_VERSION_MINIMUM) diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt new file mode 100644 index 00000000..d1f0a532 --- /dev/null +++ b/wasm/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/wasm/dist") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -O3 -fexceptions") + +set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/wasm) + +include_directories(${PROJECT_SOURCE_DIR}/include) +include_directories(${PROJECT_SOURCE_DIR}/src) +include_directories(${PROJECT_SOURCE_DIR}/wasm) + +link_directories(${CMAKE_BINARY_DIR}/lib) + +file(GLOB MUJOCO_WASM_FILES + "codegen/generated/*.cc" + "unpack.cc" +) + +if(NOT MUJOCO_WASM_FILES) + message(FATAL_ERROR "No source files found in codegen/generated/") +endif() + +add_compile_options(-pthread) +add_compile_options(-fexceptions) + +# Set Emscripten linker flags +set(EMCC_LINKER_FLAGS + "--bind" + "-s ASSERTIONS=1" + "-s ALLOW_MEMORY_GROWTH=1" + "-s EXPORT_ES6=1" + "-s MODULARIZE=1" + "-s FORCE_FILESYSTEM=1" + "-s EXPORTED_RUNTIME_METHODS=['ccall','cwrap','FS','MEMFS']" + "-s EXPORT_NAME=loadMujoco" + "-s DISABLE_EXCEPTION_CATCHING=0" + "-gsource-map" + "-g" + "--emit-tsd mujoco_wasm.d.ts" +) +string (REPLACE ";" " " EMCC_LINKER_FLAGS_STR "${EMCC_LINKER_FLAGS}") + +add_executable(mujoco_wasm ${MUJOCO_WASM_FILES}) + +set_target_properties(mujoco_wasm PROPERTIES LINK_FLAGS "${EMCC_LINKER_FLAGS_STR}") + +target_link_libraries(mujoco_wasm ccd lodepng mujoco tinyxml2 qhullstatic_r) + +install(TARGETS mujoco_wasm DESTINATION ${DIVISIBLE_INSTALL_BIN_DIR}) diff --git a/wasm/README.md b/wasm/README.md new file mode 100644 index 00000000..ee32fb41 --- /dev/null +++ b/wasm/README.md @@ -0,0 +1,219 @@ +# MuJoCo JavaScript Bindings + +> [!CAUTION] +> **These bindings are not yet ready for general use. They have been added +> without announcement while we develop the CI we need to accept pull +> requests.** + +These are the canonical JavaScript and TypeScript bindings for the MuJoCo +physics engine. + +This package provides a high-level API that allows you to interact with the core +MuJoCo engine compiled into a high-performance WebAssembly (WASM) module. These +bindings are developed and maintained by Google DeepMind and are always up to +date with the latest developments in MuJoCo. For brevity, the documentation +below will often refer to “JavaScript” but the concepts apply equally to +TypeScript. + +> [!IMPORTANT] +> _These bindings are still a WIP. For details, see the [Future Work](#future-work) +> section. Also note that development has primarily taken place on Linux using +> Google Chrome. If you're working on a different OS or browser, you may +> encounter some rough edges._ + +## Prerequisites + +> [!NOTE] +> Run all the commands in this README from the top-level directory. + +- To compile the [`bindings.cc`](codegen/generated/bindings.cc) file, which + generates the `.wasm` WebAssembly file, `.js` JavaScript import, and `.d.ts` + TypeScript declaration file, you will need Emscripten SDK version `4.0.10`. + Later versions may work but are untested. To set up the SDK, do the + following, you can run this anywhere but the rest of the commands in this + README only work in the shell where you source the `emsdk_env.sh` script. + + ```sh + git clone https://github.com/emscripten-core/emsdk.git + ./emsdk/emsdk install 4.0.10 + ./emsdk/emsdk activate 4.0.10 + source ./emsdk/emsdk_env.sh + ``` + +- To easily run the JavaScript tests and the demo application, `node` and `npm` + are required. We recommend managing these using + [nvm](https://github.com/nvm-sh/nvm). There are also various JavaScript + dependencies needed for the tests, demo, and bindings build process. These + dependencies are expected to be located in the `wasm` folder. To install + them and ensure they can be found by later commands, run the following: + + ```sh + npm install --prefix ./wasm + export PATH="$(pwd)/wasm/node_modules/.bin:$PATH" + ``` + +- To modify the bindings `python3` is required because the [`bindings.cc`](codegen/generated/bindings.cc) + file is generated by a Python script. To run the bindings generator tests, + `absl` is required and `pytest` will be helpful. Set up a Python environment + with these dependencies as follows: + + ```sh + python3 -m venv .venv + source .venv/bin/activate + pip install -r python/build_requirements.txt + ``` + +> [!TIP] +> _Emscripten is well-documented. We recommend reading the sections covering the +> [Emscripten Compiler Settings](https://emscripten.org/docs/tools_reference/settings_reference.html), +> the [Emscripten SDK](https://emscripten.org/docs/tools_reference/emsdk.html), +> and the [Embind](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html) +> library. To understand the limitations and caveats related to using the +> browser as a platform, see the +> [Porting](https://emscripten.org/docs/porting/index.html#porting) section._ + +## User Guide + +### Bindings Generation + +The [`bindings.cc`](codegen/generated/bindings.cc) file is compiled to generate +to `.wasm` WebAssembly file, `.js` JavaScript import, and `.d.ts` TypeScript +declaration file. These are the files you'll use to call MuJoCo from JavaScript. +To generate them ensure the npm and Emscripten SDK prerequisites are set up and +then run the following: + +```sh +emcmake cmake -B build && cmake --build build +``` + +This command will generate the following folders under the project root: + +- `build`: contains MuJoCo compiled using Emscripten. +- `wasm/dist`: contains the WebAssembly module, `.js` and `.d.ts` files. + +### Example Application + +After generating the bindings you will be ready to write web applications using +MuJoCo. We have provided a basic web application that uses Three.js to render a +simple simulation, to try it run this command: + +```sh +npm run dev:demo --prefix ./wasm +``` + +You may prefer to write your entire app in C++ and compile it using Emscripten. +If you do this, you won’t need to use these bindings, since you’ll be writing +minimal JavaScript, and the granularity of these bindings may be inappropriate +(e.g., you might want to call multiple MuJoCo functions in the C++ callback +invoked by `requestAnimationFrame`). + +We have also found that a hybrid approach can be helpful, as it is often more +convenient to work with browser APIs directly in JavaScript. If you choose to +write your application in C++ and compile it using Emscripten, you may want to +copy a subset of the `EMSCRIPTEN_BINDINGS` from `bindings.cc` into your +application’s source file. + +## Development + +In order to change the bindings you will need to change the [`bindings.cc`](codegen/generated/bindings.cc) +file but this should not be done manually. The file is generated using the +Python scripts and template files in the [`codegen`](codegen) folder, to edit +the bindings you will need to change those files and re-generate [`bindings.cc`](codegen/generated/bindings.cc) +using this command: + +```sh +PYTHONPATH=python/mujoco python3 -m wasm.codegen.update +``` + +The codegen scripts use MuJoCo’s Python introspect library to generate the +Embind `EMSCRIPTEN_BINDINGS` block that binds C++ functions and classes to +JavaScript. The functions and classes that are bound are wrappers around +MuJoCo's C API. These wrappers provide a convenient place to add features like +bounds checking and nice error reporting. + +### Testing + +1. **JavaScript API tests.** + These verify that a wide variety of MuJoCo functions and classes work + correctly when called from JavaScript. There are also preliminary benchmarks + for JavaScript/C++ shared memory buffers. The enums test is special because + it is generated by a Python script. Run the tests as follows: + + ```sh + npm run test --prefix ./wasm + ``` + +2. **Bindings generator tests.** + These are relevant when developing or extending the bindings. The following + command finds and runs all `test_*.py` or `*_test.py` files in the `wasm` + folder: + + ```sh + PYTHONPATH=python/mujoco python3 -m pytest ./wasm + ``` + +> [!NOTE] +> If you add/edit an enum in MuJoCo you will need to run the following command +> to re-creates the [`enums_tests.ts`](tests/enums_tests.ts) file which checks +> that all the enums in the API are bound: +> +> ```sh +> PYTHONPATH=python/mujoco python3 -m wasm.codegen.enums_test_generator +> ``` + +### Debugging + +We provide a “sandbox” app where you can quickly write code to run in your +browser. Write your code in the [`main.ts`](tests/sandbox/main.ts) file and use +the following command to execute it in your browser: + +```sh +npm run dev:sandbox --prefix ./wasm +``` + +The page will be blank since the script only logs to the console output. You +can add your code at the indicated placeholder and use Chrome DevTools for +debugging. It is possible to set up a debug workflow where stack traces and +stepping through code across language boundaries work correctly. Our current +method to do this only works internally at Google, but it should be possible to +replicate the experience with open-source tooling — community suggestions are +welcome! + +## Future Work + +1. **Bind all useful APIs.** + These bindings are not yet complete. While the main MuJoCo APIs (`mj_step`, + `mj_loadXML`, etc.) are well tested, other APIs (e.g., functions from + `mjspec.h`) remain untested in real web applications (though test code for + the `mjspec` bindings does exist). One notable feature not yet supported in + the WASM bindings, which has proved very useful in the Python bindings, is + named access methods — where data distributed across multiple arrays in C can + be conveniently accessed by name, e.g., `model.geom('mygeom')` or + `data.joint('myjoint')`. Currently, this data must be accessed via the + `mj_name2id` function. Adding support for these features is a high priority, + as it affects user code written in JavaScript. + +2. **Improve the developer experience.** + There is still work to be done to improve the developer experience when + developing the WASM bindings. The most obvious issue is that bindings + generation is not yet fully automated. As a result, it is currently less + convenient than we'd like to identify and apply the changes needed to update + the bindings. The goal is to eventually automate all binding code generation + and clearly communicate what changes are required in the WASM bindings as a + result of C++ updates. This problem should only affect developers working on + the MuJoCo engine in C++, not end users writing JavaScript. + +3. **Improve the documentation.** + The documentation in this README will eventually be merged into the main + MuJoCo documentation once the bindings are complete and named access is + implemented. We also intend to review the bindings APIs and make adjustments + to minimize differences with the Python bindings (while respecting language + idioms) to reduce the amount of additional documentation required. + +4. **Improve the [example](#example-application).** + We aim to provide an example application that can be easily modified and + embedded into a paper project page (see [this example](https://kzakka.com/robopianist/)). + This could be achieved by extending the Three.js example or by compiling the + MuJoCo toolbox C++ code using the Emscripten toolchain. Community suggestions + and contributions are welcome! + diff --git a/wasm/codegen/binding_builder.py b/wasm/codegen/binding_builder.py new file mode 100644 index 00000000..e3ee2601 --- /dev/null +++ b/wasm/codegen/binding_builder.py @@ -0,0 +1,115 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Builds WASM bindings for MuJoCo.""" + +from introspect import enums as introspect_enums +from introspect import functions as introspect_functions + +from wasm.codegen.generators import enums +from wasm.codegen.generators import functions +from wasm.codegen.generators import structs + +from wasm.codegen.helpers import common +from wasm.codegen.helpers import constants as _constants +from wasm.codegen.helpers import function_utils + + +class BindingBuilder: + """Builds WASM bindings for MuJoCo.""" + + def __init__( + self, + template_path_h: str, + template_path_cc: str, + generated_path_h: str, + generated_path_cc: str, + ): + self.generated_path_h = generated_path_h + self.generated_path_cc = generated_path_cc + with open(template_path_h, "r") as f: + self.content_h = f.readlines() + with open(template_path_cc, "r") as f: + self.content_cc = f.readlines() + + filtered_functions = { + name: func + for name, func in introspect_functions.FUNCTIONS.items() + if not function_utils.is_excluded_function_name(name) + and name not in _constants.BOUNDCHECK_FUNCS + } + self.enums_generator = enums.Generator(introspect_enums.ENUMS) + self.functions_generator = functions.Generator(filtered_functions) + self.structs_generator = structs.Generator() + + def set_enums(self): + """Generates and sets the enum bindings.""" + enum_bindings = self.enums_generator.generate() + self.content_cc = common.replace_lines_containing_marker( + self.content_cc, + "// {{ ENUM_BINDINGS }}", + enum_bindings, + ) + return self + + def set_headers(self): + """Generates and sets the struct definitions.""" + struct_hdr_markers_and_content = self.structs_generator.generate_header() + + for marker, content in struct_hdr_markers_and_content: + self.content_h = common.replace_lines_containing_marker( + self.content_h, marker, content + ) + + return self + + def set_structs(self): + """Generates and sets the struct bindings.""" + + struct_src_markers_and_content = ( + self.structs_generator.generate_source() + ) + for marker, content in struct_src_markers_and_content: + self.content_cc = common.replace_lines_containing_marker( + self.content_cc, marker, content + ) + return self + + def set_functions(self): + """Generates and sets the function wrappers and bindings.""" + wrapper_functions, function_bindings = ( + self.functions_generator.generate() + ) + self.content_cc = common.replace_lines_containing_marker( + self.content_cc, + "// {{ WRAPPER_FUNCTIONS }}", + wrapper_functions, + ) + self.content_cc = common.replace_lines_containing_marker( + self.content_cc, + "// {{ FUNCTION_BINDINGS }}", + function_bindings, + ) + return self + + def build(self): + """Writes the generated content to the output files.""" + common.write_to_file(self.generated_path_h, "".join(self.content_h)) + common.write_to_file(self.generated_path_cc, "".join(self.content_cc)) + + def to_string_header(self) -> str: + return "".join(self.content_h) + + def to_string_source(self) -> str: + return "".join(self.content_cc) diff --git a/wasm/codegen/bindings_diff_test.py b/wasm/codegen/bindings_diff_test.py new file mode 100644 index 00000000..cc7a09c3 --- /dev/null +++ b/wasm/codegen/bindings_diff_test.py @@ -0,0 +1,67 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pathlib import Path + +from absl.testing import absltest + +from wasm.codegen import binding_builder + +ERROR_MESSAGE = """ +The file '{}' needs to be updated, please run: +update.py as described in wasm/README.md""".lstrip() + + +class BindingsDiffTest(absltest.TestCase): + + def setUp(self): + super().setUp() + + SCRIPT_DIR = Path(__file__).parent + with open(SCRIPT_DIR / 'generated/bindings.h', 'r') as f: + self.generated_hdr = f.read() + with open(SCRIPT_DIR / 'generated/bindings.cc', 'r') as f: + self.generated_src = f.read() + self.template_path_h = SCRIPT_DIR / 'templates/bindings.h' + self.template_path_cc = SCRIPT_DIR / 'templates/bindings.cc' + self.generated_path_h = SCRIPT_DIR / 'generated/bindings.h' + self.generated_path_cc = SCRIPT_DIR / 'generated/bindings.cc' + + self.builder = binding_builder.BindingBuilder( + self.template_path_h, + self.template_path_cc, + self.generated_path_h, + self.generated_path_cc, + ) + + def test_bindings_source(self): + generator_output = (self.builder.set_enums().set_structs().set_functions(). + to_string_source()) + self.assertEqual( + generator_output, + self.generated_src, + msg=ERROR_MESSAGE.format('bindings.cc'), + ) + + def test_bindings_header(self): + generator_output = (self.builder.set_headers().to_string_header()) + self.assertEqual( + generator_output, + self.generated_hdr, + msg=ERROR_MESSAGE.format('bindings.h'), + ) + + +if __name__ == '__main__': + absltest.main() diff --git a/wasm/codegen/coverage_test.py b/wasm/codegen/coverage_test.py new file mode 100644 index 00000000..fcb7c893 --- /dev/null +++ b/wasm/codegen/coverage_test.py @@ -0,0 +1,152 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests to ensure that all Mujoco functions and structs are correctly handled. + +This file contains tests that verify: +- All functions defined in Mujoco's introspect module are either bound in the + generated bindings.cc file or explicitly excluded in constants.py. +- All structs defined in Mujoco's introspect module are either bound in the + generated bindings.cc file or explicitly skipped in SKIPPED_STRUCTS in + constants.py. + +These tests help maintain the integrity of the generated WASM bindings by +ensuring that no functions or structs are accidentally missed or incorrectly +handled during the code generation process. +""" + +from pathlib import Path +import re + +from absl.testing import absltest +from introspect import functions as introspect_functions +from introspect import structs as introspect_structs + +from wasm.codegen.helpers import common +from wasm.codegen.helpers import constants +from wasm.codegen.helpers import function_utils + + +def _get_resource_content(file_path: str) -> str: + """Reads resource file content using resources.GetResource.""" + try: + with open(file_path, 'r') as f: + return f.read() + except FileNotFoundError: + print(f'Warning: Resource {file_path} not found.') + return '' + except IOError as e: + print(f'Error reading resource {file_path}: {e}') + return '' + + +def _get_bound_functions_from_cc() -> set[str]: + """Reads bindings.cc and extracts the names of bound functions.""" + content = _get_resource_content( + Path(__file__).parent / 'generated/bindings.cc' + ) + if not content: + return set() + + bound_functions = set() + # Find all strings within function("...") calls. + matches = re.findall(r'function\("([^"]+)"', content) + bound_functions.update(matches) + return bound_functions + + +def _get_bound_structs_from_cc() -> set[str]: + """Reads bindings.cc and extracts the names of bound structs.""" + content = _get_resource_content( + Path(__file__).parent / 'generated/bindings.cc' + ) + if not content: + return set() + + bound_structs = set() + # Find all strings within class_<...>("...") calls. + matches = re.findall(r'class_<[^>]+>\("([^"]+)"\)', content) + bound_structs.update(matches) + return bound_structs + + +class BindingCoverageTest(absltest.TestCase): + + def test_function_coverage(self): + """Asserts that each function is either excluded or bound.""" + all_functions = set(introspect_functions.FUNCTIONS.keys()) + excluded_functions = { + name + for name in all_functions + if function_utils.is_excluded_function_name(name) + } + bound_functions = _get_bound_functions_from_cc() + + missing_functions = [] + for func_name in all_functions: + if ( + func_name not in excluded_functions + and func_name not in bound_functions + ): + missing_functions.append(func_name) + + if missing_functions: + error_message = ( + f"""The following functions from functions.py are neither excluded in + constants.py nor bound in bindings.cc: + + {", ".join(sorted(missing_functions))} + + Please either add them to a exclusion list in + constants.py or create a binding in bindings.cc.""" + ) + self.fail(error_message) + + def test_struct_coverage(self): + """Asserts that each struct is either not bound or bound in structs.cc.""" + bound_structs = _get_bound_structs_from_cc() + all_structs = { + common.uppercase_first_letter(struct_name) + for struct_name in introspect_structs.STRUCTS.keys() + } + skipped_structs = { + common.uppercase_first_letter(struct_name) + for struct_name in constants.SKIPPED_STRUCTS + } + missing_structs = [] + for struct_name in all_structs: + if ( + struct_name not in skipped_structs + and struct_name not in bound_structs + ): + missing_structs.append(struct_name) + error_messages = [] + + if missing_structs: + error_messages.append( + f"""The following structs are defined in structs.py but are neither + bound in bindings.cc nor listed in SKIPPED_STRUCTS: + + {", ".join(sorted(missing_structs))} + + Please either add them to SKIPPED_STRUCTS or create its binding + in bindings.cc.""" + ) + + if error_messages: + self.fail('\n\n'.join(error_messages)) + + +if __name__ == '__main__': + absltest.main() diff --git a/wasm/codegen/enums_test_generator.py b/wasm/codegen/enums_test_generator.py new file mode 100644 index 00000000..dd3a69a5 --- /dev/null +++ b/wasm/codegen/enums_test_generator.py @@ -0,0 +1,54 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generates TypeScript code that tests Mujoco enums.""" + +import textwrap + +from introspect import enums as introspect_enums + +from wasm.codegen.helpers import common + + +def generate_typescript_enum_tests(): + """Generates TypeScript code that tests Mujoco enums.""" + output = textwrap.dedent("""\ + import 'jasmine'; + + import { MainModule } from "../dist/mujoco_wasm" + import loadMujoco from "../dist/mujoco_wasm.js" + + let mujoco: MainModule; + + describe('Enums', () => { + beforeAll(async () => { + mujoco = await loadMujoco(); + }); + """) + + for enum_decl in introspect_enums.ENUMS.values(): + enum_name = enum_decl.name + output += f""" + it('{enum_name} should exist', () => {{ + expect(mujoco.{enum_name}).toBeDefined(); + }});\n""" + + output += "});\n" + return output + + +if __name__ == "__main__": + ts_test_code = generate_typescript_enum_tests() + output_file = 'wasm/tests/enums_test.ts' + common.write_to_file(output_file, ts_test_code) diff --git a/wasm/codegen/generated/bindings.cc b/wasm/codegen/generated/bindings.cc new file mode 100644 index 00000000..769a8250 --- /dev/null +++ b/wasm/codegen/generated/bindings.cc @@ -0,0 +1,6312 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// NOLINTBEGIN(whitespace/line_length) +// NOLINTBEGIN(whitespace/semicolon) + +#include "third_party/mujoco/wasm/codegen/generated/bindings.h" + +#include +#include + +#include +#include +#include +#include // NOLINT + +#include +#include +#include +#include // NOLINT +#include // NOLINT +#include + +#include +#include +#include +#include "engine/engine_util_errmem.h" +#include "unpack.h" + +namespace mujoco::wasm { + +using emscripten::enum_; +using emscripten::class_; +using emscripten::function; +using emscripten::val; +using emscripten::constant; +using emscripten::register_optional; +using emscripten::register_type; +using emscripten::register_vector; +using emscripten::return_value_policy::reference; +using emscripten::return_value_policy::take_ownership; + +// ERROR HANDLER +void ThrowMujocoErrorToJS(const char* msg) { + // Get a handle to the JS global Error constructor function, create a new + // object instance and then throw the object as an exception using the + // val::throw_() helper function. + val(val::global("Error").new_(val("MuJoCo Error: " + std::string(msg)))) + .throw_(); +} +__attribute__((constructor)) void InitMuJoCoErrorHandler() { + mju_user_error = ThrowMujocoErrorToJS; +} + +// CONSTANTS +template +val MakeValArray(const char* (&strings)[N]) { + val result = val::array(); + for (int i = 0; i < N; i++) { + result.call("push", val(strings[i])); + } + return result; +} + +template +val MakeValArray3(const char* (&strings)[N][M]) { + val result = val::array(); + for (int i = 0; i < N; i++) { + val inner = val::array(); + for (int j = 0; j < M; j++) { + inner.call("push", val(strings[i][j])); + } + result.call("push", inner); + } + return result; +} + +val get_mjDISABLESTRING() { return MakeValArray(mjDISABLESTRING); } +val get_mjENABLESTRING() { return MakeValArray(mjENABLESTRING); } +val get_mjTIMERSTRING() { return MakeValArray(mjTIMERSTRING); } +val get_mjLABELSTRING() { return MakeValArray(mjLABELSTRING); } +val get_mjFRAMESTRING() { return MakeValArray(mjFRAMESTRING); } +val get_mjVISSTRING() { return MakeValArray3(mjVISSTRING); } +val get_mjRNDSTRING() { return MakeValArray3(mjRNDSTRING); } + +EMSCRIPTEN_BINDINGS(constants) { + // from mjmodel.h + constant("mjPI", mjPI); + constant("mjMAXVAL", mjMAXVAL); + constant("mjMINMU", mjMINMU); + constant("mjMINIMP", mjMINIMP); + constant("mjMAXIMP", mjMAXIMP); + constant("mjMAXCONPAIR", mjMAXCONPAIR); + constant("mjNEQDATA", mjNEQDATA); + constant("mjNDYN", mjNDYN); + constant("mjNGAIN", mjNGAIN); + constant("mjNBIAS", mjNBIAS); + constant("mjNREF", mjNREF); + constant("mjNIMP", mjNIMP); + constant("mjNSOLVER", mjNSOLVER); + + // from mjvisualize.h + constant("mjNGROUP", mjNGROUP); + constant("mjMAXLIGHT", mjMAXLIGHT); + constant("mjMAXOVERLAY", mjMAXOVERLAY); + constant("mjMAXLINE", mjMAXLINE); + constant("mjMAXLINEPNT", mjMAXLINEPNT); + constant("mjMAXPLANEGRID", mjMAXPLANEGRID); + + // from mujoco.h + constant("mjVERSION_HEADER", mjVERSION_HEADER); + + // from mjtnum.h + constant("mjMINVAL", mjMINVAL); + + // emscripten::constant() is designed for simple, compile-time literal values + // (like numbers or a single string literal), complex values need to be + // bound as functions. + emscripten::function("get_mjDISABLESTRING", &get_mjDISABLESTRING); + emscripten::function("get_mjENABLESTRING", &get_mjENABLESTRING); + emscripten::function("get_mjTIMERSTRING", &get_mjTIMERSTRING); + emscripten::function("get_mjLABELSTRING", &get_mjLABELSTRING); + emscripten::function("get_mjFRAMESTRING", &get_mjFRAMESTRING); + emscripten::function("get_mjVISSTRING", &get_mjVISSTRING); + emscripten::function("get_mjRNDSTRING", &get_mjRNDSTRING); +} + +EMSCRIPTEN_BINDINGS(mujoco_enums) { + enum_("mjtDisableBit") + .value("mjDSBL_CONSTRAINT", mjDSBL_CONSTRAINT) + .value("mjDSBL_EQUALITY", mjDSBL_EQUALITY) + .value("mjDSBL_FRICTIONLOSS", mjDSBL_FRICTIONLOSS) + .value("mjDSBL_LIMIT", mjDSBL_LIMIT) + .value("mjDSBL_CONTACT", mjDSBL_CONTACT) + .value("mjDSBL_SPRING", mjDSBL_SPRING) + .value("mjDSBL_DAMPER", mjDSBL_DAMPER) + .value("mjDSBL_GRAVITY", mjDSBL_GRAVITY) + .value("mjDSBL_CLAMPCTRL", mjDSBL_CLAMPCTRL) + .value("mjDSBL_WARMSTART", mjDSBL_WARMSTART) + .value("mjDSBL_FILTERPARENT", mjDSBL_FILTERPARENT) + .value("mjDSBL_ACTUATION", mjDSBL_ACTUATION) + .value("mjDSBL_REFSAFE", mjDSBL_REFSAFE) + .value("mjDSBL_SENSOR", mjDSBL_SENSOR) + .value("mjDSBL_MIDPHASE", mjDSBL_MIDPHASE) + .value("mjDSBL_EULERDAMP", mjDSBL_EULERDAMP) + .value("mjDSBL_AUTORESET", mjDSBL_AUTORESET) + .value("mjDSBL_NATIVECCD", mjDSBL_NATIVECCD) + .value("mjDSBL_ISLAND", mjDSBL_ISLAND) + .value("mjNDISABLE", mjNDISABLE); + + enum_("mjtEnableBit") + .value("mjENBL_OVERRIDE", mjENBL_OVERRIDE) + .value("mjENBL_ENERGY", mjENBL_ENERGY) + .value("mjENBL_FWDINV", mjENBL_FWDINV) + .value("mjENBL_INVDISCRETE", mjENBL_INVDISCRETE) + .value("mjENBL_MULTICCD", mjENBL_MULTICCD) + .value("mjNENABLE", mjNENABLE); + + enum_("mjtJoint") + .value("mjJNT_FREE", mjJNT_FREE) + .value("mjJNT_BALL", mjJNT_BALL) + .value("mjJNT_SLIDE", mjJNT_SLIDE) + .value("mjJNT_HINGE", mjJNT_HINGE); + + enum_("mjtGeom") + .value("mjGEOM_PLANE", mjGEOM_PLANE) + .value("mjGEOM_HFIELD", mjGEOM_HFIELD) + .value("mjGEOM_SPHERE", mjGEOM_SPHERE) + .value("mjGEOM_CAPSULE", mjGEOM_CAPSULE) + .value("mjGEOM_ELLIPSOID", mjGEOM_ELLIPSOID) + .value("mjGEOM_CYLINDER", mjGEOM_CYLINDER) + .value("mjGEOM_BOX", mjGEOM_BOX) + .value("mjGEOM_MESH", mjGEOM_MESH) + .value("mjGEOM_SDF", mjGEOM_SDF) + .value("mjNGEOMTYPES", mjNGEOMTYPES) + .value("mjGEOM_ARROW", mjGEOM_ARROW) + .value("mjGEOM_ARROW1", mjGEOM_ARROW1) + .value("mjGEOM_ARROW2", mjGEOM_ARROW2) + .value("mjGEOM_LINE", mjGEOM_LINE) + .value("mjGEOM_LINEBOX", mjGEOM_LINEBOX) + .value("mjGEOM_FLEX", mjGEOM_FLEX) + .value("mjGEOM_SKIN", mjGEOM_SKIN) + .value("mjGEOM_LABEL", mjGEOM_LABEL) + .value("mjGEOM_TRIANGLE", mjGEOM_TRIANGLE) + .value("mjGEOM_NONE", mjGEOM_NONE); + + enum_("mjtCamLight") + .value("mjCAMLIGHT_FIXED", mjCAMLIGHT_FIXED) + .value("mjCAMLIGHT_TRACK", mjCAMLIGHT_TRACK) + .value("mjCAMLIGHT_TRACKCOM", mjCAMLIGHT_TRACKCOM) + .value("mjCAMLIGHT_TARGETBODY", mjCAMLIGHT_TARGETBODY) + .value("mjCAMLIGHT_TARGETBODYCOM", mjCAMLIGHT_TARGETBODYCOM); + + enum_("mjtLightType") + .value("mjLIGHT_SPOT", mjLIGHT_SPOT) + .value("mjLIGHT_DIRECTIONAL", mjLIGHT_DIRECTIONAL) + .value("mjLIGHT_POINT", mjLIGHT_POINT) + .value("mjLIGHT_IMAGE", mjLIGHT_IMAGE); + + enum_("mjtTexture") + .value("mjTEXTURE_2D", mjTEXTURE_2D) + .value("mjTEXTURE_CUBE", mjTEXTURE_CUBE) + .value("mjTEXTURE_SKYBOX", mjTEXTURE_SKYBOX); + + enum_("mjtTextureRole") + .value("mjTEXROLE_USER", mjTEXROLE_USER) + .value("mjTEXROLE_RGB", mjTEXROLE_RGB) + .value("mjTEXROLE_OCCLUSION", mjTEXROLE_OCCLUSION) + .value("mjTEXROLE_ROUGHNESS", mjTEXROLE_ROUGHNESS) + .value("mjTEXROLE_METALLIC", mjTEXROLE_METALLIC) + .value("mjTEXROLE_NORMAL", mjTEXROLE_NORMAL) + .value("mjTEXROLE_OPACITY", mjTEXROLE_OPACITY) + .value("mjTEXROLE_EMISSIVE", mjTEXROLE_EMISSIVE) + .value("mjTEXROLE_RGBA", mjTEXROLE_RGBA) + .value("mjTEXROLE_ORM", mjTEXROLE_ORM) + .value("mjNTEXROLE", mjNTEXROLE); + + enum_("mjtColorSpace") + .value("mjCOLORSPACE_AUTO", mjCOLORSPACE_AUTO) + .value("mjCOLORSPACE_LINEAR", mjCOLORSPACE_LINEAR) + .value("mjCOLORSPACE_SRGB", mjCOLORSPACE_SRGB); + + enum_("mjtIntegrator") + .value("mjINT_EULER", mjINT_EULER) + .value("mjINT_RK4", mjINT_RK4) + .value("mjINT_IMPLICIT", mjINT_IMPLICIT) + .value("mjINT_IMPLICITFAST", mjINT_IMPLICITFAST); + + enum_("mjtCone") + .value("mjCONE_PYRAMIDAL", mjCONE_PYRAMIDAL) + .value("mjCONE_ELLIPTIC", mjCONE_ELLIPTIC); + + enum_("mjtJacobian") + .value("mjJAC_DENSE", mjJAC_DENSE) + .value("mjJAC_SPARSE", mjJAC_SPARSE) + .value("mjJAC_AUTO", mjJAC_AUTO); + + enum_("mjtSolver") + .value("mjSOL_PGS", mjSOL_PGS) + .value("mjSOL_CG", mjSOL_CG) + .value("mjSOL_NEWTON", mjSOL_NEWTON); + + enum_("mjtEq") + .value("mjEQ_CONNECT", mjEQ_CONNECT) + .value("mjEQ_WELD", mjEQ_WELD) + .value("mjEQ_JOINT", mjEQ_JOINT) + .value("mjEQ_TENDON", mjEQ_TENDON) + .value("mjEQ_FLEX", mjEQ_FLEX) + .value("mjEQ_DISTANCE", mjEQ_DISTANCE); + + enum_("mjtWrap") + .value("mjWRAP_NONE", mjWRAP_NONE) + .value("mjWRAP_JOINT", mjWRAP_JOINT) + .value("mjWRAP_PULLEY", mjWRAP_PULLEY) + .value("mjWRAP_SITE", mjWRAP_SITE) + .value("mjWRAP_SPHERE", mjWRAP_SPHERE) + .value("mjWRAP_CYLINDER", mjWRAP_CYLINDER); + + enum_("mjtTrn") + .value("mjTRN_JOINT", mjTRN_JOINT) + .value("mjTRN_JOINTINPARENT", mjTRN_JOINTINPARENT) + .value("mjTRN_SLIDERCRANK", mjTRN_SLIDERCRANK) + .value("mjTRN_TENDON", mjTRN_TENDON) + .value("mjTRN_SITE", mjTRN_SITE) + .value("mjTRN_BODY", mjTRN_BODY) + .value("mjTRN_UNDEFINED", mjTRN_UNDEFINED); + + enum_("mjtDyn") + .value("mjDYN_NONE", mjDYN_NONE) + .value("mjDYN_INTEGRATOR", mjDYN_INTEGRATOR) + .value("mjDYN_FILTER", mjDYN_FILTER) + .value("mjDYN_FILTEREXACT", mjDYN_FILTEREXACT) + .value("mjDYN_MUSCLE", mjDYN_MUSCLE) + .value("mjDYN_USER", mjDYN_USER); + + enum_("mjtGain") + .value("mjGAIN_FIXED", mjGAIN_FIXED) + .value("mjGAIN_AFFINE", mjGAIN_AFFINE) + .value("mjGAIN_MUSCLE", mjGAIN_MUSCLE) + .value("mjGAIN_USER", mjGAIN_USER); + + enum_("mjtBias") + .value("mjBIAS_NONE", mjBIAS_NONE) + .value("mjBIAS_AFFINE", mjBIAS_AFFINE) + .value("mjBIAS_MUSCLE", mjBIAS_MUSCLE) + .value("mjBIAS_USER", mjBIAS_USER); + + enum_("mjtObj") + .value("mjOBJ_UNKNOWN", mjOBJ_UNKNOWN) + .value("mjOBJ_BODY", mjOBJ_BODY) + .value("mjOBJ_XBODY", mjOBJ_XBODY) + .value("mjOBJ_JOINT", mjOBJ_JOINT) + .value("mjOBJ_DOF", mjOBJ_DOF) + .value("mjOBJ_GEOM", mjOBJ_GEOM) + .value("mjOBJ_SITE", mjOBJ_SITE) + .value("mjOBJ_CAMERA", mjOBJ_CAMERA) + .value("mjOBJ_LIGHT", mjOBJ_LIGHT) + .value("mjOBJ_FLEX", mjOBJ_FLEX) + .value("mjOBJ_MESH", mjOBJ_MESH) + .value("mjOBJ_SKIN", mjOBJ_SKIN) + .value("mjOBJ_HFIELD", mjOBJ_HFIELD) + .value("mjOBJ_TEXTURE", mjOBJ_TEXTURE) + .value("mjOBJ_MATERIAL", mjOBJ_MATERIAL) + .value("mjOBJ_PAIR", mjOBJ_PAIR) + .value("mjOBJ_EXCLUDE", mjOBJ_EXCLUDE) + .value("mjOBJ_EQUALITY", mjOBJ_EQUALITY) + .value("mjOBJ_TENDON", mjOBJ_TENDON) + .value("mjOBJ_ACTUATOR", mjOBJ_ACTUATOR) + .value("mjOBJ_SENSOR", mjOBJ_SENSOR) + .value("mjOBJ_NUMERIC", mjOBJ_NUMERIC) + .value("mjOBJ_TEXT", mjOBJ_TEXT) + .value("mjOBJ_TUPLE", mjOBJ_TUPLE) + .value("mjOBJ_KEY", mjOBJ_KEY) + .value("mjOBJ_PLUGIN", mjOBJ_PLUGIN) + .value("mjNOBJECT", mjNOBJECT) + .value("mjOBJ_FRAME", mjOBJ_FRAME) + .value("mjOBJ_DEFAULT", mjOBJ_DEFAULT) + .value("mjOBJ_MODEL", mjOBJ_MODEL); + + enum_("mjtSensor") + .value("mjSENS_TOUCH", mjSENS_TOUCH) + .value("mjSENS_ACCELEROMETER", mjSENS_ACCELEROMETER) + .value("mjSENS_VELOCIMETER", mjSENS_VELOCIMETER) + .value("mjSENS_GYRO", mjSENS_GYRO) + .value("mjSENS_FORCE", mjSENS_FORCE) + .value("mjSENS_TORQUE", mjSENS_TORQUE) + .value("mjSENS_MAGNETOMETER", mjSENS_MAGNETOMETER) + .value("mjSENS_RANGEFINDER", mjSENS_RANGEFINDER) + .value("mjSENS_CAMPROJECTION", mjSENS_CAMPROJECTION) + .value("mjSENS_JOINTPOS", mjSENS_JOINTPOS) + .value("mjSENS_JOINTVEL", mjSENS_JOINTVEL) + .value("mjSENS_TENDONPOS", mjSENS_TENDONPOS) + .value("mjSENS_TENDONVEL", mjSENS_TENDONVEL) + .value("mjSENS_ACTUATORPOS", mjSENS_ACTUATORPOS) + .value("mjSENS_ACTUATORVEL", mjSENS_ACTUATORVEL) + .value("mjSENS_ACTUATORFRC", mjSENS_ACTUATORFRC) + .value("mjSENS_JOINTACTFRC", mjSENS_JOINTACTFRC) + .value("mjSENS_TENDONACTFRC", mjSENS_TENDONACTFRC) + .value("mjSENS_BALLQUAT", mjSENS_BALLQUAT) + .value("mjSENS_BALLANGVEL", mjSENS_BALLANGVEL) + .value("mjSENS_JOINTLIMITPOS", mjSENS_JOINTLIMITPOS) + .value("mjSENS_JOINTLIMITVEL", mjSENS_JOINTLIMITVEL) + .value("mjSENS_JOINTLIMITFRC", mjSENS_JOINTLIMITFRC) + .value("mjSENS_TENDONLIMITPOS", mjSENS_TENDONLIMITPOS) + .value("mjSENS_TENDONLIMITVEL", mjSENS_TENDONLIMITVEL) + .value("mjSENS_TENDONLIMITFRC", mjSENS_TENDONLIMITFRC) + .value("mjSENS_FRAMEPOS", mjSENS_FRAMEPOS) + .value("mjSENS_FRAMEQUAT", mjSENS_FRAMEQUAT) + .value("mjSENS_FRAMEXAXIS", mjSENS_FRAMEXAXIS) + .value("mjSENS_FRAMEYAXIS", mjSENS_FRAMEYAXIS) + .value("mjSENS_FRAMEZAXIS", mjSENS_FRAMEZAXIS) + .value("mjSENS_FRAMELINVEL", mjSENS_FRAMELINVEL) + .value("mjSENS_FRAMEANGVEL", mjSENS_FRAMEANGVEL) + .value("mjSENS_FRAMELINACC", mjSENS_FRAMELINACC) + .value("mjSENS_FRAMEANGACC", mjSENS_FRAMEANGACC) + .value("mjSENS_SUBTREECOM", mjSENS_SUBTREECOM) + .value("mjSENS_SUBTREELINVEL", mjSENS_SUBTREELINVEL) + .value("mjSENS_SUBTREEANGMOM", mjSENS_SUBTREEANGMOM) + .value("mjSENS_INSIDESITE", mjSENS_INSIDESITE) + .value("mjSENS_GEOMDIST", mjSENS_GEOMDIST) + .value("mjSENS_GEOMNORMAL", mjSENS_GEOMNORMAL) + .value("mjSENS_GEOMFROMTO", mjSENS_GEOMFROMTO) + .value("mjSENS_CONTACT", mjSENS_CONTACT) + .value("mjSENS_E_POTENTIAL", mjSENS_E_POTENTIAL) + .value("mjSENS_E_KINETIC", mjSENS_E_KINETIC) + .value("mjSENS_CLOCK", mjSENS_CLOCK) + .value("mjSENS_TACTILE", mjSENS_TACTILE) + .value("mjSENS_PLUGIN", mjSENS_PLUGIN) + .value("mjSENS_USER", mjSENS_USER); + + enum_("mjtStage") + .value("mjSTAGE_NONE", mjSTAGE_NONE) + .value("mjSTAGE_POS", mjSTAGE_POS) + .value("mjSTAGE_VEL", mjSTAGE_VEL) + .value("mjSTAGE_ACC", mjSTAGE_ACC); + + enum_("mjtDataType") + .value("mjDATATYPE_REAL", mjDATATYPE_REAL) + .value("mjDATATYPE_POSITIVE", mjDATATYPE_POSITIVE) + .value("mjDATATYPE_AXIS", mjDATATYPE_AXIS) + .value("mjDATATYPE_QUATERNION", mjDATATYPE_QUATERNION); + + enum_("mjtConDataField") + .value("mjCONDATA_FOUND", mjCONDATA_FOUND) + .value("mjCONDATA_FORCE", mjCONDATA_FORCE) + .value("mjCONDATA_TORQUE", mjCONDATA_TORQUE) + .value("mjCONDATA_DIST", mjCONDATA_DIST) + .value("mjCONDATA_POS", mjCONDATA_POS) + .value("mjCONDATA_NORMAL", mjCONDATA_NORMAL) + .value("mjCONDATA_TANGENT", mjCONDATA_TANGENT) + .value("mjNCONDATA", mjNCONDATA); + + enum_("mjtSameFrame") + .value("mjSAMEFRAME_NONE", mjSAMEFRAME_NONE) + .value("mjSAMEFRAME_BODY", mjSAMEFRAME_BODY) + .value("mjSAMEFRAME_INERTIA", mjSAMEFRAME_INERTIA) + .value("mjSAMEFRAME_BODYROT", mjSAMEFRAME_BODYROT) + .value("mjSAMEFRAME_INERTIAROT", mjSAMEFRAME_INERTIAROT); + + enum_("mjtLRMode") + .value("mjLRMODE_NONE", mjLRMODE_NONE) + .value("mjLRMODE_MUSCLE", mjLRMODE_MUSCLE) + .value("mjLRMODE_MUSCLEUSER", mjLRMODE_MUSCLEUSER) + .value("mjLRMODE_ALL", mjLRMODE_ALL); + + enum_("mjtFlexSelf") + .value("mjFLEXSELF_NONE", mjFLEXSELF_NONE) + .value("mjFLEXSELF_NARROW", mjFLEXSELF_NARROW) + .value("mjFLEXSELF_BVH", mjFLEXSELF_BVH) + .value("mjFLEXSELF_SAP", mjFLEXSELF_SAP) + .value("mjFLEXSELF_AUTO", mjFLEXSELF_AUTO); + + enum_("mjtSDFType") + .value("mjSDFTYPE_SINGLE", mjSDFTYPE_SINGLE) + .value("mjSDFTYPE_INTERSECTION", mjSDFTYPE_INTERSECTION) + .value("mjSDFTYPE_MIDSURFACE", mjSDFTYPE_MIDSURFACE) + .value("mjSDFTYPE_COLLISION", mjSDFTYPE_COLLISION); + + enum_("mjtTaskStatus") + .value("mjTASK_NEW", mjTASK_NEW) + .value("mjTASK_QUEUED", mjTASK_QUEUED) + .value("mjTASK_COMPLETED", mjTASK_COMPLETED); + + enum_("mjtState") + .value("mjSTATE_TIME", mjSTATE_TIME) + .value("mjSTATE_QPOS", mjSTATE_QPOS) + .value("mjSTATE_QVEL", mjSTATE_QVEL) + .value("mjSTATE_ACT", mjSTATE_ACT) + .value("mjSTATE_WARMSTART", mjSTATE_WARMSTART) + .value("mjSTATE_CTRL", mjSTATE_CTRL) + .value("mjSTATE_QFRC_APPLIED", mjSTATE_QFRC_APPLIED) + .value("mjSTATE_XFRC_APPLIED", mjSTATE_XFRC_APPLIED) + .value("mjSTATE_EQ_ACTIVE", mjSTATE_EQ_ACTIVE) + .value("mjSTATE_MOCAP_POS", mjSTATE_MOCAP_POS) + .value("mjSTATE_MOCAP_QUAT", mjSTATE_MOCAP_QUAT) + .value("mjSTATE_USERDATA", mjSTATE_USERDATA) + .value("mjSTATE_PLUGIN", mjSTATE_PLUGIN) + .value("mjNSTATE", mjNSTATE) + .value("mjSTATE_PHYSICS", mjSTATE_PHYSICS) + .value("mjSTATE_FULLPHYSICS", mjSTATE_FULLPHYSICS) + .value("mjSTATE_USER", mjSTATE_USER) + .value("mjSTATE_INTEGRATION", mjSTATE_INTEGRATION); + + enum_("mjtConstraint") + .value("mjCNSTR_EQUALITY", mjCNSTR_EQUALITY) + .value("mjCNSTR_FRICTION_DOF", mjCNSTR_FRICTION_DOF) + .value("mjCNSTR_FRICTION_TENDON", mjCNSTR_FRICTION_TENDON) + .value("mjCNSTR_LIMIT_JOINT", mjCNSTR_LIMIT_JOINT) + .value("mjCNSTR_LIMIT_TENDON", mjCNSTR_LIMIT_TENDON) + .value("mjCNSTR_CONTACT_FRICTIONLESS", mjCNSTR_CONTACT_FRICTIONLESS) + .value("mjCNSTR_CONTACT_PYRAMIDAL", mjCNSTR_CONTACT_PYRAMIDAL) + .value("mjCNSTR_CONTACT_ELLIPTIC", mjCNSTR_CONTACT_ELLIPTIC); + + enum_("mjtConstraintState") + .value("mjCNSTRSTATE_SATISFIED", mjCNSTRSTATE_SATISFIED) + .value("mjCNSTRSTATE_QUADRATIC", mjCNSTRSTATE_QUADRATIC) + .value("mjCNSTRSTATE_LINEARNEG", mjCNSTRSTATE_LINEARNEG) + .value("mjCNSTRSTATE_LINEARPOS", mjCNSTRSTATE_LINEARPOS) + .value("mjCNSTRSTATE_CONE", mjCNSTRSTATE_CONE); + + enum_("mjtWarning") + .value("mjWARN_INERTIA", mjWARN_INERTIA) + .value("mjWARN_CONTACTFULL", mjWARN_CONTACTFULL) + .value("mjWARN_CNSTRFULL", mjWARN_CNSTRFULL) + .value("mjWARN_VGEOMFULL", mjWARN_VGEOMFULL) + .value("mjWARN_BADQPOS", mjWARN_BADQPOS) + .value("mjWARN_BADQVEL", mjWARN_BADQVEL) + .value("mjWARN_BADQACC", mjWARN_BADQACC) + .value("mjWARN_BADCTRL", mjWARN_BADCTRL) + .value("mjNWARNING", mjNWARNING); + + enum_("mjtTimer") + .value("mjTIMER_STEP", mjTIMER_STEP) + .value("mjTIMER_FORWARD", mjTIMER_FORWARD) + .value("mjTIMER_INVERSE", mjTIMER_INVERSE) + .value("mjTIMER_POSITION", mjTIMER_POSITION) + .value("mjTIMER_VELOCITY", mjTIMER_VELOCITY) + .value("mjTIMER_ACTUATION", mjTIMER_ACTUATION) + .value("mjTIMER_CONSTRAINT", mjTIMER_CONSTRAINT) + .value("mjTIMER_ADVANCE", mjTIMER_ADVANCE) + .value("mjTIMER_POS_KINEMATICS", mjTIMER_POS_KINEMATICS) + .value("mjTIMER_POS_INERTIA", mjTIMER_POS_INERTIA) + .value("mjTIMER_POS_COLLISION", mjTIMER_POS_COLLISION) + .value("mjTIMER_POS_MAKE", mjTIMER_POS_MAKE) + .value("mjTIMER_POS_PROJECT", mjTIMER_POS_PROJECT) + .value("mjTIMER_COL_BROAD", mjTIMER_COL_BROAD) + .value("mjTIMER_COL_NARROW", mjTIMER_COL_NARROW) + .value("mjNTIMER", mjNTIMER); + + enum_("mjtCatBit") + .value("mjCAT_STATIC", mjCAT_STATIC) + .value("mjCAT_DYNAMIC", mjCAT_DYNAMIC) + .value("mjCAT_DECOR", mjCAT_DECOR) + .value("mjCAT_ALL", mjCAT_ALL); + + enum_("mjtMouse") + .value("mjMOUSE_NONE", mjMOUSE_NONE) + .value("mjMOUSE_ROTATE_V", mjMOUSE_ROTATE_V) + .value("mjMOUSE_ROTATE_H", mjMOUSE_ROTATE_H) + .value("mjMOUSE_MOVE_V", mjMOUSE_MOVE_V) + .value("mjMOUSE_MOVE_H", mjMOUSE_MOVE_H) + .value("mjMOUSE_ZOOM", mjMOUSE_ZOOM) + .value("mjMOUSE_MOVE_V_REL", mjMOUSE_MOVE_V_REL) + .value("mjMOUSE_MOVE_H_REL", mjMOUSE_MOVE_H_REL); + + enum_("mjtPertBit") + .value("mjPERT_TRANSLATE", mjPERT_TRANSLATE) + .value("mjPERT_ROTATE", mjPERT_ROTATE); + + enum_("mjtCamera") + .value("mjCAMERA_FREE", mjCAMERA_FREE) + .value("mjCAMERA_TRACKING", mjCAMERA_TRACKING) + .value("mjCAMERA_FIXED", mjCAMERA_FIXED) + .value("mjCAMERA_USER", mjCAMERA_USER); + + enum_("mjtLabel") + .value("mjLABEL_NONE", mjLABEL_NONE) + .value("mjLABEL_BODY", mjLABEL_BODY) + .value("mjLABEL_JOINT", mjLABEL_JOINT) + .value("mjLABEL_GEOM", mjLABEL_GEOM) + .value("mjLABEL_SITE", mjLABEL_SITE) + .value("mjLABEL_CAMERA", mjLABEL_CAMERA) + .value("mjLABEL_LIGHT", mjLABEL_LIGHT) + .value("mjLABEL_TENDON", mjLABEL_TENDON) + .value("mjLABEL_ACTUATOR", mjLABEL_ACTUATOR) + .value("mjLABEL_CONSTRAINT", mjLABEL_CONSTRAINT) + .value("mjLABEL_FLEX", mjLABEL_FLEX) + .value("mjLABEL_SKIN", mjLABEL_SKIN) + .value("mjLABEL_SELECTION", mjLABEL_SELECTION) + .value("mjLABEL_SELPNT", mjLABEL_SELPNT) + .value("mjLABEL_CONTACTPOINT", mjLABEL_CONTACTPOINT) + .value("mjLABEL_CONTACTFORCE", mjLABEL_CONTACTFORCE) + .value("mjLABEL_ISLAND", mjLABEL_ISLAND) + .value("mjNLABEL", mjNLABEL); + + enum_("mjtFrame") + .value("mjFRAME_NONE", mjFRAME_NONE) + .value("mjFRAME_BODY", mjFRAME_BODY) + .value("mjFRAME_GEOM", mjFRAME_GEOM) + .value("mjFRAME_SITE", mjFRAME_SITE) + .value("mjFRAME_CAMERA", mjFRAME_CAMERA) + .value("mjFRAME_LIGHT", mjFRAME_LIGHT) + .value("mjFRAME_CONTACT", mjFRAME_CONTACT) + .value("mjFRAME_WORLD", mjFRAME_WORLD) + .value("mjNFRAME", mjNFRAME); + + enum_("mjtVisFlag") + .value("mjVIS_CONVEXHULL", mjVIS_CONVEXHULL) + .value("mjVIS_TEXTURE", mjVIS_TEXTURE) + .value("mjVIS_JOINT", mjVIS_JOINT) + .value("mjVIS_CAMERA", mjVIS_CAMERA) + .value("mjVIS_ACTUATOR", mjVIS_ACTUATOR) + .value("mjVIS_ACTIVATION", mjVIS_ACTIVATION) + .value("mjVIS_LIGHT", mjVIS_LIGHT) + .value("mjVIS_TENDON", mjVIS_TENDON) + .value("mjVIS_RANGEFINDER", mjVIS_RANGEFINDER) + .value("mjVIS_CONSTRAINT", mjVIS_CONSTRAINT) + .value("mjVIS_INERTIA", mjVIS_INERTIA) + .value("mjVIS_SCLINERTIA", mjVIS_SCLINERTIA) + .value("mjVIS_PERTFORCE", mjVIS_PERTFORCE) + .value("mjVIS_PERTOBJ", mjVIS_PERTOBJ) + .value("mjVIS_CONTACTPOINT", mjVIS_CONTACTPOINT) + .value("mjVIS_ISLAND", mjVIS_ISLAND) + .value("mjVIS_CONTACTFORCE", mjVIS_CONTACTFORCE) + .value("mjVIS_CONTACTSPLIT", mjVIS_CONTACTSPLIT) + .value("mjVIS_TRANSPARENT", mjVIS_TRANSPARENT) + .value("mjVIS_AUTOCONNECT", mjVIS_AUTOCONNECT) + .value("mjVIS_COM", mjVIS_COM) + .value("mjVIS_SELECT", mjVIS_SELECT) + .value("mjVIS_STATIC", mjVIS_STATIC) + .value("mjVIS_SKIN", mjVIS_SKIN) + .value("mjVIS_FLEXVERT", mjVIS_FLEXVERT) + .value("mjVIS_FLEXEDGE", mjVIS_FLEXEDGE) + .value("mjVIS_FLEXFACE", mjVIS_FLEXFACE) + .value("mjVIS_FLEXSKIN", mjVIS_FLEXSKIN) + .value("mjVIS_BODYBVH", mjVIS_BODYBVH) + .value("mjVIS_MESHBVH", mjVIS_MESHBVH) + .value("mjVIS_SDFITER", mjVIS_SDFITER) + .value("mjNVISFLAG", mjNVISFLAG); + + enum_("mjtRndFlag") + .value("mjRND_SHADOW", mjRND_SHADOW) + .value("mjRND_WIREFRAME", mjRND_WIREFRAME) + .value("mjRND_REFLECTION", mjRND_REFLECTION) + .value("mjRND_ADDITIVE", mjRND_ADDITIVE) + .value("mjRND_SKYBOX", mjRND_SKYBOX) + .value("mjRND_FOG", mjRND_FOG) + .value("mjRND_HAZE", mjRND_HAZE) + .value("mjRND_SEGMENT", mjRND_SEGMENT) + .value("mjRND_IDCOLOR", mjRND_IDCOLOR) + .value("mjRND_CULL_FACE", mjRND_CULL_FACE) + .value("mjNRNDFLAG", mjNRNDFLAG); + + enum_("mjtStereo") + .value("mjSTEREO_NONE", mjSTEREO_NONE) + .value("mjSTEREO_QUADBUFFERED", mjSTEREO_QUADBUFFERED) + .value("mjSTEREO_SIDEBYSIDE", mjSTEREO_SIDEBYSIDE); + + enum_("mjtPluginCapabilityBit") + .value("mjPLUGIN_ACTUATOR", mjPLUGIN_ACTUATOR) + .value("mjPLUGIN_SENSOR", mjPLUGIN_SENSOR) + .value("mjPLUGIN_PASSIVE", mjPLUGIN_PASSIVE) + .value("mjPLUGIN_SDF", mjPLUGIN_SDF); + + enum_("mjtGridPos") + .value("mjGRID_TOPLEFT", mjGRID_TOPLEFT) + .value("mjGRID_TOPRIGHT", mjGRID_TOPRIGHT) + .value("mjGRID_BOTTOMLEFT", mjGRID_BOTTOMLEFT) + .value("mjGRID_BOTTOMRIGHT", mjGRID_BOTTOMRIGHT) + .value("mjGRID_TOP", mjGRID_TOP) + .value("mjGRID_BOTTOM", mjGRID_BOTTOM) + .value("mjGRID_LEFT", mjGRID_LEFT) + .value("mjGRID_RIGHT", mjGRID_RIGHT); + + enum_("mjtFramebuffer") + .value("mjFB_WINDOW", mjFB_WINDOW) + .value("mjFB_OFFSCREEN", mjFB_OFFSCREEN); + + enum_("mjtDepthMap") + .value("mjDEPTH_ZERONEAR", mjDEPTH_ZERONEAR) + .value("mjDEPTH_ZEROFAR", mjDEPTH_ZEROFAR); + + enum_("mjtFontScale") + .value("mjFONTSCALE_50", mjFONTSCALE_50) + .value("mjFONTSCALE_100", mjFONTSCALE_100) + .value("mjFONTSCALE_150", mjFONTSCALE_150) + .value("mjFONTSCALE_200", mjFONTSCALE_200) + .value("mjFONTSCALE_250", mjFONTSCALE_250) + .value("mjFONTSCALE_300", mjFONTSCALE_300); + + enum_("mjtFont") + .value("mjFONT_NORMAL", mjFONT_NORMAL) + .value("mjFONT_SHADOW", mjFONT_SHADOW) + .value("mjFONT_BIG", mjFONT_BIG); + + enum_("mjtGeomInertia") + .value("mjINERTIA_VOLUME", mjINERTIA_VOLUME) + .value("mjINERTIA_SHELL", mjINERTIA_SHELL); + + enum_("mjtMeshInertia") + .value("mjMESH_INERTIA_CONVEX", mjMESH_INERTIA_CONVEX) + .value("mjMESH_INERTIA_EXACT", mjMESH_INERTIA_EXACT) + .value("mjMESH_INERTIA_LEGACY", mjMESH_INERTIA_LEGACY) + .value("mjMESH_INERTIA_SHELL", mjMESH_INERTIA_SHELL); + + enum_("mjtMeshBuiltin") + .value("mjMESH_BUILTIN_NONE", mjMESH_BUILTIN_NONE) + .value("mjMESH_BUILTIN_SPHERE", mjMESH_BUILTIN_SPHERE) + .value("mjMESH_BUILTIN_HEMISPHERE", mjMESH_BUILTIN_HEMISPHERE) + .value("mjMESH_BUILTIN_CONE", mjMESH_BUILTIN_CONE) + .value("mjMESH_BUILTIN_SUPERSPHERE", mjMESH_BUILTIN_SUPERSPHERE) + .value("mjMESH_BUILTIN_SUPERTORUS", mjMESH_BUILTIN_SUPERTORUS) + .value("mjMESH_BUILTIN_WEDGE", mjMESH_BUILTIN_WEDGE) + .value("mjMESH_BUILTIN_PLATE", mjMESH_BUILTIN_PLATE); + + enum_("mjtBuiltin") + .value("mjBUILTIN_NONE", mjBUILTIN_NONE) + .value("mjBUILTIN_GRADIENT", mjBUILTIN_GRADIENT) + .value("mjBUILTIN_CHECKER", mjBUILTIN_CHECKER) + .value("mjBUILTIN_FLAT", mjBUILTIN_FLAT); + + enum_("mjtMark") + .value("mjMARK_NONE", mjMARK_NONE) + .value("mjMARK_EDGE", mjMARK_EDGE) + .value("mjMARK_CROSS", mjMARK_CROSS) + .value("mjMARK_RANDOM", mjMARK_RANDOM); + + enum_("mjtLimited") + .value("mjLIMITED_FALSE", mjLIMITED_FALSE) + .value("mjLIMITED_TRUE", mjLIMITED_TRUE) + .value("mjLIMITED_AUTO", mjLIMITED_AUTO); + + enum_("mjtAlignFree") + .value("mjALIGNFREE_FALSE", mjALIGNFREE_FALSE) + .value("mjALIGNFREE_TRUE", mjALIGNFREE_TRUE) + .value("mjALIGNFREE_AUTO", mjALIGNFREE_AUTO); + + enum_("mjtInertiaFromGeom") + .value("mjINERTIAFROMGEOM_FALSE", mjINERTIAFROMGEOM_FALSE) + .value("mjINERTIAFROMGEOM_TRUE", mjINERTIAFROMGEOM_TRUE) + .value("mjINERTIAFROMGEOM_AUTO", mjINERTIAFROMGEOM_AUTO); + + enum_("mjtOrientation") + .value("mjORIENTATION_QUAT", mjORIENTATION_QUAT) + .value("mjORIENTATION_AXISANGLE", mjORIENTATION_AXISANGLE) + .value("mjORIENTATION_XYAXES", mjORIENTATION_XYAXES) + .value("mjORIENTATION_ZAXIS", mjORIENTATION_ZAXIS) + .value("mjORIENTATION_EULER", mjORIENTATION_EULER); + + enum_("mjtButton") + .value("mjBUTTON_NONE", mjBUTTON_NONE) + .value("mjBUTTON_LEFT", mjBUTTON_LEFT) + .value("mjBUTTON_RIGHT", mjBUTTON_RIGHT) + .value("mjBUTTON_MIDDLE", mjBUTTON_MIDDLE); + + enum_("mjtEvent") + .value("mjEVENT_NONE", mjEVENT_NONE) + .value("mjEVENT_MOVE", mjEVENT_MOVE) + .value("mjEVENT_PRESS", mjEVENT_PRESS) + .value("mjEVENT_RELEASE", mjEVENT_RELEASE) + .value("mjEVENT_SCROLL", mjEVENT_SCROLL) + .value("mjEVENT_KEY", mjEVENT_KEY) + .value("mjEVENT_RESIZE", mjEVENT_RESIZE) + .value("mjEVENT_REDRAW", mjEVENT_REDRAW) + .value("mjEVENT_FILESDROP", mjEVENT_FILESDROP); + + enum_("mjtItem") + .value("mjITEM_END", mjITEM_END) + .value("mjITEM_SECTION", mjITEM_SECTION) + .value("mjITEM_SEPARATOR", mjITEM_SEPARATOR) + .value("mjITEM_STATIC", mjITEM_STATIC) + .value("mjITEM_BUTTON", mjITEM_BUTTON) + .value("mjITEM_CHECKINT", mjITEM_CHECKINT) + .value("mjITEM_CHECKBYTE", mjITEM_CHECKBYTE) + .value("mjITEM_RADIO", mjITEM_RADIO) + .value("mjITEM_RADIOLINE", mjITEM_RADIOLINE) + .value("mjITEM_SELECT", mjITEM_SELECT) + .value("mjITEM_SLIDERINT", mjITEM_SLIDERINT) + .value("mjITEM_SLIDERNUM", mjITEM_SLIDERNUM) + .value("mjITEM_EDITINT", mjITEM_EDITINT) + .value("mjITEM_EDITNUM", mjITEM_EDITNUM) + .value("mjITEM_EDITFLOAT", mjITEM_EDITFLOAT) + .value("mjITEM_EDITTXT", mjITEM_EDITTXT) + .value("mjNITEM", mjNITEM); + + enum_("mjtSection") + .value("mjSECT_CLOSED", mjSECT_CLOSED) + .value("mjSECT_OPEN", mjSECT_OPEN) + .value("mjSECT_FIXED", mjSECT_FIXED); +} + +// STRUCTS +// =============== MjLROpt =============== // +MjLROpt::MjLROpt(mjLROpt *ptr) : ptr_(ptr) {} +MjLROpt::MjLROpt() : ptr_(new mjLROpt) { + owned_ = true; + mj_defaultLROpt(ptr_); +} +MjLROpt::MjLROpt(const MjLROpt &other) : MjLROpt() { + *ptr_ = *other.get(); +} +MjLROpt& MjLROpt::operator=(const MjLROpt &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjLROpt::~MjLROpt() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjLROpt::copy() { + return std::make_unique(*this); +} + +// =============== MjOption =============== // +MjOption::MjOption(mjOption *ptr) : ptr_(ptr) {} +MjOption::MjOption() : ptr_(new mjOption) { + owned_ = true; + mj_defaultOption(ptr_); +} +MjOption::MjOption(const MjOption &other) : MjOption() { + *ptr_ = *other.get(); +} +MjOption& MjOption::operator=(const MjOption &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjOption::~MjOption() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjOption::copy() { + return std::make_unique(*this); +} + +// =============== MjStatistic =============== // +MjStatistic::MjStatistic(mjStatistic *ptr) : ptr_(ptr) {} +MjStatistic::MjStatistic() : ptr_(new mjStatistic) { + owned_ = true; +} +MjStatistic::MjStatistic(const MjStatistic &other) : MjStatistic() { + *ptr_ = *other.get(); +} +MjStatistic& MjStatistic::operator=(const MjStatistic &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjStatistic::~MjStatistic() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjStatistic::copy() { + return std::make_unique(*this); +} + +// =============== MjVisual... =============== // +MjVisualGlobal::MjVisualGlobal(mjVisualGlobal *ptr) : ptr_(ptr) {} +MjVisualGlobal::MjVisualGlobal() : ptr_(new mjVisualGlobal) { + owned_ = true; +} +MjVisualGlobal::MjVisualGlobal(const MjVisualGlobal &other) : MjVisualGlobal() { + *ptr_ = *other.get(); +} +MjVisualGlobal& MjVisualGlobal::operator=(const MjVisualGlobal &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjVisualGlobal::~MjVisualGlobal() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjVisualGlobal::copy() { + return std::make_unique(*this); +} + +MjVisualQuality::MjVisualQuality(mjVisualQuality *ptr) : ptr_(ptr) {} +MjVisualQuality::MjVisualQuality() : ptr_(new mjVisualQuality) { + owned_ = true; +} +MjVisualQuality::MjVisualQuality(const MjVisualQuality &other) : MjVisualQuality() { + *ptr_ = *other.get(); +} +MjVisualQuality& MjVisualQuality::operator=(const MjVisualQuality &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjVisualQuality::~MjVisualQuality() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjVisualQuality::copy() { + return std::make_unique(*this); +} + +MjVisualHeadlight::MjVisualHeadlight(mjVisualHeadlight *ptr) : ptr_(ptr) {} +MjVisualHeadlight::MjVisualHeadlight() : ptr_(new mjVisualHeadlight) { + owned_ = true; +} +MjVisualHeadlight::MjVisualHeadlight(const MjVisualHeadlight &other) : MjVisualHeadlight() { + *ptr_ = *other.get(); +} +MjVisualHeadlight& MjVisualHeadlight::operator=(const MjVisualHeadlight &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjVisualHeadlight::~MjVisualHeadlight() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjVisualHeadlight::copy() { + return std::make_unique(*this); +} + +MjVisualMap::MjVisualMap(mjVisualMap *ptr) : ptr_(ptr) {} +MjVisualMap::MjVisualMap() : ptr_(new mjVisualMap) { + owned_ = true; +} +MjVisualMap::MjVisualMap(const MjVisualMap &other) : MjVisualMap() { + *ptr_ = *other.get(); +} +MjVisualMap& MjVisualMap::operator=(const MjVisualMap &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjVisualMap::~MjVisualMap() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjVisualMap::copy() { + return std::make_unique(*this); +} + +MjVisualScale::MjVisualScale(mjVisualScale *ptr) : ptr_(ptr) {} +MjVisualScale::MjVisualScale() : ptr_(new mjVisualScale) { + owned_ = true; +} +MjVisualScale::MjVisualScale(const MjVisualScale &other) : MjVisualScale() { + *ptr_ = *other.get(); +} +MjVisualScale& MjVisualScale::operator=(const MjVisualScale &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjVisualScale::~MjVisualScale() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjVisualScale::copy() { + return std::make_unique(*this); +} + +MjVisualRgba::MjVisualRgba(mjVisualRgba *ptr) : ptr_(ptr) {} +MjVisualRgba::MjVisualRgba() : ptr_(new mjVisualRgba) { + owned_ = true; +} +MjVisualRgba::MjVisualRgba(const MjVisualRgba &other) : MjVisualRgba() { + *ptr_ = *other.get(); +} +MjVisualRgba& MjVisualRgba::operator=(const MjVisualRgba &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjVisualRgba::~MjVisualRgba() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjVisualRgba::copy() { + return std::make_unique(*this); +} + +MjVisual::MjVisual(mjVisual *ptr) : ptr_(ptr), global(&ptr_->global), quality(&ptr_->quality), headlight(&ptr_->headlight), map(&ptr_->map), scale(&ptr_->scale), rgba(&ptr_->rgba) {} +MjVisual::MjVisual() : ptr_(new mjVisual), global(&ptr_->global), quality(&ptr_->quality), headlight(&ptr_->headlight), map(&ptr_->map), scale(&ptr_->scale), rgba(&ptr_->rgba) { + owned_ = true; + mj_defaultVisual(ptr_); +} +MjVisual::MjVisual(const MjVisual &other) : MjVisual() { + *ptr_ = *other.get(); + global.set(&ptr_->global); + quality.set(&ptr_->quality); + headlight.set(&ptr_->headlight); + map.set(&ptr_->map); + scale.set(&ptr_->scale); + rgba.set(&ptr_->rgba); +} +MjVisual& MjVisual::operator=(const MjVisual &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + global.set(&ptr_->global); + quality.set(&ptr_->quality); + headlight.set(&ptr_->headlight); + map.set(&ptr_->map); + scale.set(&ptr_->scale); + rgba.set(&ptr_->rgba); + return *this; +} +MjVisual::~MjVisual() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjVisual::copy() { + return std::make_unique(*this); +} + +// =============== MjSolverStat =============== // +MjSolverStat::MjSolverStat(mjSolverStat *ptr) : ptr_(ptr) {} +MjSolverStat::MjSolverStat() : ptr_(new mjSolverStat) { + owned_ = true; +} +MjSolverStat::MjSolverStat(const MjSolverStat &other) : MjSolverStat() { + *ptr_ = *other.get(); +} +MjSolverStat& MjSolverStat::operator=(const MjSolverStat &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjSolverStat::~MjSolverStat() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjSolverStat::copy() { + return std::make_unique(*this); +} + +// =============== MjTimerStat =============== // +MjTimerStat::MjTimerStat(mjTimerStat *ptr) : ptr_(ptr) {} +MjTimerStat::MjTimerStat() : ptr_(new mjTimerStat) { + owned_ = true; +} +MjTimerStat::MjTimerStat(const MjTimerStat &other) : MjTimerStat() { + *ptr_ = *other.get(); +} +MjTimerStat& MjTimerStat::operator=(const MjTimerStat &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjTimerStat::~MjTimerStat() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjTimerStat::copy() { + return std::make_unique(*this); +} + +// =============== MjWarningStat =============== // +MjWarningStat::MjWarningStat(mjWarningStat *ptr) : ptr_(ptr) {} +MjWarningStat::MjWarningStat() : ptr_(new mjWarningStat) { + owned_ = true; +} +MjWarningStat::MjWarningStat(const MjWarningStat &other) : MjWarningStat() { + *ptr_ = *other.get(); +} +MjWarningStat& MjWarningStat::operator=(const MjWarningStat &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjWarningStat::~MjWarningStat() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjWarningStat::copy() { + return std::make_unique(*this); +} + +// =============== MjContact =============== // +MjContact::MjContact(mjContact *ptr) : ptr_(ptr) {} +MjContact::MjContact() : ptr_(new mjContact) { + owned_ = true; +} +MjContact::MjContact(const MjContact &other) : MjContact() { + *ptr_ = *other.get(); +} +MjContact& MjContact::operator=(const MjContact &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjContact::~MjContact() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjContact::copy() { + return std::make_unique(*this); +} + +// =============== MjModel =============== // +MjModel::MjModel(mjModel *m) + : ptr_(m), opt(&m->opt), stat(&m->stat), vis(&m->vis) {} +MjModel::MjModel(const MjModel &other) + : ptr_(mj_copyModel(nullptr, other.get())), + opt(&ptr_->opt), + stat(&ptr_->stat), + vis(&ptr_->vis) {} +MjModel::~MjModel() { + if (ptr_) { + mj_deleteModel(ptr_); + } +} + +// TODO(manevi): Consider passing `const MjModel& m` here, mj_makeData uses a const model. +// =============== MjData =============== // +MjData::MjData(MjModel *m) { + model = m->get(); + ptr_ = mj_makeData(model); + if (ptr_) { + solver = InitSolverArray(); + timer = InitTimerArray(); + warning = InitWarningArray(); + } +} +MjData::MjData(const MjModel &model, const MjData &other) + : ptr_(mj_copyData(nullptr, model.get(), other.get())), model(model.get()) { + if (ptr_) { + solver = InitSolverArray(); + timer = InitTimerArray(); + warning = InitWarningArray(); + } +} +MjData::~MjData() { + if (ptr_) { + mj_deleteData(ptr_); + } +} +std::vector MjData::InitSolverArray() { + std::vector arr; + arr.reserve(mjNSOLVER * mjNISLAND); + for (int i = 0; i < mjNSOLVER * mjNISLAND; i++) { + arr.emplace_back(&get()->solver[i]); + } + return arr; +} +std::vector MjData::InitTimerArray() { + std::vector arr; + arr.reserve(mjNTIMER); + for (int i = 0; i < mjNTIMER; i++) { + arr.emplace_back(&get()->timer[i]); + } + return arr; +} +std::vector +MjData::InitWarningArray() { + std::vector arr; + arr.reserve(mjNWARNING); + for (int i = 0; i < mjNWARNING; i++) { + arr.emplace_back(&get()->warning[i]); + } + return arr; +} +std::vector MjData::contact() const { + std::vector contacts; + contacts.reserve(get()->ncon); + for (int i = 0; i < get()->ncon; ++i) { + contacts.emplace_back(&get()->contact[i]); + } + return contacts; +} +// =============== MjvPerturb =============== // +MjvPerturb::MjvPerturb(mjvPerturb *ptr) : ptr_(ptr) {} +MjvPerturb::MjvPerturb() : ptr_(new mjvPerturb) { + owned_ = true; + mjv_defaultPerturb(ptr_); +} +MjvPerturb::MjvPerturb(const MjvPerturb &other) : MjvPerturb() { + *ptr_ = *other.get(); +} +MjvPerturb& MjvPerturb::operator=(const MjvPerturb &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjvPerturb::~MjvPerturb() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjvPerturb::copy() { + return std::make_unique(*this); +} + +// =============== MjvCamera =============== // +MjvCamera::MjvCamera(mjvCamera *ptr) : ptr_(ptr) {} +MjvCamera::MjvCamera() : ptr_(new mjvCamera) { + owned_ = true; + mjv_defaultCamera(ptr_); +} +MjvCamera::MjvCamera(const MjvCamera &other) : MjvCamera() { + *ptr_ = *other.get(); +} +MjvCamera& MjvCamera::operator=(const MjvCamera &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjvCamera::~MjvCamera() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjvCamera::copy() { + return std::make_unique(*this); +} + +// =============== MjvGLCamera =============== // +MjvGLCamera::MjvGLCamera(mjvGLCamera *ptr) : ptr_(ptr) {} +MjvGLCamera::MjvGLCamera() : ptr_(new mjvGLCamera) { + owned_ = true; +} +MjvGLCamera::MjvGLCamera(const MjvGLCamera &other) : MjvGLCamera() { + *ptr_ = *other.get(); +} +MjvGLCamera& MjvGLCamera::operator=(const MjvGLCamera &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjvGLCamera::~MjvGLCamera() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjvGLCamera::copy() { + return std::make_unique(*this); +} + +// =============== MjvGeom =============== // +MjvGeom::MjvGeom(mjvGeom *ptr) { ptr_ = ptr; }; +MjvGeom::MjvGeom() : ptr_(new mjvGeom) { + owned_ = true; + mjv_initGeom(ptr_, mjGEOM_NONE, nullptr, nullptr, nullptr, nullptr); +}; +MjvGeom::MjvGeom(const MjvGeom &other) : MjvGeom() { + *ptr_ = *other.get(); +} +MjvGeom &MjvGeom::operator=( + const MjvGeom &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjvGeom::~MjvGeom() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjvGeom::copy() { + return std::make_unique(*this); +} + +// =============== MjvLight =============== // +MjvLight::MjvLight(mjvLight *ptr) : ptr_(ptr) {} +MjvLight::MjvLight() : ptr_(new mjvLight) { + owned_ = true; +} +MjvLight::MjvLight(const MjvLight &other) : MjvLight() { + *ptr_ = *other.get(); +} +MjvLight& MjvLight::operator=(const MjvLight &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjvLight::~MjvLight() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjvLight::copy() { + return std::make_unique(*this); +} + +// =============== MjvOption =============== // +MjvOption::MjvOption(mjvOption *ptr) : ptr_(ptr) {} +MjvOption::MjvOption() : ptr_(new mjvOption) { + owned_ = true; + mjv_defaultOption(ptr_); +} +MjvOption::MjvOption(const MjvOption &other) : MjvOption() { + *ptr_ = *other.get(); +} +MjvOption& MjvOption::operator=(const MjvOption &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjvOption::~MjvOption() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjvOption::copy() { + return std::make_unique(*this); +} + +// =============== MjvScene =============== // +MjvScene::MjvScene() { + owned_ = true; + ptr_ = new mjvScene; + mjv_defaultScene(ptr_); + mjv_makeScene(nullptr, ptr_, 0); + lights = InitLightsArray(); + camera = InitCameraArray(); +}; + +MjvScene::MjvScene(MjModel *m, int maxgeom) { + owned_ = true; + model = m->get(); + ptr_ = new mjvScene; + mjv_defaultScene(ptr_); + mjv_makeScene(model, ptr_, maxgeom); + lights = InitLightsArray(); + camera = InitCameraArray(); +}; +MjvScene::~MjvScene() { + if (owned_ && ptr_) { + mjv_freeScene(ptr_); + delete ptr_; + } +} + +// Taken from the python mujoco bindings code for MjvScene Wrapper +int MjvScene::GetSumFlexFaces() const { + int nflexface = 0; + int flexfacenum = 0; + for (int f = 0; f < model->nflex; f++) { + if (model->flex_dim[f] == 0) { + // 1D : 0 + flexfacenum = 0; + } else if (model->flex_dim[f] == 2) { + // 2D: 2*fragments + 2*elements + flexfacenum = 2 * model->flex_shellnum[f] + 2 * model->flex_elemnum[f]; + } else { + // 3D: max(fragments, 4*maxlayer) + // find number of elements in biggest layer + int maxlayer = 0, layer = 0, nlayer = 1; + while (nlayer) { + nlayer = 0; + for (int e = 0; e < model->flex_elemnum[f]; e++) { + if (model->flex_elemlayer[model->flex_elemadr[f] + e] == layer) { + nlayer++; + } + } + maxlayer = mjMAX(maxlayer, nlayer); + layer++; + } + flexfacenum = mjMAX(model->flex_shellnum[f], 4 * maxlayer); + } + + // accumulate over flexes + nflexface += flexfacenum; + } + return nflexface; +} + +std::vector MjvScene::InitLightsArray() { + std::vector arr; + arr.reserve(mjMAXLIGHT); + for (int i = 0; i < mjMAXLIGHT; i++) { + arr.emplace_back(&ptr_->lights[i]); + } + return arr; +} + +std::vector MjvScene::InitCameraArray() { + std::vector arr; + arr.reserve(2); + for (int i = 0; i < 2; i++) { + arr.emplace_back(&ptr_->camera[i]); + } + return arr; +} + +std::vector MjvScene::geoms() const { + std::vector geoms; + geoms.reserve(ptr_->ngeom); + for (int i = 0; i < ptr_->ngeom; ++i) { + geoms.emplace_back(&ptr_->geoms[i]); + } + return geoms; +} + +// =============== MjvFigure =============== // +MjvFigure::MjvFigure(mjvFigure *ptr) : ptr_(ptr) {} +MjvFigure::MjvFigure() : ptr_(new mjvFigure) { + owned_ = true; + mjv_defaultFigure(ptr_); +} +MjvFigure::MjvFigure(const MjvFigure &other) : MjvFigure() { + *ptr_ = *other.get(); +} +MjvFigure& MjvFigure::operator=(const MjvFigure &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjvFigure::~MjvFigure() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjvFigure::copy() { + return std::make_unique(*this); +} + +// =============== MjsElement =============== // +MjsElement::MjsElement(mjsElement *ptr) : ptr_(ptr) {} +MjsElement::~MjsElement() {} +std::unique_ptr MjsElement::copy() { + return std::make_unique(*this); +} + +// =============== MjsCompiler =============== // +MjsCompiler::MjsCompiler(mjsCompiler *ptr) : ptr_(ptr), LRopt(&ptr_->LRopt) {} +MjsCompiler::~MjsCompiler() {} + +// =============== MjSpec =============== // +MjSpec::MjSpec() + : ptr_(mj_makeSpec()), + option(&ptr_->option), + visual(&ptr_->visual), + stat(&ptr_->stat), + compiler(&ptr_->compiler), + element(ptr_->element) { + owned_ = true; + mjs_defaultSpec(ptr_); +}; + +MjSpec::MjSpec(mjSpec *ptr) + : ptr_(ptr), + option(&ptr_->option), + visual(&ptr_->visual), + stat(&ptr_->stat), + compiler(&ptr_->compiler), + element(ptr_->element) {} + +MjSpec::MjSpec(const MjSpec &other) + : ptr_(mj_copySpec(other.get())), + option(&ptr_->option), + visual(&ptr_->visual), + stat(&ptr_->stat), + compiler(&ptr_->compiler), + element(ptr_->element) { + owned_ = true; +} + +MjSpec& MjSpec::operator=(const MjSpec &other) { + if (this == &other) { + return *this; + } + if (owned_ && ptr_) { + mj_deleteSpec(ptr_); + } + ptr_ = mj_copySpec(other.get()); + owned_ = true; + option.set(&ptr_->option); + visual.set(&ptr_->visual); + stat.set(&ptr_->stat); + compiler.set(&ptr_->compiler); + element.set(ptr_->element); + return *this; +} + +MjSpec::~MjSpec() { + if (ptr_ && owned_) { + mj_deleteSpec(ptr_); + } +} + +// =============== MjsOrientation =============== // +MjsOrientation::MjsOrientation(mjsOrientation *ptr) : ptr_(ptr) {} +MjsOrientation::~MjsOrientation() {} +std::unique_ptr MjsOrientation::copy() { + return std::make_unique(*this); +} + +// =============== MjsBody =============== // +MjsBody::MjsBody(mjsBody *ptr) : ptr_(ptr), element(ptr_->element), alt(&ptr_->alt), ialt(&ptr_->ialt), plugin(&ptr_->plugin) {} +MjsBody::~MjsBody() {} + +// =============== MjsGeom =============== // +MjsGeom::MjsGeom(mjsGeom *ptr) : ptr_(ptr), element(ptr_->element), alt(&ptr_->alt), plugin(&ptr_->plugin) {} +MjsGeom::~MjsGeom() {} + +// =============== MjsFrame =============== // +MjsFrame::MjsFrame(mjsFrame *ptr) : ptr_(ptr), element(ptr_->element), alt(&ptr_->alt) {} +MjsFrame::~MjsFrame() {} + +// =============== MjsJoint =============== // +MjsJoint::MjsJoint(mjsJoint *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsJoint::~MjsJoint() {} + +// =============== MjsSite =============== // +MjsSite::MjsSite(mjsSite *ptr) : ptr_(ptr), element(ptr_->element), alt(&ptr_->alt) {} +MjsSite::~MjsSite() {} + +// =============== MjsCamera =============== // +MjsCamera::MjsCamera(mjsCamera *ptr) : ptr_(ptr), element(ptr_->element), alt(&ptr_->alt) {} +MjsCamera::~MjsCamera() {} + +// =============== MjsLight =============== // +MjsLight::MjsLight(mjsLight *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsLight::~MjsLight() {} + +// =============== MjsFlex =============== // +MjsFlex::MjsFlex(mjsFlex *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsFlex::~MjsFlex() {} + +// =============== MjsMesh =============== // +MjsMesh::MjsMesh(mjsMesh *ptr) : ptr_(ptr), element(ptr_->element), plugin(&ptr_->plugin) {} +MjsMesh::~MjsMesh() {} + +// =============== MjsHField =============== // +MjsHField::MjsHField(mjsHField *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsHField::~MjsHField() {} + +// =============== MjsSkin =============== // +MjsSkin::MjsSkin(mjsSkin *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsSkin::~MjsSkin() {} + +// =============== MjsTexture =============== // +MjsTexture::MjsTexture(mjsTexture *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsTexture::~MjsTexture() {} + +// =============== MjsMaterial =============== // +MjsMaterial::MjsMaterial(mjsMaterial *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsMaterial::~MjsMaterial() {} + +// =============== MjsPair =============== // +MjsPair::MjsPair(mjsPair *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsPair::~MjsPair() {} + +// =============== MjsExclude =============== // +MjsExclude::MjsExclude(mjsExclude *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsExclude::~MjsExclude() {} + +// =============== MjsEquality =============== // +MjsEquality::MjsEquality(mjsEquality *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsEquality::~MjsEquality() {} + +// =============== MjsTendon =============== // +MjsTendon::MjsTendon(mjsTendon *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsTendon::~MjsTendon() {} + +// =============== MjsWrap =============== // +MjsWrap::MjsWrap(mjsWrap *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsWrap::~MjsWrap() {} + +// =============== MjsActuator =============== // +MjsActuator::MjsActuator(mjsActuator *ptr) : ptr_(ptr), element(ptr_->element), plugin(&ptr_->plugin) {} +MjsActuator::~MjsActuator() {} + +// =============== MjsSensor =============== // +MjsSensor::MjsSensor(mjsSensor *ptr) : ptr_(ptr), element(ptr_->element), plugin(&ptr_->plugin) {} +MjsSensor::~MjsSensor() {} + +// =============== MjsNumeric =============== // +MjsNumeric::MjsNumeric(mjsNumeric *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsNumeric::~MjsNumeric() {} + +// =============== MjsText =============== // +MjsText::MjsText(mjsText *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsText::~MjsText() {} + +// =============== MjsTuple =============== // +MjsTuple::MjsTuple(mjsTuple *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsTuple::~MjsTuple() {} + +// =============== MjsKey =============== // +MjsKey::MjsKey(mjsKey *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsKey::~MjsKey() {} + +// =============== MjsDefault =============== // +MjsDefault::MjsDefault(mjsDefault *ptr) : ptr_(ptr), element(ptr_->element), joint(ptr_->joint), geom(ptr_->geom), site(ptr_->site), camera(ptr_->camera), light(ptr_->light), flex(ptr_->flex), mesh(ptr_->mesh), material(ptr_->material), pair(ptr_->pair), equality(ptr_->equality), tendon(ptr_->tendon), actuator(ptr_->actuator) {} +MjsDefault::~MjsDefault() {} + +// =============== MjsPlugin =============== // +MjsPlugin::MjsPlugin(mjsPlugin *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsPlugin::~MjsPlugin() {} + +// =============== MjVFS =============== // +MjVFS::MjVFS(mjVFS *ptr) : ptr_(ptr) {} +MjVFS::MjVFS() : ptr_(new mjVFS) { + owned_ = true; + mj_defaultVFS(ptr_); +} +MjVFS::~MjVFS() { + if (owned_ && ptr_) { + mj_deleteVFS(ptr_); + } +} + +// ======= FACTORY AND HELPER FUNCTIONS ========= // +std::unique_ptr loadFromXML(std::string filename) { + char error[1000]; + mjModel *model = mj_loadXML(filename.c_str(), nullptr, error, sizeof(error)); + if (!model) { + printf("Loading error: %s\n", error); + return nullptr; + } + return std::unique_ptr(new MjModel(model)); +} + +std::unique_ptr parseXMLString(const std::string &xml) { + char error[1000]; + mjSpec *ptr = mj_parseXMLString(xml.c_str(), nullptr, error, sizeof(error)); + if (!ptr) { + printf("Could not create Spec from XML string: %s\n", error); + return nullptr; + } + return std::unique_ptr(new MjSpec(ptr)); +} + +EMSCRIPTEN_BINDINGS(mujoco_bindings) { + function("parseXMLString", &parseXMLString, take_ownership()); + + emscripten::class_("MjLROpt") + .constructor<>() + .function("copy", &MjLROpt::copy, take_ownership()) + .property("mode", &MjLROpt::mode, &MjLROpt::set_mode, reference()) + .property("useexisting", &MjLROpt::useexisting, &MjLROpt::set_useexisting, reference()) + .property("uselimit", &MjLROpt::uselimit, &MjLROpt::set_uselimit, reference()) + .property("accel", &MjLROpt::accel, &MjLROpt::set_accel, reference()) + .property("maxforce", &MjLROpt::maxforce, &MjLROpt::set_maxforce, reference()) + .property("timeconst", &MjLROpt::timeconst, &MjLROpt::set_timeconst, reference()) + .property("timestep", &MjLROpt::timestep, &MjLROpt::set_timestep, reference()) + .property("inttotal", &MjLROpt::inttotal, &MjLROpt::set_inttotal, reference()) + .property("interval", &MjLROpt::interval, &MjLROpt::set_interval, reference()) + .property("tolrange", &MjLROpt::tolrange, &MjLROpt::set_tolrange, reference()) + ; + emscripten::class_("MjModel") + .class_function("loadFromXML", &loadFromXML, take_ownership()) + .constructor() + .property("nq", &MjModel::nq, &MjModel::set_nq, reference()) + .property("nv", &MjModel::nv, &MjModel::set_nv, reference()) + .property("nu", &MjModel::nu, &MjModel::set_nu, reference()) + .property("na", &MjModel::na, &MjModel::set_na, reference()) + .property("nbody", &MjModel::nbody, &MjModel::set_nbody, reference()) + .property("nbvh", &MjModel::nbvh, &MjModel::set_nbvh, reference()) + .property("nbvhstatic", &MjModel::nbvhstatic, &MjModel::set_nbvhstatic, reference()) + .property("nbvhdynamic", &MjModel::nbvhdynamic, &MjModel::set_nbvhdynamic, reference()) + .property("noct", &MjModel::noct, &MjModel::set_noct, reference()) + .property("njnt", &MjModel::njnt, &MjModel::set_njnt, reference()) + .property("ntree", &MjModel::ntree, &MjModel::set_ntree, reference()) + .property("nM", &MjModel::nM, &MjModel::set_nM, reference()) + .property("nB", &MjModel::nB, &MjModel::set_nB, reference()) + .property("nC", &MjModel::nC, &MjModel::set_nC, reference()) + .property("nD", &MjModel::nD, &MjModel::set_nD, reference()) + .property("ngeom", &MjModel::ngeom, &MjModel::set_ngeom, reference()) + .property("nsite", &MjModel::nsite, &MjModel::set_nsite, reference()) + .property("ncam", &MjModel::ncam, &MjModel::set_ncam, reference()) + .property("nlight", &MjModel::nlight, &MjModel::set_nlight, reference()) + .property("nflex", &MjModel::nflex, &MjModel::set_nflex, reference()) + .property("nflexnode", &MjModel::nflexnode, &MjModel::set_nflexnode, reference()) + .property("nflexvert", &MjModel::nflexvert, &MjModel::set_nflexvert, reference()) + .property("nflexedge", &MjModel::nflexedge, &MjModel::set_nflexedge, reference()) + .property("nflexelem", &MjModel::nflexelem, &MjModel::set_nflexelem, reference()) + .property("nflexelemdata", &MjModel::nflexelemdata, &MjModel::set_nflexelemdata, reference()) + .property("nflexelemedge", &MjModel::nflexelemedge, &MjModel::set_nflexelemedge, reference()) + .property("nflexshelldata", &MjModel::nflexshelldata, &MjModel::set_nflexshelldata, reference()) + .property("nflexevpair", &MjModel::nflexevpair, &MjModel::set_nflexevpair, reference()) + .property("nflextexcoord", &MjModel::nflextexcoord, &MjModel::set_nflextexcoord, reference()) + .property("nmesh", &MjModel::nmesh, &MjModel::set_nmesh, reference()) + .property("nmeshvert", &MjModel::nmeshvert, &MjModel::set_nmeshvert, reference()) + .property("nmeshnormal", &MjModel::nmeshnormal, &MjModel::set_nmeshnormal, reference()) + .property("nmeshtexcoord", &MjModel::nmeshtexcoord, &MjModel::set_nmeshtexcoord, reference()) + .property("nmeshface", &MjModel::nmeshface, &MjModel::set_nmeshface, reference()) + .property("nmeshgraph", &MjModel::nmeshgraph, &MjModel::set_nmeshgraph, reference()) + .property("nmeshpoly", &MjModel::nmeshpoly, &MjModel::set_nmeshpoly, reference()) + .property("nmeshpolyvert", &MjModel::nmeshpolyvert, &MjModel::set_nmeshpolyvert, reference()) + .property("nmeshpolymap", &MjModel::nmeshpolymap, &MjModel::set_nmeshpolymap, reference()) + .property("nskin", &MjModel::nskin, &MjModel::set_nskin, reference()) + .property("nskinvert", &MjModel::nskinvert, &MjModel::set_nskinvert, reference()) + .property("nskintexvert", &MjModel::nskintexvert, &MjModel::set_nskintexvert, reference()) + .property("nskinface", &MjModel::nskinface, &MjModel::set_nskinface, reference()) + .property("nskinbone", &MjModel::nskinbone, &MjModel::set_nskinbone, reference()) + .property("nskinbonevert", &MjModel::nskinbonevert, &MjModel::set_nskinbonevert, reference()) + .property("nhfield", &MjModel::nhfield, &MjModel::set_nhfield, reference()) + .property("nhfielddata", &MjModel::nhfielddata, &MjModel::set_nhfielddata, reference()) + .property("ntex", &MjModel::ntex, &MjModel::set_ntex, reference()) + .property("ntexdata", &MjModel::ntexdata, &MjModel::set_ntexdata, reference()) + .property("nmat", &MjModel::nmat, &MjModel::set_nmat, reference()) + .property("npair", &MjModel::npair, &MjModel::set_npair, reference()) + .property("nexclude", &MjModel::nexclude, &MjModel::set_nexclude, reference()) + .property("neq", &MjModel::neq, &MjModel::set_neq, reference()) + .property("ntendon", &MjModel::ntendon, &MjModel::set_ntendon, reference()) + .property("nwrap", &MjModel::nwrap, &MjModel::set_nwrap, reference()) + .property("nsensor", &MjModel::nsensor, &MjModel::set_nsensor, reference()) + .property("nnumeric", &MjModel::nnumeric, &MjModel::set_nnumeric, reference()) + .property("nnumericdata", &MjModel::nnumericdata, &MjModel::set_nnumericdata, reference()) + .property("ntext", &MjModel::ntext, &MjModel::set_ntext, reference()) + .property("ntextdata", &MjModel::ntextdata, &MjModel::set_ntextdata, reference()) + .property("ntuple", &MjModel::ntuple, &MjModel::set_ntuple, reference()) + .property("ntupledata", &MjModel::ntupledata, &MjModel::set_ntupledata, reference()) + .property("nkey", &MjModel::nkey, &MjModel::set_nkey, reference()) + .property("nmocap", &MjModel::nmocap, &MjModel::set_nmocap, reference()) + .property("nplugin", &MjModel::nplugin, &MjModel::set_nplugin, reference()) + .property("npluginattr", &MjModel::npluginattr, &MjModel::set_npluginattr, reference()) + .property("nuser_body", &MjModel::nuser_body, &MjModel::set_nuser_body, reference()) + .property("nuser_jnt", &MjModel::nuser_jnt, &MjModel::set_nuser_jnt, reference()) + .property("nuser_geom", &MjModel::nuser_geom, &MjModel::set_nuser_geom, reference()) + .property("nuser_site", &MjModel::nuser_site, &MjModel::set_nuser_site, reference()) + .property("nuser_cam", &MjModel::nuser_cam, &MjModel::set_nuser_cam, reference()) + .property("nuser_tendon", &MjModel::nuser_tendon, &MjModel::set_nuser_tendon, reference()) + .property("nuser_actuator", &MjModel::nuser_actuator, &MjModel::set_nuser_actuator, reference()) + .property("nuser_sensor", &MjModel::nuser_sensor, &MjModel::set_nuser_sensor, reference()) + .property("nnames", &MjModel::nnames, &MjModel::set_nnames, reference()) + .property("npaths", &MjModel::npaths, &MjModel::set_npaths, reference()) + .property("nnames_map", &MjModel::nnames_map, &MjModel::set_nnames_map, reference()) + .property("nJmom", &MjModel::nJmom, &MjModel::set_nJmom, reference()) + .property("ngravcomp", &MjModel::ngravcomp, &MjModel::set_ngravcomp, reference()) + .property("nemax", &MjModel::nemax, &MjModel::set_nemax, reference()) + .property("njmax", &MjModel::njmax, &MjModel::set_njmax, reference()) + .property("nconmax", &MjModel::nconmax, &MjModel::set_nconmax, reference()) + .property("nuserdata", &MjModel::nuserdata, &MjModel::set_nuserdata, reference()) + .property("nsensordata", &MjModel::nsensordata, &MjModel::set_nsensordata, reference()) + .property("npluginstate", &MjModel::npluginstate, &MjModel::set_npluginstate, reference()) + .property("narena", &MjModel::narena, &MjModel::set_narena, reference()) + .property("nbuffer", &MjModel::nbuffer, &MjModel::set_nbuffer, reference()) + .property("opt", &MjModel::opt, reference()) + .property("vis", &MjModel::vis, reference()) + .property("stat", &MjModel::stat, reference()) + .property("buffer", &MjModel::buffer) + .property("qpos0", &MjModel::qpos0) + .property("qpos_spring", &MjModel::qpos_spring) + .property("body_parentid", &MjModel::body_parentid) + .property("body_rootid", &MjModel::body_rootid) + .property("body_weldid", &MjModel::body_weldid) + .property("body_mocapid", &MjModel::body_mocapid) + .property("body_jntnum", &MjModel::body_jntnum) + .property("body_jntadr", &MjModel::body_jntadr) + .property("body_dofnum", &MjModel::body_dofnum) + .property("body_dofadr", &MjModel::body_dofadr) + .property("body_treeid", &MjModel::body_treeid) + .property("body_geomnum", &MjModel::body_geomnum) + .property("body_geomadr", &MjModel::body_geomadr) + .property("body_simple", &MjModel::body_simple) + .property("body_sameframe", &MjModel::body_sameframe) + .property("body_pos", &MjModel::body_pos) + .property("body_quat", &MjModel::body_quat) + .property("body_ipos", &MjModel::body_ipos) + .property("body_iquat", &MjModel::body_iquat) + .property("body_mass", &MjModel::body_mass) + .property("body_subtreemass", &MjModel::body_subtreemass) + .property("body_inertia", &MjModel::body_inertia) + .property("body_invweight0", &MjModel::body_invweight0) + .property("body_gravcomp", &MjModel::body_gravcomp) + .property("body_margin", &MjModel::body_margin) + .property("body_user", &MjModel::body_user) + .property("body_plugin", &MjModel::body_plugin) + .property("body_contype", &MjModel::body_contype) + .property("body_conaffinity", &MjModel::body_conaffinity) + .property("body_bvhadr", &MjModel::body_bvhadr) + .property("body_bvhnum", &MjModel::body_bvhnum) + .property("bvh_depth", &MjModel::bvh_depth) + .property("bvh_child", &MjModel::bvh_child) + .property("bvh_nodeid", &MjModel::bvh_nodeid) + .property("bvh_aabb", &MjModel::bvh_aabb) + .property("oct_depth", &MjModel::oct_depth) + .property("oct_child", &MjModel::oct_child) + .property("oct_aabb", &MjModel::oct_aabb) + .property("oct_coeff", &MjModel::oct_coeff) + .property("jnt_type", &MjModel::jnt_type) + .property("jnt_qposadr", &MjModel::jnt_qposadr) + .property("jnt_dofadr", &MjModel::jnt_dofadr) + .property("jnt_bodyid", &MjModel::jnt_bodyid) + .property("jnt_group", &MjModel::jnt_group) + .property("jnt_limited", &MjModel::jnt_limited) + .property("jnt_actfrclimited", &MjModel::jnt_actfrclimited) + .property("jnt_actgravcomp", &MjModel::jnt_actgravcomp) + .property("jnt_solref", &MjModel::jnt_solref) + .property("jnt_solimp", &MjModel::jnt_solimp) + .property("jnt_pos", &MjModel::jnt_pos) + .property("jnt_axis", &MjModel::jnt_axis) + .property("jnt_stiffness", &MjModel::jnt_stiffness) + .property("jnt_range", &MjModel::jnt_range) + .property("jnt_actfrcrange", &MjModel::jnt_actfrcrange) + .property("jnt_margin", &MjModel::jnt_margin) + .property("jnt_user", &MjModel::jnt_user) + .property("dof_bodyid", &MjModel::dof_bodyid) + .property("dof_jntid", &MjModel::dof_jntid) + .property("dof_parentid", &MjModel::dof_parentid) + .property("dof_treeid", &MjModel::dof_treeid) + .property("dof_Madr", &MjModel::dof_Madr) + .property("dof_simplenum", &MjModel::dof_simplenum) + .property("dof_solref", &MjModel::dof_solref) + .property("dof_solimp", &MjModel::dof_solimp) + .property("dof_frictionloss", &MjModel::dof_frictionloss) + .property("dof_armature", &MjModel::dof_armature) + .property("dof_damping", &MjModel::dof_damping) + .property("dof_invweight0", &MjModel::dof_invweight0) + .property("dof_M0", &MjModel::dof_M0) + .property("geom_type", &MjModel::geom_type) + .property("geom_contype", &MjModel::geom_contype) + .property("geom_conaffinity", &MjModel::geom_conaffinity) + .property("geom_condim", &MjModel::geom_condim) + .property("geom_bodyid", &MjModel::geom_bodyid) + .property("geom_dataid", &MjModel::geom_dataid) + .property("geom_matid", &MjModel::geom_matid) + .property("geom_group", &MjModel::geom_group) + .property("geom_priority", &MjModel::geom_priority) + .property("geom_plugin", &MjModel::geom_plugin) + .property("geom_sameframe", &MjModel::geom_sameframe) + .property("geom_solmix", &MjModel::geom_solmix) + .property("geom_solref", &MjModel::geom_solref) + .property("geom_solimp", &MjModel::geom_solimp) + .property("geom_size", &MjModel::geom_size) + .property("geom_aabb", &MjModel::geom_aabb) + .property("geom_rbound", &MjModel::geom_rbound) + .property("geom_pos", &MjModel::geom_pos) + .property("geom_quat", &MjModel::geom_quat) + .property("geom_friction", &MjModel::geom_friction) + .property("geom_margin", &MjModel::geom_margin) + .property("geom_gap", &MjModel::geom_gap) + .property("geom_fluid", &MjModel::geom_fluid) + .property("geom_user", &MjModel::geom_user) + .property("geom_rgba", &MjModel::geom_rgba) + .property("site_type", &MjModel::site_type) + .property("site_bodyid", &MjModel::site_bodyid) + .property("site_matid", &MjModel::site_matid) + .property("site_group", &MjModel::site_group) + .property("site_sameframe", &MjModel::site_sameframe) + .property("site_size", &MjModel::site_size) + .property("site_pos", &MjModel::site_pos) + .property("site_quat", &MjModel::site_quat) + .property("site_user", &MjModel::site_user) + .property("site_rgba", &MjModel::site_rgba) + .property("cam_mode", &MjModel::cam_mode) + .property("cam_bodyid", &MjModel::cam_bodyid) + .property("cam_targetbodyid", &MjModel::cam_targetbodyid) + .property("cam_pos", &MjModel::cam_pos) + .property("cam_quat", &MjModel::cam_quat) + .property("cam_poscom0", &MjModel::cam_poscom0) + .property("cam_pos0", &MjModel::cam_pos0) + .property("cam_mat0", &MjModel::cam_mat0) + .property("cam_orthographic", &MjModel::cam_orthographic) + .property("cam_fovy", &MjModel::cam_fovy) + .property("cam_ipd", &MjModel::cam_ipd) + .property("cam_resolution", &MjModel::cam_resolution) + .property("cam_sensorsize", &MjModel::cam_sensorsize) + .property("cam_intrinsic", &MjModel::cam_intrinsic) + .property("cam_user", &MjModel::cam_user) + .property("light_mode", &MjModel::light_mode) + .property("light_bodyid", &MjModel::light_bodyid) + .property("light_targetbodyid", &MjModel::light_targetbodyid) + .property("light_type", &MjModel::light_type) + .property("light_texid", &MjModel::light_texid) + .property("light_castshadow", &MjModel::light_castshadow) + .property("light_bulbradius", &MjModel::light_bulbradius) + .property("light_intensity", &MjModel::light_intensity) + .property("light_range", &MjModel::light_range) + .property("light_active", &MjModel::light_active) + .property("light_pos", &MjModel::light_pos) + .property("light_dir", &MjModel::light_dir) + .property("light_poscom0", &MjModel::light_poscom0) + .property("light_pos0", &MjModel::light_pos0) + .property("light_dir0", &MjModel::light_dir0) + .property("light_attenuation", &MjModel::light_attenuation) + .property("light_cutoff", &MjModel::light_cutoff) + .property("light_exponent", &MjModel::light_exponent) + .property("light_ambient", &MjModel::light_ambient) + .property("light_diffuse", &MjModel::light_diffuse) + .property("light_specular", &MjModel::light_specular) + .property("flex_contype", &MjModel::flex_contype) + .property("flex_conaffinity", &MjModel::flex_conaffinity) + .property("flex_condim", &MjModel::flex_condim) + .property("flex_priority", &MjModel::flex_priority) + .property("flex_solmix", &MjModel::flex_solmix) + .property("flex_solref", &MjModel::flex_solref) + .property("flex_solimp", &MjModel::flex_solimp) + .property("flex_friction", &MjModel::flex_friction) + .property("flex_margin", &MjModel::flex_margin) + .property("flex_gap", &MjModel::flex_gap) + .property("flex_internal", &MjModel::flex_internal) + .property("flex_selfcollide", &MjModel::flex_selfcollide) + .property("flex_activelayers", &MjModel::flex_activelayers) + .property("flex_passive", &MjModel::flex_passive) + .property("flex_dim", &MjModel::flex_dim) + .property("flex_matid", &MjModel::flex_matid) + .property("flex_group", &MjModel::flex_group) + .property("flex_interp", &MjModel::flex_interp) + .property("flex_nodeadr", &MjModel::flex_nodeadr) + .property("flex_nodenum", &MjModel::flex_nodenum) + .property("flex_vertadr", &MjModel::flex_vertadr) + .property("flex_vertnum", &MjModel::flex_vertnum) + .property("flex_edgeadr", &MjModel::flex_edgeadr) + .property("flex_edgenum", &MjModel::flex_edgenum) + .property("flex_elemadr", &MjModel::flex_elemadr) + .property("flex_elemnum", &MjModel::flex_elemnum) + .property("flex_elemdataadr", &MjModel::flex_elemdataadr) + .property("flex_elemedgeadr", &MjModel::flex_elemedgeadr) + .property("flex_shellnum", &MjModel::flex_shellnum) + .property("flex_shelldataadr", &MjModel::flex_shelldataadr) + .property("flex_evpairadr", &MjModel::flex_evpairadr) + .property("flex_evpairnum", &MjModel::flex_evpairnum) + .property("flex_texcoordadr", &MjModel::flex_texcoordadr) + .property("flex_nodebodyid", &MjModel::flex_nodebodyid) + .property("flex_vertbodyid", &MjModel::flex_vertbodyid) + .property("flex_edge", &MjModel::flex_edge) + .property("flex_edgeflap", &MjModel::flex_edgeflap) + .property("flex_elem", &MjModel::flex_elem) + .property("flex_elemtexcoord", &MjModel::flex_elemtexcoord) + .property("flex_elemedge", &MjModel::flex_elemedge) + .property("flex_elemlayer", &MjModel::flex_elemlayer) + .property("flex_shell", &MjModel::flex_shell) + .property("flex_evpair", &MjModel::flex_evpair) + .property("flex_vert", &MjModel::flex_vert) + .property("flex_vert0", &MjModel::flex_vert0) + .property("flex_node", &MjModel::flex_node) + .property("flex_node0", &MjModel::flex_node0) + .property("flexedge_length0", &MjModel::flexedge_length0) + .property("flexedge_invweight0", &MjModel::flexedge_invweight0) + .property("flex_radius", &MjModel::flex_radius) + .property("flex_stiffness", &MjModel::flex_stiffness) + .property("flex_bending", &MjModel::flex_bending) + .property("flex_damping", &MjModel::flex_damping) + .property("flex_edgestiffness", &MjModel::flex_edgestiffness) + .property("flex_edgedamping", &MjModel::flex_edgedamping) + .property("flex_edgeequality", &MjModel::flex_edgeequality) + .property("flex_rigid", &MjModel::flex_rigid) + .property("flexedge_rigid", &MjModel::flexedge_rigid) + .property("flex_centered", &MjModel::flex_centered) + .property("flex_flatskin", &MjModel::flex_flatskin) + .property("flex_bvhadr", &MjModel::flex_bvhadr) + .property("flex_bvhnum", &MjModel::flex_bvhnum) + .property("flex_rgba", &MjModel::flex_rgba) + .property("flex_texcoord", &MjModel::flex_texcoord) + .property("mesh_vertadr", &MjModel::mesh_vertadr) + .property("mesh_vertnum", &MjModel::mesh_vertnum) + .property("mesh_faceadr", &MjModel::mesh_faceadr) + .property("mesh_facenum", &MjModel::mesh_facenum) + .property("mesh_bvhadr", &MjModel::mesh_bvhadr) + .property("mesh_bvhnum", &MjModel::mesh_bvhnum) + .property("mesh_octadr", &MjModel::mesh_octadr) + .property("mesh_octnum", &MjModel::mesh_octnum) + .property("mesh_normaladr", &MjModel::mesh_normaladr) + .property("mesh_normalnum", &MjModel::mesh_normalnum) + .property("mesh_texcoordadr", &MjModel::mesh_texcoordadr) + .property("mesh_texcoordnum", &MjModel::mesh_texcoordnum) + .property("mesh_graphadr", &MjModel::mesh_graphadr) + .property("mesh_vert", &MjModel::mesh_vert) + .property("mesh_normal", &MjModel::mesh_normal) + .property("mesh_texcoord", &MjModel::mesh_texcoord) + .property("mesh_face", &MjModel::mesh_face) + .property("mesh_facenormal", &MjModel::mesh_facenormal) + .property("mesh_facetexcoord", &MjModel::mesh_facetexcoord) + .property("mesh_graph", &MjModel::mesh_graph) + .property("mesh_scale", &MjModel::mesh_scale) + .property("mesh_pos", &MjModel::mesh_pos) + .property("mesh_quat", &MjModel::mesh_quat) + .property("mesh_pathadr", &MjModel::mesh_pathadr) + .property("mesh_polynum", &MjModel::mesh_polynum) + .property("mesh_polyadr", &MjModel::mesh_polyadr) + .property("mesh_polynormal", &MjModel::mesh_polynormal) + .property("mesh_polyvertadr", &MjModel::mesh_polyvertadr) + .property("mesh_polyvertnum", &MjModel::mesh_polyvertnum) + .property("mesh_polyvert", &MjModel::mesh_polyvert) + .property("mesh_polymapadr", &MjModel::mesh_polymapadr) + .property("mesh_polymapnum", &MjModel::mesh_polymapnum) + .property("mesh_polymap", &MjModel::mesh_polymap) + .property("skin_matid", &MjModel::skin_matid) + .property("skin_group", &MjModel::skin_group) + .property("skin_rgba", &MjModel::skin_rgba) + .property("skin_inflate", &MjModel::skin_inflate) + .property("skin_vertadr", &MjModel::skin_vertadr) + .property("skin_vertnum", &MjModel::skin_vertnum) + .property("skin_texcoordadr", &MjModel::skin_texcoordadr) + .property("skin_faceadr", &MjModel::skin_faceadr) + .property("skin_facenum", &MjModel::skin_facenum) + .property("skin_boneadr", &MjModel::skin_boneadr) + .property("skin_bonenum", &MjModel::skin_bonenum) + .property("skin_vert", &MjModel::skin_vert) + .property("skin_texcoord", &MjModel::skin_texcoord) + .property("skin_face", &MjModel::skin_face) + .property("skin_bonevertadr", &MjModel::skin_bonevertadr) + .property("skin_bonevertnum", &MjModel::skin_bonevertnum) + .property("skin_bonebindpos", &MjModel::skin_bonebindpos) + .property("skin_bonebindquat", &MjModel::skin_bonebindquat) + .property("skin_bonebodyid", &MjModel::skin_bonebodyid) + .property("skin_bonevertid", &MjModel::skin_bonevertid) + .property("skin_bonevertweight", &MjModel::skin_bonevertweight) + .property("skin_pathadr", &MjModel::skin_pathadr) + .property("hfield_size", &MjModel::hfield_size) + .property("hfield_nrow", &MjModel::hfield_nrow) + .property("hfield_ncol", &MjModel::hfield_ncol) + .property("hfield_adr", &MjModel::hfield_adr) + .property("hfield_data", &MjModel::hfield_data) + .property("hfield_pathadr", &MjModel::hfield_pathadr) + .property("tex_type", &MjModel::tex_type) + .property("tex_colorspace", &MjModel::tex_colorspace) + .property("tex_height", &MjModel::tex_height) + .property("tex_width", &MjModel::tex_width) + .property("tex_nchannel", &MjModel::tex_nchannel) + .property("tex_adr", &MjModel::tex_adr) + .property("tex_data", &MjModel::tex_data) + .property("tex_pathadr", &MjModel::tex_pathadr) + .property("mat_texid", &MjModel::mat_texid) + .property("mat_texuniform", &MjModel::mat_texuniform) + .property("mat_texrepeat", &MjModel::mat_texrepeat) + .property("mat_emission", &MjModel::mat_emission) + .property("mat_specular", &MjModel::mat_specular) + .property("mat_shininess", &MjModel::mat_shininess) + .property("mat_reflectance", &MjModel::mat_reflectance) + .property("mat_metallic", &MjModel::mat_metallic) + .property("mat_roughness", &MjModel::mat_roughness) + .property("mat_rgba", &MjModel::mat_rgba) + .property("pair_dim", &MjModel::pair_dim) + .property("pair_geom1", &MjModel::pair_geom1) + .property("pair_geom2", &MjModel::pair_geom2) + .property("pair_signature", &MjModel::pair_signature) + .property("pair_solref", &MjModel::pair_solref) + .property("pair_solreffriction", &MjModel::pair_solreffriction) + .property("pair_solimp", &MjModel::pair_solimp) + .property("pair_margin", &MjModel::pair_margin) + .property("pair_gap", &MjModel::pair_gap) + .property("pair_friction", &MjModel::pair_friction) + .property("exclude_signature", &MjModel::exclude_signature) + .property("eq_type", &MjModel::eq_type) + .property("eq_obj1id", &MjModel::eq_obj1id) + .property("eq_obj2id", &MjModel::eq_obj2id) + .property("eq_objtype", &MjModel::eq_objtype) + .property("eq_active0", &MjModel::eq_active0) + .property("eq_solref", &MjModel::eq_solref) + .property("eq_solimp", &MjModel::eq_solimp) + .property("eq_data", &MjModel::eq_data) + .property("tendon_adr", &MjModel::tendon_adr) + .property("tendon_num", &MjModel::tendon_num) + .property("tendon_matid", &MjModel::tendon_matid) + .property("tendon_group", &MjModel::tendon_group) + .property("tendon_limited", &MjModel::tendon_limited) + .property("tendon_actfrclimited", &MjModel::tendon_actfrclimited) + .property("tendon_width", &MjModel::tendon_width) + .property("tendon_solref_lim", &MjModel::tendon_solref_lim) + .property("tendon_solimp_lim", &MjModel::tendon_solimp_lim) + .property("tendon_solref_fri", &MjModel::tendon_solref_fri) + .property("tendon_solimp_fri", &MjModel::tendon_solimp_fri) + .property("tendon_range", &MjModel::tendon_range) + .property("tendon_actfrcrange", &MjModel::tendon_actfrcrange) + .property("tendon_margin", &MjModel::tendon_margin) + .property("tendon_stiffness", &MjModel::tendon_stiffness) + .property("tendon_damping", &MjModel::tendon_damping) + .property("tendon_armature", &MjModel::tendon_armature) + .property("tendon_frictionloss", &MjModel::tendon_frictionloss) + .property("tendon_lengthspring", &MjModel::tendon_lengthspring) + .property("tendon_length0", &MjModel::tendon_length0) + .property("tendon_invweight0", &MjModel::tendon_invweight0) + .property("tendon_user", &MjModel::tendon_user) + .property("tendon_rgba", &MjModel::tendon_rgba) + .property("wrap_type", &MjModel::wrap_type) + .property("wrap_objid", &MjModel::wrap_objid) + .property("wrap_prm", &MjModel::wrap_prm) + .property("actuator_trntype", &MjModel::actuator_trntype) + .property("actuator_dyntype", &MjModel::actuator_dyntype) + .property("actuator_gaintype", &MjModel::actuator_gaintype) + .property("actuator_biastype", &MjModel::actuator_biastype) + .property("actuator_trnid", &MjModel::actuator_trnid) + .property("actuator_actadr", &MjModel::actuator_actadr) + .property("actuator_actnum", &MjModel::actuator_actnum) + .property("actuator_group", &MjModel::actuator_group) + .property("actuator_ctrllimited", &MjModel::actuator_ctrllimited) + .property("actuator_forcelimited", &MjModel::actuator_forcelimited) + .property("actuator_actlimited", &MjModel::actuator_actlimited) + .property("actuator_dynprm", &MjModel::actuator_dynprm) + .property("actuator_gainprm", &MjModel::actuator_gainprm) + .property("actuator_biasprm", &MjModel::actuator_biasprm) + .property("actuator_actearly", &MjModel::actuator_actearly) + .property("actuator_ctrlrange", &MjModel::actuator_ctrlrange) + .property("actuator_forcerange", &MjModel::actuator_forcerange) + .property("actuator_actrange", &MjModel::actuator_actrange) + .property("actuator_gear", &MjModel::actuator_gear) + .property("actuator_cranklength", &MjModel::actuator_cranklength) + .property("actuator_acc0", &MjModel::actuator_acc0) + .property("actuator_length0", &MjModel::actuator_length0) + .property("actuator_lengthrange", &MjModel::actuator_lengthrange) + .property("actuator_user", &MjModel::actuator_user) + .property("actuator_plugin", &MjModel::actuator_plugin) + .property("sensor_type", &MjModel::sensor_type) + .property("sensor_datatype", &MjModel::sensor_datatype) + .property("sensor_needstage", &MjModel::sensor_needstage) + .property("sensor_objtype", &MjModel::sensor_objtype) + .property("sensor_objid", &MjModel::sensor_objid) + .property("sensor_reftype", &MjModel::sensor_reftype) + .property("sensor_refid", &MjModel::sensor_refid) + .property("sensor_intprm", &MjModel::sensor_intprm) + .property("sensor_dim", &MjModel::sensor_dim) + .property("sensor_adr", &MjModel::sensor_adr) + .property("sensor_cutoff", &MjModel::sensor_cutoff) + .property("sensor_noise", &MjModel::sensor_noise) + .property("sensor_user", &MjModel::sensor_user) + .property("sensor_plugin", &MjModel::sensor_plugin) + .property("plugin", &MjModel::plugin) + .property("plugin_stateadr", &MjModel::plugin_stateadr) + .property("plugin_statenum", &MjModel::plugin_statenum) + .property("plugin_attr", &MjModel::plugin_attr) + .property("plugin_attradr", &MjModel::plugin_attradr) + .property("numeric_adr", &MjModel::numeric_adr) + .property("numeric_size", &MjModel::numeric_size) + .property("numeric_data", &MjModel::numeric_data) + .property("text_adr", &MjModel::text_adr) + .property("text_size", &MjModel::text_size) + .property("text_data", &MjModel::text_data) + .property("tuple_adr", &MjModel::tuple_adr) + .property("tuple_size", &MjModel::tuple_size) + .property("tuple_objtype", &MjModel::tuple_objtype) + .property("tuple_objid", &MjModel::tuple_objid) + .property("tuple_objprm", &MjModel::tuple_objprm) + .property("key_time", &MjModel::key_time) + .property("key_qpos", &MjModel::key_qpos) + .property("key_qvel", &MjModel::key_qvel) + .property("key_act", &MjModel::key_act) + .property("key_mpos", &MjModel::key_mpos) + .property("key_mquat", &MjModel::key_mquat) + .property("key_ctrl", &MjModel::key_ctrl) + .property("name_bodyadr", &MjModel::name_bodyadr) + .property("name_jntadr", &MjModel::name_jntadr) + .property("name_geomadr", &MjModel::name_geomadr) + .property("name_siteadr", &MjModel::name_siteadr) + .property("name_camadr", &MjModel::name_camadr) + .property("name_lightadr", &MjModel::name_lightadr) + .property("name_flexadr", &MjModel::name_flexadr) + .property("name_meshadr", &MjModel::name_meshadr) + .property("name_skinadr", &MjModel::name_skinadr) + .property("name_hfieldadr", &MjModel::name_hfieldadr) + .property("name_texadr", &MjModel::name_texadr) + .property("name_matadr", &MjModel::name_matadr) + .property("name_pairadr", &MjModel::name_pairadr) + .property("name_excludeadr", &MjModel::name_excludeadr) + .property("name_eqadr", &MjModel::name_eqadr) + .property("name_tendonadr", &MjModel::name_tendonadr) + .property("name_actuatoradr", &MjModel::name_actuatoradr) + .property("name_sensoradr", &MjModel::name_sensoradr) + .property("name_numericadr", &MjModel::name_numericadr) + .property("name_textadr", &MjModel::name_textadr) + .property("name_tupleadr", &MjModel::name_tupleadr) + .property("name_keyadr", &MjModel::name_keyadr) + .property("name_pluginadr", &MjModel::name_pluginadr) + .property("names", &MjModel::names) + .property("names_map", &MjModel::names_map) + .property("paths", &MjModel::paths) + .property("B_rownnz", &MjModel::B_rownnz) + .property("B_rowadr", &MjModel::B_rowadr) + .property("B_colind", &MjModel::B_colind) + .property("M_rownnz", &MjModel::M_rownnz) + .property("M_rowadr", &MjModel::M_rowadr) + .property("M_colind", &MjModel::M_colind) + .property("mapM2M", &MjModel::mapM2M) + .property("D_rownnz", &MjModel::D_rownnz) + .property("D_rowadr", &MjModel::D_rowadr) + .property("D_diag", &MjModel::D_diag) + .property("D_colind", &MjModel::D_colind) + .property("mapM2D", &MjModel::mapM2D) + .property("mapD2M", &MjModel::mapD2M) + .property("signature", &MjModel::signature, &MjModel::set_signature, reference()) + ; + emscripten::class_("MjData") + .constructor() + .constructor() + .property("narena", &MjData::narena, &MjData::set_narena, reference()) + .property("nbuffer", &MjData::nbuffer, &MjData::set_nbuffer, reference()) + .property("nplugin", &MjData::nplugin, &MjData::set_nplugin, reference()) + .property("pstack", &MjData::pstack, &MjData::set_pstack, reference()) + .property("pbase", &MjData::pbase, &MjData::set_pbase, reference()) + .property("parena", &MjData::parena, &MjData::set_parena, reference()) + .property("maxuse_stack", &MjData::maxuse_stack, &MjData::set_maxuse_stack, reference()) + .property("maxuse_threadstack", &MjData::maxuse_threadstack) + .property("maxuse_arena", &MjData::maxuse_arena, &MjData::set_maxuse_arena, reference()) + .property("maxuse_con", &MjData::maxuse_con, &MjData::set_maxuse_con, reference()) + .property("maxuse_efc", &MjData::maxuse_efc, &MjData::set_maxuse_efc, reference()) + .property("solver", &MjData::solver) + .property("solver_niter", &MjData::solver_niter) + .property("solver_nnz", &MjData::solver_nnz) + .property("solver_fwdinv", &MjData::solver_fwdinv) + .property("warning", &MjData::warning) + .property("timer", &MjData::timer) + .property("ncon", &MjData::ncon, &MjData::set_ncon, reference()) + .property("ne", &MjData::ne, &MjData::set_ne, reference()) + .property("nf", &MjData::nf, &MjData::set_nf, reference()) + .property("nl", &MjData::nl, &MjData::set_nl, reference()) + .property("nefc", &MjData::nefc, &MjData::set_nefc, reference()) + .property("nJ", &MjData::nJ, &MjData::set_nJ, reference()) + .property("nA", &MjData::nA, &MjData::set_nA, reference()) + .property("nisland", &MjData::nisland, &MjData::set_nisland, reference()) + .property("nidof", &MjData::nidof, &MjData::set_nidof, reference()) + .property("time", &MjData::time, &MjData::set_time, reference()) + .property("energy", &MjData::energy) + .property("buffer", &MjData::buffer) + .property("arena", &MjData::arena) + .property("qpos", &MjData::qpos) + .property("qvel", &MjData::qvel) + .property("act", &MjData::act) + .property("qacc_warmstart", &MjData::qacc_warmstart) + .property("plugin_state", &MjData::plugin_state) + .property("ctrl", &MjData::ctrl) + .property("qfrc_applied", &MjData::qfrc_applied) + .property("xfrc_applied", &MjData::xfrc_applied) + .property("eq_active", &MjData::eq_active) + .property("mocap_pos", &MjData::mocap_pos) + .property("mocap_quat", &MjData::mocap_quat) + .property("qacc", &MjData::qacc) + .property("act_dot", &MjData::act_dot) + .property("userdata", &MjData::userdata) + .property("sensordata", &MjData::sensordata) + .property("plugin", &MjData::plugin) + .property("plugin_data", &MjData::plugin_data) + .property("xpos", &MjData::xpos) + .property("xquat", &MjData::xquat) + .property("xmat", &MjData::xmat) + .property("xipos", &MjData::xipos) + .property("ximat", &MjData::ximat) + .property("xanchor", &MjData::xanchor) + .property("xaxis", &MjData::xaxis) + .property("geom_xpos", &MjData::geom_xpos) + .property("geom_xmat", &MjData::geom_xmat) + .property("site_xpos", &MjData::site_xpos) + .property("site_xmat", &MjData::site_xmat) + .property("cam_xpos", &MjData::cam_xpos) + .property("cam_xmat", &MjData::cam_xmat) + .property("light_xpos", &MjData::light_xpos) + .property("light_xdir", &MjData::light_xdir) + .property("subtree_com", &MjData::subtree_com) + .property("cdof", &MjData::cdof) + .property("cinert", &MjData::cinert) + .property("flexvert_xpos", &MjData::flexvert_xpos) + .property("flexelem_aabb", &MjData::flexelem_aabb) + .property("flexedge_J_rownnz", &MjData::flexedge_J_rownnz) + .property("flexedge_J_rowadr", &MjData::flexedge_J_rowadr) + .property("flexedge_J_colind", &MjData::flexedge_J_colind) + .property("flexedge_J", &MjData::flexedge_J) + .property("flexedge_length", &MjData::flexedge_length) + .property("bvh_aabb_dyn", &MjData::bvh_aabb_dyn) + .property("ten_wrapadr", &MjData::ten_wrapadr) + .property("ten_wrapnum", &MjData::ten_wrapnum) + .property("ten_J_rownnz", &MjData::ten_J_rownnz) + .property("ten_J_rowadr", &MjData::ten_J_rowadr) + .property("ten_J_colind", &MjData::ten_J_colind) + .property("ten_J", &MjData::ten_J) + .property("ten_length", &MjData::ten_length) + .property("wrap_obj", &MjData::wrap_obj) + .property("wrap_xpos", &MjData::wrap_xpos) + .property("actuator_length", &MjData::actuator_length) + .property("moment_rownnz", &MjData::moment_rownnz) + .property("moment_rowadr", &MjData::moment_rowadr) + .property("moment_colind", &MjData::moment_colind) + .property("actuator_moment", &MjData::actuator_moment) + .property("crb", &MjData::crb) + .property("qM", &MjData::qM) + .property("M", &MjData::M) + .property("qLD", &MjData::qLD) + .property("qLDiagInv", &MjData::qLDiagInv) + .property("bvh_active", &MjData::bvh_active) + .property("flexedge_velocity", &MjData::flexedge_velocity) + .property("ten_velocity", &MjData::ten_velocity) + .property("actuator_velocity", &MjData::actuator_velocity) + .property("cvel", &MjData::cvel) + .property("cdof_dot", &MjData::cdof_dot) + .property("qfrc_bias", &MjData::qfrc_bias) + .property("qfrc_spring", &MjData::qfrc_spring) + .property("qfrc_damper", &MjData::qfrc_damper) + .property("qfrc_gravcomp", &MjData::qfrc_gravcomp) + .property("qfrc_fluid", &MjData::qfrc_fluid) + .property("qfrc_passive", &MjData::qfrc_passive) + .property("subtree_linvel", &MjData::subtree_linvel) + .property("subtree_angmom", &MjData::subtree_angmom) + .property("qH", &MjData::qH) + .property("qHDiagInv", &MjData::qHDiagInv) + .property("qDeriv", &MjData::qDeriv) + .property("qLU", &MjData::qLU) + .property("actuator_force", &MjData::actuator_force) + .property("qfrc_actuator", &MjData::qfrc_actuator) + .property("qfrc_smooth", &MjData::qfrc_smooth) + .property("qacc_smooth", &MjData::qacc_smooth) + .property("qfrc_constraint", &MjData::qfrc_constraint) + .property("qfrc_inverse", &MjData::qfrc_inverse) + .property("cacc", &MjData::cacc) + .property("cfrc_int", &MjData::cfrc_int) + .property("cfrc_ext", &MjData::cfrc_ext) + .property("contact", &MjData::contact) + .property("efc_type", &MjData::efc_type) + .property("efc_id", &MjData::efc_id) + .property("efc_J_rownnz", &MjData::efc_J_rownnz) + .property("efc_J_rowadr", &MjData::efc_J_rowadr) + .property("efc_J_rowsuper", &MjData::efc_J_rowsuper) + .property("efc_J_colind", &MjData::efc_J_colind) + .property("efc_J", &MjData::efc_J) + .property("efc_pos", &MjData::efc_pos) + .property("efc_margin", &MjData::efc_margin) + .property("efc_frictionloss", &MjData::efc_frictionloss) + .property("efc_diagApprox", &MjData::efc_diagApprox) + .property("efc_KBIP", &MjData::efc_KBIP) + .property("efc_D", &MjData::efc_D) + .property("efc_R", &MjData::efc_R) + .property("tendon_efcadr", &MjData::tendon_efcadr) + .property("dof_island", &MjData::dof_island) + .property("island_nv", &MjData::island_nv) + .property("island_idofadr", &MjData::island_idofadr) + .property("island_dofadr", &MjData::island_dofadr) + .property("map_dof2idof", &MjData::map_dof2idof) + .property("map_idof2dof", &MjData::map_idof2dof) + .property("ifrc_smooth", &MjData::ifrc_smooth) + .property("iacc_smooth", &MjData::iacc_smooth) + .property("iM_rownnz", &MjData::iM_rownnz) + .property("iM_rowadr", &MjData::iM_rowadr) + .property("iM_colind", &MjData::iM_colind) + .property("iM", &MjData::iM) + .property("iLD", &MjData::iLD) + .property("iLDiagInv", &MjData::iLDiagInv) + .property("iacc", &MjData::iacc) + .property("efc_island", &MjData::efc_island) + .property("island_ne", &MjData::island_ne) + .property("island_nf", &MjData::island_nf) + .property("island_nefc", &MjData::island_nefc) + .property("island_iefcadr", &MjData::island_iefcadr) + .property("map_efc2iefc", &MjData::map_efc2iefc) + .property("map_iefc2efc", &MjData::map_iefc2efc) + .property("iefc_type", &MjData::iefc_type) + .property("iefc_id", &MjData::iefc_id) + .property("iefc_J_rownnz", &MjData::iefc_J_rownnz) + .property("iefc_J_rowadr", &MjData::iefc_J_rowadr) + .property("iefc_J_rowsuper", &MjData::iefc_J_rowsuper) + .property("iefc_J_colind", &MjData::iefc_J_colind) + .property("iefc_J", &MjData::iefc_J) + .property("iefc_frictionloss", &MjData::iefc_frictionloss) + .property("iefc_D", &MjData::iefc_D) + .property("iefc_R", &MjData::iefc_R) + .property("efc_AR_rownnz", &MjData::efc_AR_rownnz) + .property("efc_AR_rowadr", &MjData::efc_AR_rowadr) + .property("efc_AR_colind", &MjData::efc_AR_colind) + .property("efc_AR", &MjData::efc_AR) + .property("efc_vel", &MjData::efc_vel) + .property("efc_aref", &MjData::efc_aref) + .property("efc_b", &MjData::efc_b) + .property("iefc_aref", &MjData::iefc_aref) + .property("iefc_state", &MjData::iefc_state) + .property("iefc_force", &MjData::iefc_force) + .property("efc_state", &MjData::efc_state) + .property("efc_force", &MjData::efc_force) + .property("ifrc_constraint", &MjData::ifrc_constraint) + .property("threadpool", &MjData::threadpool, &MjData::set_threadpool, reference()) + .property("signature", &MjData::signature, &MjData::set_signature, reference()) + ; + emscripten::class_("MjOption") + .constructor<>() + .function("copy", &MjOption::copy, take_ownership()) + .property("timestep", &MjOption::timestep, &MjOption::set_timestep, reference()) + .property("impratio", &MjOption::impratio, &MjOption::set_impratio, reference()) + .property("tolerance", &MjOption::tolerance, &MjOption::set_tolerance, reference()) + .property("ls_tolerance", &MjOption::ls_tolerance, &MjOption::set_ls_tolerance, reference()) + .property("noslip_tolerance", &MjOption::noslip_tolerance, &MjOption::set_noslip_tolerance, reference()) + .property("ccd_tolerance", &MjOption::ccd_tolerance, &MjOption::set_ccd_tolerance, reference()) + .property("gravity", &MjOption::gravity) + .property("wind", &MjOption::wind) + .property("magnetic", &MjOption::magnetic) + .property("density", &MjOption::density, &MjOption::set_density, reference()) + .property("viscosity", &MjOption::viscosity, &MjOption::set_viscosity, reference()) + .property("o_margin", &MjOption::o_margin, &MjOption::set_o_margin, reference()) + .property("o_solref", &MjOption::o_solref) + .property("o_solimp", &MjOption::o_solimp) + .property("o_friction", &MjOption::o_friction) + .property("integrator", &MjOption::integrator, &MjOption::set_integrator, reference()) + .property("cone", &MjOption::cone, &MjOption::set_cone, reference()) + .property("jacobian", &MjOption::jacobian, &MjOption::set_jacobian, reference()) + .property("solver", &MjOption::solver, &MjOption::set_solver, reference()) + .property("iterations", &MjOption::iterations, &MjOption::set_iterations, reference()) + .property("ls_iterations", &MjOption::ls_iterations, &MjOption::set_ls_iterations, reference()) + .property("noslip_iterations", &MjOption::noslip_iterations, &MjOption::set_noslip_iterations, reference()) + .property("ccd_iterations", &MjOption::ccd_iterations, &MjOption::set_ccd_iterations, reference()) + .property("disableflags", &MjOption::disableflags, &MjOption::set_disableflags, reference()) + .property("enableflags", &MjOption::enableflags, &MjOption::set_enableflags, reference()) + .property("disableactuator", &MjOption::disableactuator, &MjOption::set_disableactuator, reference()) + .property("sdf_initpoints", &MjOption::sdf_initpoints, &MjOption::set_sdf_initpoints, reference()) + .property("sdf_iterations", &MjOption::sdf_iterations, &MjOption::set_sdf_iterations, reference()) + ; + emscripten::class_("MjStatistic") + .constructor<>() + .function("copy", &MjStatistic::copy, take_ownership()) + .property("meaninertia", &MjStatistic::meaninertia, &MjStatistic::set_meaninertia, reference()) + .property("meanmass", &MjStatistic::meanmass, &MjStatistic::set_meanmass, reference()) + .property("meansize", &MjStatistic::meansize, &MjStatistic::set_meansize, reference()) + .property("extent", &MjStatistic::extent, &MjStatistic::set_extent, reference()) + .property("center", &MjStatistic::center) + ; + emscripten::class_("MjVisualGlobal") + .constructor<>() + .function("copy", &MjVisualGlobal::copy, take_ownership()) + .property("cameraid", &MjVisualGlobal::cameraid, &MjVisualGlobal::set_cameraid, reference()) + .property("orthographic", &MjVisualGlobal::orthographic, &MjVisualGlobal::set_orthographic, reference()) + .property("fovy", &MjVisualGlobal::fovy, &MjVisualGlobal::set_fovy, reference()) + .property("ipd", &MjVisualGlobal::ipd, &MjVisualGlobal::set_ipd, reference()) + .property("azimuth", &MjVisualGlobal::azimuth, &MjVisualGlobal::set_azimuth, reference()) + .property("elevation", &MjVisualGlobal::elevation, &MjVisualGlobal::set_elevation, reference()) + .property("linewidth", &MjVisualGlobal::linewidth, &MjVisualGlobal::set_linewidth, reference()) + .property("glow", &MjVisualGlobal::glow, &MjVisualGlobal::set_glow, reference()) + .property("realtime", &MjVisualGlobal::realtime, &MjVisualGlobal::set_realtime, reference()) + .property("offwidth", &MjVisualGlobal::offwidth, &MjVisualGlobal::set_offwidth, reference()) + .property("offheight", &MjVisualGlobal::offheight, &MjVisualGlobal::set_offheight, reference()) + .property("ellipsoidinertia", &MjVisualGlobal::ellipsoidinertia, &MjVisualGlobal::set_ellipsoidinertia, reference()) + .property("bvactive", &MjVisualGlobal::bvactive, &MjVisualGlobal::set_bvactive, reference()) + ; + emscripten::class_("MjVisualQuality") + .constructor<>() + .function("copy", &MjVisualQuality::copy, take_ownership()) + .property("shadowsize", &MjVisualQuality::shadowsize, &MjVisualQuality::set_shadowsize, reference()) + .property("offsamples", &MjVisualQuality::offsamples, &MjVisualQuality::set_offsamples, reference()) + .property("numslices", &MjVisualQuality::numslices, &MjVisualQuality::set_numslices, reference()) + .property("numstacks", &MjVisualQuality::numstacks, &MjVisualQuality::set_numstacks, reference()) + .property("numquads", &MjVisualQuality::numquads, &MjVisualQuality::set_numquads, reference()) + ; + emscripten::class_("MjVisualHeadlight") + .constructor<>() + .function("copy", &MjVisualHeadlight::copy, take_ownership()) + .property("ambient", &MjVisualHeadlight::ambient) + .property("diffuse", &MjVisualHeadlight::diffuse) + .property("specular", &MjVisualHeadlight::specular) + .property("active", &MjVisualHeadlight::active, &MjVisualHeadlight::set_active, reference()) + ; + emscripten::class_("MjVisualMap") + .constructor<>() + .function("copy", &MjVisualMap::copy, take_ownership()) + .property("stiffness", &MjVisualMap::stiffness, &MjVisualMap::set_stiffness, reference()) + .property("stiffnessrot", &MjVisualMap::stiffnessrot, &MjVisualMap::set_stiffnessrot, reference()) + .property("force", &MjVisualMap::force, &MjVisualMap::set_force, reference()) + .property("torque", &MjVisualMap::torque, &MjVisualMap::set_torque, reference()) + .property("alpha", &MjVisualMap::alpha, &MjVisualMap::set_alpha, reference()) + .property("fogstart", &MjVisualMap::fogstart, &MjVisualMap::set_fogstart, reference()) + .property("fogend", &MjVisualMap::fogend, &MjVisualMap::set_fogend, reference()) + .property("znear", &MjVisualMap::znear, &MjVisualMap::set_znear, reference()) + .property("zfar", &MjVisualMap::zfar, &MjVisualMap::set_zfar, reference()) + .property("haze", &MjVisualMap::haze, &MjVisualMap::set_haze, reference()) + .property("shadowclip", &MjVisualMap::shadowclip, &MjVisualMap::set_shadowclip, reference()) + .property("shadowscale", &MjVisualMap::shadowscale, &MjVisualMap::set_shadowscale, reference()) + .property("actuatortendon", &MjVisualMap::actuatortendon, &MjVisualMap::set_actuatortendon, reference()) + ; + emscripten::class_("MjVisualScale") + .constructor<>() + .function("copy", &MjVisualScale::copy, take_ownership()) + .property("forcewidth", &MjVisualScale::forcewidth, &MjVisualScale::set_forcewidth, reference()) + .property("contactwidth", &MjVisualScale::contactwidth, &MjVisualScale::set_contactwidth, reference()) + .property("contactheight", &MjVisualScale::contactheight, &MjVisualScale::set_contactheight, reference()) + .property("connect", &MjVisualScale::connect, &MjVisualScale::set_connect, reference()) + .property("com", &MjVisualScale::com, &MjVisualScale::set_com, reference()) + .property("camera", &MjVisualScale::camera, &MjVisualScale::set_camera, reference()) + .property("light", &MjVisualScale::light, &MjVisualScale::set_light, reference()) + .property("selectpoint", &MjVisualScale::selectpoint, &MjVisualScale::set_selectpoint, reference()) + .property("jointlength", &MjVisualScale::jointlength, &MjVisualScale::set_jointlength, reference()) + .property("jointwidth", &MjVisualScale::jointwidth, &MjVisualScale::set_jointwidth, reference()) + .property("actuatorlength", &MjVisualScale::actuatorlength, &MjVisualScale::set_actuatorlength, reference()) + .property("actuatorwidth", &MjVisualScale::actuatorwidth, &MjVisualScale::set_actuatorwidth, reference()) + .property("framelength", &MjVisualScale::framelength, &MjVisualScale::set_framelength, reference()) + .property("framewidth", &MjVisualScale::framewidth, &MjVisualScale::set_framewidth, reference()) + .property("constraint", &MjVisualScale::constraint, &MjVisualScale::set_constraint, reference()) + .property("slidercrank", &MjVisualScale::slidercrank, &MjVisualScale::set_slidercrank, reference()) + .property("frustum", &MjVisualScale::frustum, &MjVisualScale::set_frustum, reference()) + ; + emscripten::class_("MjVisualRgba") + .constructor<>() + .function("copy", &MjVisualRgba::copy, take_ownership()) + .property("fog", &MjVisualRgba::fog) + .property("haze", &MjVisualRgba::haze) + .property("force", &MjVisualRgba::force) + .property("inertia", &MjVisualRgba::inertia) + .property("joint", &MjVisualRgba::joint) + .property("actuator", &MjVisualRgba::actuator) + .property("actuatornegative", &MjVisualRgba::actuatornegative) + .property("actuatorpositive", &MjVisualRgba::actuatorpositive) + .property("com", &MjVisualRgba::com) + .property("camera", &MjVisualRgba::camera) + .property("light", &MjVisualRgba::light) + .property("selectpoint", &MjVisualRgba::selectpoint) + .property("connect", &MjVisualRgba::connect) + .property("contactpoint", &MjVisualRgba::contactpoint) + .property("contactforce", &MjVisualRgba::contactforce) + .property("contactfriction", &MjVisualRgba::contactfriction) + .property("contacttorque", &MjVisualRgba::contacttorque) + .property("contactgap", &MjVisualRgba::contactgap) + .property("rangefinder", &MjVisualRgba::rangefinder) + .property("constraint", &MjVisualRgba::constraint) + .property("slidercrank", &MjVisualRgba::slidercrank) + .property("crankbroken", &MjVisualRgba::crankbroken) + .property("frustum", &MjVisualRgba::frustum) + .property("bv", &MjVisualRgba::bv) + .property("bvactive", &MjVisualRgba::bvactive) + ; + emscripten::class_("MjVisual") + .constructor<>() + .function("copy", &MjVisual::copy, take_ownership()) + .property("global", &MjVisual::global, reference()) + .property("quality", &MjVisual::quality, reference()) + .property("headlight", &MjVisual::headlight, reference()) + .property("map", &MjVisual::map, reference()) + .property("scale", &MjVisual::scale, reference()) + .property("rgba", &MjVisual::rgba, reference()) + ; + emscripten::class_("MjSolverStat") + .constructor<>() + .function("copy", &MjSolverStat::copy, take_ownership()) + .property("improvement", &MjSolverStat::improvement, &MjSolverStat::set_improvement, reference()) + .property("gradient", &MjSolverStat::gradient, &MjSolverStat::set_gradient, reference()) + .property("lineslope", &MjSolverStat::lineslope, &MjSolverStat::set_lineslope, reference()) + .property("nactive", &MjSolverStat::nactive, &MjSolverStat::set_nactive, reference()) + .property("nchange", &MjSolverStat::nchange, &MjSolverStat::set_nchange, reference()) + .property("neval", &MjSolverStat::neval, &MjSolverStat::set_neval, reference()) + .property("nupdate", &MjSolverStat::nupdate, &MjSolverStat::set_nupdate, reference()) + ; + emscripten::class_("MjTimerStat") + .constructor<>() + .function("copy", &MjTimerStat::copy, take_ownership()) + .property("duration", &MjTimerStat::duration, &MjTimerStat::set_duration, reference()) + .property("number", &MjTimerStat::number, &MjTimerStat::set_number, reference()) + ; + emscripten::class_("MjWarningStat") + .constructor<>() + .function("copy", &MjWarningStat::copy, take_ownership()) + .property("lastinfo", &MjWarningStat::lastinfo, &MjWarningStat::set_lastinfo, reference()) + .property("number", &MjWarningStat::number, &MjWarningStat::set_number, reference()) + ; + emscripten::class_("MjContact") + .constructor<>() + .function("copy", &MjContact::copy, take_ownership()) + .property("dist", &MjContact::dist, &MjContact::set_dist, reference()) + .property("pos", &MjContact::pos) + .property("frame", &MjContact::frame) + .property("includemargin", &MjContact::includemargin, &MjContact::set_includemargin, reference()) + .property("friction", &MjContact::friction) + .property("solref", &MjContact::solref) + .property("solreffriction", &MjContact::solreffriction) + .property("solimp", &MjContact::solimp) + .property("mu", &MjContact::mu, &MjContact::set_mu, reference()) + .property("H", &MjContact::H) + .property("dim", &MjContact::dim, &MjContact::set_dim, reference()) + .property("geom1", &MjContact::geom1, &MjContact::set_geom1, reference()) + .property("geom2", &MjContact::geom2, &MjContact::set_geom2, reference()) + .property("geom", &MjContact::geom) + .property("flex", &MjContact::flex) + .property("elem", &MjContact::elem) + .property("vert", &MjContact::vert) + .property("exclude", &MjContact::exclude, &MjContact::set_exclude, reference()) + .property("efc_address", &MjContact::efc_address, &MjContact::set_efc_address, reference()) + ; + emscripten::class_("MjvPerturb") + .constructor<>() + .function("copy", &MjvPerturb::copy, take_ownership()) + .property("select", &MjvPerturb::select, &MjvPerturb::set_select, reference()) + .property("flexselect", &MjvPerturb::flexselect, &MjvPerturb::set_flexselect, reference()) + .property("skinselect", &MjvPerturb::skinselect, &MjvPerturb::set_skinselect, reference()) + .property("active", &MjvPerturb::active, &MjvPerturb::set_active, reference()) + .property("active2", &MjvPerturb::active2, &MjvPerturb::set_active2, reference()) + .property("refpos", &MjvPerturb::refpos) + .property("refquat", &MjvPerturb::refquat) + .property("refselpos", &MjvPerturb::refselpos) + .property("localpos", &MjvPerturb::localpos) + .property("localmass", &MjvPerturb::localmass, &MjvPerturb::set_localmass, reference()) + .property("scale", &MjvPerturb::scale, &MjvPerturb::set_scale, reference()) + ; + emscripten::class_("MjvCamera") + .constructor<>() + .function("copy", &MjvCamera::copy, take_ownership()) + .property("type", &MjvCamera::type, &MjvCamera::set_type, reference()) + .property("fixedcamid", &MjvCamera::fixedcamid, &MjvCamera::set_fixedcamid, reference()) + .property("trackbodyid", &MjvCamera::trackbodyid, &MjvCamera::set_trackbodyid, reference()) + .property("lookat", &MjvCamera::lookat) + .property("distance", &MjvCamera::distance, &MjvCamera::set_distance, reference()) + .property("azimuth", &MjvCamera::azimuth, &MjvCamera::set_azimuth, reference()) + .property("elevation", &MjvCamera::elevation, &MjvCamera::set_elevation, reference()) + .property("orthographic", &MjvCamera::orthographic, &MjvCamera::set_orthographic, reference()) + ; + emscripten::class_("MjvGLCamera") + .constructor<>() + .function("copy", &MjvGLCamera::copy, take_ownership()) + .property("pos", &MjvGLCamera::pos) + .property("forward", &MjvGLCamera::forward) + .property("up", &MjvGLCamera::up) + .property("frustum_center", &MjvGLCamera::frustum_center, &MjvGLCamera::set_frustum_center, reference()) + .property("frustum_width", &MjvGLCamera::frustum_width, &MjvGLCamera::set_frustum_width, reference()) + .property("frustum_bottom", &MjvGLCamera::frustum_bottom, &MjvGLCamera::set_frustum_bottom, reference()) + .property("frustum_top", &MjvGLCamera::frustum_top, &MjvGLCamera::set_frustum_top, reference()) + .property("frustum_near", &MjvGLCamera::frustum_near, &MjvGLCamera::set_frustum_near, reference()) + .property("frustum_far", &MjvGLCamera::frustum_far, &MjvGLCamera::set_frustum_far, reference()) + .property("orthographic", &MjvGLCamera::orthographic, &MjvGLCamera::set_orthographic, reference()) + ; + emscripten::class_("MjvGeom") + .constructor<>() + .function("copy", &MjvGLCamera::copy, take_ownership()) + .property("type", &MjvGeom::type, &MjvGeom::set_type, reference()) + .property("dataid", &MjvGeom::dataid, &MjvGeom::set_dataid, reference()) + .property("objtype", &MjvGeom::objtype, &MjvGeom::set_objtype, reference()) + .property("objid", &MjvGeom::objid, &MjvGeom::set_objid, reference()) + .property("category", &MjvGeom::category, &MjvGeom::set_category, reference()) + .property("matid", &MjvGeom::matid, &MjvGeom::set_matid, reference()) + .property("texcoord", &MjvGeom::texcoord, &MjvGeom::set_texcoord, reference()) + .property("segid", &MjvGeom::segid, &MjvGeom::set_segid, reference()) + .property("size", &MjvGeom::size) + .property("pos", &MjvGeom::pos) + .property("mat", &MjvGeom::mat) + .property("rgba", &MjvGeom::rgba) + .property("emission", &MjvGeom::emission, &MjvGeom::set_emission, reference()) + .property("specular", &MjvGeom::specular, &MjvGeom::set_specular, reference()) + .property("shininess", &MjvGeom::shininess, &MjvGeom::set_shininess, reference()) + .property("reflectance", &MjvGeom::reflectance, &MjvGeom::set_reflectance, reference()) + .property("label", &MjvGeom::label) + .property("camdist", &MjvGeom::camdist, &MjvGeom::set_camdist, reference()) + .property("modelrbound", &MjvGeom::modelrbound, &MjvGeom::set_modelrbound, reference()) + .property("transparent", &MjvGeom::transparent, &MjvGeom::set_transparent, reference()) + ; + emscripten::class_("MjvLight") + .constructor<>() + .function("copy", &MjvLight::copy, take_ownership()) + .property("id", &MjvLight::id, &MjvLight::set_id, reference()) + .property("pos", &MjvLight::pos) + .property("dir", &MjvLight::dir) + .property("type", &MjvLight::type, &MjvLight::set_type, reference()) + .property("texid", &MjvLight::texid, &MjvLight::set_texid, reference()) + .property("attenuation", &MjvLight::attenuation) + .property("cutoff", &MjvLight::cutoff, &MjvLight::set_cutoff, reference()) + .property("exponent", &MjvLight::exponent, &MjvLight::set_exponent, reference()) + .property("ambient", &MjvLight::ambient) + .property("diffuse", &MjvLight::diffuse) + .property("specular", &MjvLight::specular) + .property("headlight", &MjvLight::headlight, &MjvLight::set_headlight, reference()) + .property("castshadow", &MjvLight::castshadow, &MjvLight::set_castshadow, reference()) + .property("bulbradius", &MjvLight::bulbradius, &MjvLight::set_bulbradius, reference()) + .property("intensity", &MjvLight::intensity, &MjvLight::set_intensity, reference()) + .property("range", &MjvLight::range, &MjvLight::set_range, reference()) + ; + emscripten::class_("MjvOption") + .constructor<>() + .function("copy", &MjvOption::copy, take_ownership()) + .property("label", &MjvOption::label, &MjvOption::set_label, reference()) + .property("frame", &MjvOption::frame, &MjvOption::set_frame, reference()) + .property("geomgroup", &MjvOption::geomgroup) + .property("sitegroup", &MjvOption::sitegroup) + .property("jointgroup", &MjvOption::jointgroup) + .property("tendongroup", &MjvOption::tendongroup) + .property("actuatorgroup", &MjvOption::actuatorgroup) + .property("flexgroup", &MjvOption::flexgroup) + .property("skingroup", &MjvOption::skingroup) + .property("flags", &MjvOption::flags) + .property("bvh_depth", &MjvOption::bvh_depth, &MjvOption::set_bvh_depth, reference()) + .property("flex_layer", &MjvOption::flex_layer, &MjvOption::set_flex_layer, reference()) + ; + + emscripten::class_("MjvScene") + .constructor<>() + .constructor() + .property("maxgeom", &MjvScene::maxgeom, &MjvScene::set_maxgeom, reference()) + .property("ngeom", &MjvScene::ngeom, &MjvScene::set_ngeom, reference()) + .property("geoms", &MjvScene::geoms) + .property("geomorder", &MjvScene::geomorder) + .property("nflex", &MjvScene::nflex, &MjvScene::set_nflex, reference()) + .property("flexedgeadr", &MjvScene::flexedgeadr) + .property("flexedgenum", &MjvScene::flexedgenum) + .property("flexvertadr", &MjvScene::flexvertadr) + .property("flexvertnum", &MjvScene::flexvertnum) + .property("flexfaceadr", &MjvScene::flexfaceadr) + .property("flexfacenum", &MjvScene::flexfacenum) + .property("flexfaceused", &MjvScene::flexfaceused) + .property("flexedge", &MjvScene::flexedge) + .property("flexvert", &MjvScene::flexvert) + .property("flexface", &MjvScene::flexface) + .property("flexnormal", &MjvScene::flexnormal) + .property("flextexcoord", &MjvScene::flextexcoord) + .property("flexvertopt", &MjvScene::flexvertopt, &MjvScene::set_flexvertopt, reference()) + .property("flexedgeopt", &MjvScene::flexedgeopt, &MjvScene::set_flexedgeopt, reference()) + .property("flexfaceopt", &MjvScene::flexfaceopt, &MjvScene::set_flexfaceopt, reference()) + .property("flexskinopt", &MjvScene::flexskinopt, &MjvScene::set_flexskinopt, reference()) + .property("nskin", &MjvScene::nskin, &MjvScene::set_nskin, reference()) + .property("skinfacenum", &MjvScene::skinfacenum) + .property("skinvertadr", &MjvScene::skinvertadr) + .property("skinvertnum", &MjvScene::skinvertnum) + .property("skinvert", &MjvScene::skinvert) + .property("skinnormal", &MjvScene::skinnormal) + .property("nlight", &MjvScene::nlight, &MjvScene::set_nlight, reference()) + .property("lights", &MjvScene::lights) + .property("camera", &MjvScene::camera) + .property("enabletransform", &MjvScene::enabletransform, &MjvScene::set_enabletransform, reference()) + .property("translate", &MjvScene::translate) + .property("rotate", &MjvScene::rotate) + .property("scale", &MjvScene::scale, &MjvScene::set_scale, reference()) + .property("stereo", &MjvScene::stereo, &MjvScene::set_stereo, reference()) + .property("flags", &MjvScene::flags) + .property("framewidth", &MjvScene::framewidth, &MjvScene::set_framewidth, reference()) + .property("framergb", &MjvScene::framergb) + .property("status", &MjvScene::status, &MjvScene::set_status, reference()) + ; + + emscripten::class_("MjvFigure") + .constructor<>() + .function("copy", &MjvFigure::copy, take_ownership()) + .property("flg_legend", &MjvFigure::flg_legend, &MjvFigure::set_flg_legend, reference()) + .property("flg_ticklabel", &MjvFigure::flg_ticklabel) + .property("flg_extend", &MjvFigure::flg_extend, &MjvFigure::set_flg_extend, reference()) + .property("flg_barplot", &MjvFigure::flg_barplot, &MjvFigure::set_flg_barplot, reference()) + .property("flg_selection", &MjvFigure::flg_selection, &MjvFigure::set_flg_selection, reference()) + .property("flg_symmetric", &MjvFigure::flg_symmetric, &MjvFigure::set_flg_symmetric, reference()) + .property("linewidth", &MjvFigure::linewidth, &MjvFigure::set_linewidth, reference()) + .property("gridwidth", &MjvFigure::gridwidth, &MjvFigure::set_gridwidth, reference()) + .property("gridsize", &MjvFigure::gridsize) + .property("gridrgb", &MjvFigure::gridrgb) + .property("figurergba", &MjvFigure::figurergba) + .property("panergba", &MjvFigure::panergba) + .property("legendrgba", &MjvFigure::legendrgba) + .property("textrgb", &MjvFigure::textrgb) + .property("linergb", &MjvFigure::linergb) + .property("range", &MjvFigure::range) + .property("xformat", &MjvFigure::xformat) + .property("yformat", &MjvFigure::yformat) + .property("minwidth", &MjvFigure::minwidth) + .property("title", &MjvFigure::title) + .property("xlabel", &MjvFigure::xlabel) + .property("linename", &MjvFigure::linename) + .property("legendoffset", &MjvFigure::legendoffset, &MjvFigure::set_legendoffset, reference()) + .property("subplot", &MjvFigure::subplot, &MjvFigure::set_subplot, reference()) + .property("highlight", &MjvFigure::highlight) + .property("highlightid", &MjvFigure::highlightid, &MjvFigure::set_highlightid, reference()) + .property("selection", &MjvFigure::selection, &MjvFigure::set_selection, reference()) + .property("linepnt", &MjvFigure::linepnt) + .property("linedata", &MjvFigure::linedata) + .property("xaxispixel", &MjvFigure::xaxispixel) + .property("yaxispixel", &MjvFigure::yaxispixel) + .property("xaxisdata", &MjvFigure::xaxisdata) + .property("yaxisdata", &MjvFigure::yaxisdata) + ; + + emscripten::class_("MjSpec") + .constructor() + .property("element", &MjSpec::element) + .property("modelname", &MjSpec::modelname, &MjSpec::set_modelname, reference()) + .property("compiler", &MjSpec::compiler) + .property("strippath", &MjSpec::strippath, &MjSpec::set_strippath, reference()) + .property("option", &MjSpec::option) + .property("visual", &MjSpec::visual) + .property("stat", &MjSpec::stat) + .property("memory", &MjSpec::memory, &MjSpec::set_memory, reference()) + .property("nemax", &MjSpec::nemax, &MjSpec::set_nemax, reference()) + .property("nuserdata", &MjSpec::nuserdata, &MjSpec::set_nuserdata, reference()) + .property("nuser_body", &MjSpec::nuser_body, &MjSpec::set_nuser_body, reference()) + .property("nuser_jnt", &MjSpec::nuser_jnt, &MjSpec::set_nuser_jnt, reference()) + .property("nuser_geom", &MjSpec::nuser_geom, &MjSpec::set_nuser_geom, reference()) + .property("nuser_site", &MjSpec::nuser_site, &MjSpec::set_nuser_site, reference()) + .property("nuser_cam", &MjSpec::nuser_cam, &MjSpec::set_nuser_cam, reference()) + .property("nuser_tendon", &MjSpec::nuser_tendon, &MjSpec::set_nuser_tendon, reference()) + .property("nuser_actuator", &MjSpec::nuser_actuator, &MjSpec::set_nuser_actuator, reference()) + .property("nuser_sensor", &MjSpec::nuser_sensor, &MjSpec::set_nuser_sensor, reference()) + .property("nkey", &MjSpec::nkey, &MjSpec::set_nkey, reference()) + .property("njmax", &MjSpec::njmax, &MjSpec::set_njmax, reference()) + .property("nconmax", &MjSpec::nconmax, &MjSpec::set_nconmax, reference()) + .property("nstack", &MjSpec::nstack, &MjSpec::set_nstack, reference()) + .property("comment", &MjSpec::comment, &MjSpec::set_comment, reference()) + .property("modelfiledir", &MjSpec::modelfiledir, &MjSpec::set_modelfiledir, reference()) + .property("hasImplicitPluginElem", &MjSpec::hasImplicitPluginElem, &MjSpec::set_hasImplicitPluginElem, reference()) + ; + + emscripten::class_("MjsElement") + .property("elemtype", &MjsElement::elemtype, &MjsElement::set_elemtype, reference()) + .property("signature", &MjsElement::signature, &MjsElement::set_signature, reference()) + ; + + emscripten::class_("MjsCompiler") + .property("autolimits", &MjsCompiler::autolimits, &MjsCompiler::set_autolimits, reference()) + .property("boundmass", &MjsCompiler::boundmass, &MjsCompiler::set_boundmass, reference()) + .property("boundinertia", &MjsCompiler::boundinertia, &MjsCompiler::set_boundinertia, reference()) + .property("settotalmass", &MjsCompiler::settotalmass, &MjsCompiler::set_settotalmass, reference()) + .property("balanceinertia", &MjsCompiler::balanceinertia, &MjsCompiler::set_balanceinertia, reference()) + .property("fitaabb", &MjsCompiler::fitaabb, &MjsCompiler::set_fitaabb, reference()) + .property("degree", &MjsCompiler::degree, &MjsCompiler::set_degree, reference()) + .property("eulerseq", &MjsCompiler::eulerseq) + .property("discardvisual", &MjsCompiler::discardvisual, &MjsCompiler::set_discardvisual, reference()) + .property("usethread", &MjsCompiler::usethread, &MjsCompiler::set_usethread, reference()) + .property("fusestatic", &MjsCompiler::fusestatic, &MjsCompiler::set_fusestatic, reference()) + .property("inertiafromgeom", &MjsCompiler::inertiafromgeom, &MjsCompiler::set_inertiafromgeom, reference()) + .property("inertiagrouprange", &MjsCompiler::inertiagrouprange) + .property("saveinertial", &MjsCompiler::saveinertial, &MjsCompiler::set_saveinertial, reference()) + .property("alignfree", &MjsCompiler::alignfree, &MjsCompiler::set_alignfree, reference()) + .property("LRopt", &MjsCompiler::LRopt, reference()) + .property("meshdir", &MjsCompiler::meshdir, &MjsCompiler::set_meshdir, reference()) + .property("texturedir", &MjsCompiler::texturedir, &MjsCompiler::set_texturedir, reference()) + ; + + emscripten::class_("MjsOrientation") + .function("copy", &MjsOrientation::copy, take_ownership()) + .property("type", &MjsOrientation::type, &MjsOrientation::set_type, reference()) + .property("axisangle", &MjsOrientation::axisangle) + .property("xyaxes", &MjsOrientation::xyaxes) + .property("zaxis", &MjsOrientation::zaxis) + .property("euler", &MjsOrientation::euler) + ; + + emscripten::class_("MjsBody") + .property("element", &MjsBody::element, reference()) + .property("childclass", &MjsBody::childclass, &MjsBody::set_childclass, reference()) + .property("pos", &MjsBody::pos) + .property("quat", &MjsBody::quat) + .property("alt", &MjsBody::alt, reference()) + .property("mass", &MjsBody::mass, &MjsBody::set_mass, reference()) + .property("ipos", &MjsBody::ipos) + .property("iquat", &MjsBody::iquat) + .property("inertia", &MjsBody::inertia) + .property("ialt", &MjsBody::ialt, reference()) + .property("fullinertia", &MjsBody::fullinertia) + .property("mocap", &MjsBody::mocap, &MjsBody::set_mocap, reference()) + .property("gravcomp", &MjsBody::gravcomp, &MjsBody::set_gravcomp, reference()) + .property("userdata", &MjsBody::userdata, reference()) + .property("explicitinertial", &MjsBody::explicitinertial, &MjsBody::set_explicitinertial, reference()) + .property("plugin", &MjsBody::plugin, reference()) + .property("info", &MjsBody::info, &MjsBody::set_info, reference()) + ; + + emscripten::class_("MjsGeom") + .property("element", &MjsGeom::element, reference()) + .property("type", &MjsGeom::type, &MjsGeom::set_type, reference()) + .property("pos", &MjsGeom::pos) + .property("quat", &MjsGeom::quat) + .property("alt", &MjsGeom::alt, reference()) + .property("fromto", &MjsGeom::fromto) + .property("size", &MjsGeom::size) + .property("contype", &MjsGeom::contype, &MjsGeom::set_contype, reference()) + .property("conaffinity", &MjsGeom::conaffinity, &MjsGeom::set_conaffinity, reference()) + .property("condim", &MjsGeom::condim, &MjsGeom::set_condim, reference()) + .property("priority", &MjsGeom::priority, &MjsGeom::set_priority, reference()) + .property("friction", &MjsGeom::friction) + .property("solmix", &MjsGeom::solmix, &MjsGeom::set_solmix, reference()) + .property("solref", &MjsGeom::solref) + .property("solimp", &MjsGeom::solimp) + .property("margin", &MjsGeom::margin, &MjsGeom::set_margin, reference()) + .property("gap", &MjsGeom::gap, &MjsGeom::set_gap, reference()) + .property("mass", &MjsGeom::mass, &MjsGeom::set_mass, reference()) + .property("density", &MjsGeom::density, &MjsGeom::set_density, reference()) + .property("typeinertia", &MjsGeom::typeinertia, &MjsGeom::set_typeinertia, reference()) + .property("fluid_ellipsoid", &MjsGeom::fluid_ellipsoid, &MjsGeom::set_fluid_ellipsoid, reference()) + .property("fluid_coefs", &MjsGeom::fluid_coefs) + .property("material", &MjsGeom::material, &MjsGeom::set_material, reference()) + .property("rgba", &MjsGeom::rgba) + .property("group", &MjsGeom::group, &MjsGeom::set_group, reference()) + .property("hfieldname", &MjsGeom::hfieldname, &MjsGeom::set_hfieldname, reference()) + .property("meshname", &MjsGeom::meshname, &MjsGeom::set_meshname, reference()) + .property("fitscale", &MjsGeom::fitscale, &MjsGeom::set_fitscale, reference()) + .property("userdata", &MjsGeom::userdata, reference()) + .property("plugin", &MjsGeom::plugin, reference()) + .property("info", &MjsGeom::info, &MjsGeom::set_info, reference()) + ; + + emscripten::class_("MjsFrame") + .property("element", &MjsFrame::element, reference()) + .property("childclass", &MjsFrame::childclass, &MjsFrame::set_childclass, reference()) + .property("pos", &MjsFrame::pos) + .property("quat", &MjsFrame::quat) + .property("alt", &MjsFrame::alt, reference()) + .property("info", &MjsFrame::info, &MjsFrame::set_info, reference()) + ; + + emscripten::class_("MjsJoint") + .property("element", &MjsJoint::element, reference()) + .property("type", &MjsJoint::type, &MjsJoint::set_type, reference()) + .property("pos", &MjsJoint::pos) + .property("axis", &MjsJoint::axis) + .property("ref", &MjsJoint::ref, &MjsJoint::set_ref, reference()) + .property("align", &MjsJoint::align, &MjsJoint::set_align, reference()) + .property("stiffness", &MjsJoint::stiffness, &MjsJoint::set_stiffness, reference()) + .property("springref", &MjsJoint::springref, &MjsJoint::set_springref, reference()) + .property("springdamper", &MjsJoint::springdamper) + .property("limited", &MjsJoint::limited, &MjsJoint::set_limited, reference()) + .property("range", &MjsJoint::range) + .property("margin", &MjsJoint::margin, &MjsJoint::set_margin, reference()) + .property("solref_limit", &MjsJoint::solref_limit) + .property("solimp_limit", &MjsJoint::solimp_limit) + .property("actfrclimited", &MjsJoint::actfrclimited, &MjsJoint::set_actfrclimited, reference()) + .property("actfrcrange", &MjsJoint::actfrcrange) + .property("armature", &MjsJoint::armature, &MjsJoint::set_armature, reference()) + .property("damping", &MjsJoint::damping, &MjsJoint::set_damping, reference()) + .property("frictionloss", &MjsJoint::frictionloss, &MjsJoint::set_frictionloss, reference()) + .property("solref_friction", &MjsJoint::solref_friction) + .property("solimp_friction", &MjsJoint::solimp_friction) + .property("group", &MjsJoint::group, &MjsJoint::set_group, reference()) + .property("actgravcomp", &MjsJoint::actgravcomp, &MjsJoint::set_actgravcomp, reference()) + .property("userdata", &MjsJoint::userdata, reference()) + .property("info", &MjsJoint::info, &MjsJoint::set_info, reference()) + ; + + emscripten::class_("MjsSite") + .property("element", &MjsSite::element, reference()) + .property("pos", &MjsSite::pos) + .property("quat", &MjsSite::quat) + .property("alt", &MjsSite::alt, reference()) + .property("fromto", &MjsSite::fromto) + .property("size", &MjsSite::size) + .property("type", &MjsSite::type, &MjsSite::set_type, reference()) + .property("material", &MjsSite::material, &MjsSite::set_material, reference()) + .property("group", &MjsSite::group, &MjsSite::set_group, reference()) + .property("rgba", &MjsSite::rgba) + .property("userdata", &MjsSite::userdata, reference()) + .property("info", &MjsSite::info, &MjsSite::set_info, reference()) + ; + + emscripten::class_("MjsCamera") + .property("element", &MjsCamera::element, reference()) + .property("pos", &MjsCamera::pos) + .property("quat", &MjsCamera::quat) + .property("alt", &MjsCamera::alt, reference()) + .property("mode", &MjsCamera::mode, &MjsCamera::set_mode, reference()) + .property("targetbody", &MjsCamera::targetbody, &MjsCamera::set_targetbody, reference()) + .property("orthographic", &MjsCamera::orthographic, &MjsCamera::set_orthographic, reference()) + .property("fovy", &MjsCamera::fovy, &MjsCamera::set_fovy, reference()) + .property("ipd", &MjsCamera::ipd, &MjsCamera::set_ipd, reference()) + .property("intrinsic", &MjsCamera::intrinsic) + .property("sensor_size", &MjsCamera::sensor_size) + .property("resolution", &MjsCamera::resolution) + .property("focal_length", &MjsCamera::focal_length) + .property("focal_pixel", &MjsCamera::focal_pixel) + .property("principal_length", &MjsCamera::principal_length) + .property("principal_pixel", &MjsCamera::principal_pixel) + .property("userdata", &MjsCamera::userdata, reference()) + .property("info", &MjsCamera::info, &MjsCamera::set_info, reference()) + ; + + emscripten::class_("MjsLight") + .property("element", &MjsLight::element, reference()) + .property("pos", &MjsLight::pos) + .property("dir", &MjsLight::dir) + .property("mode", &MjsLight::mode, &MjsLight::set_mode, reference()) + .property("targetbody", &MjsLight::targetbody, &MjsLight::set_targetbody, reference()) + .property("active", &MjsLight::active, &MjsLight::set_active, reference()) + .property("type", &MjsLight::type, &MjsLight::set_type, reference()) + .property("texture", &MjsLight::texture, &MjsLight::set_texture, reference()) + .property("castshadow", &MjsLight::castshadow, &MjsLight::set_castshadow, reference()) + .property("bulbradius", &MjsLight::bulbradius, &MjsLight::set_bulbradius, reference()) + .property("intensity", &MjsLight::intensity, &MjsLight::set_intensity, reference()) + .property("range", &MjsLight::range, &MjsLight::set_range, reference()) + .property("attenuation", &MjsLight::attenuation) + .property("cutoff", &MjsLight::cutoff, &MjsLight::set_cutoff, reference()) + .property("exponent", &MjsLight::exponent, &MjsLight::set_exponent, reference()) + .property("ambient", &MjsLight::ambient) + .property("diffuse", &MjsLight::diffuse) + .property("specular", &MjsLight::specular) + .property("info", &MjsLight::info, &MjsLight::set_info, reference()) + ; + + emscripten::class_("MjsFlex") + .property("element", &MjsFlex::element, reference()) + .property("contype", &MjsFlex::contype, &MjsFlex::set_contype, reference()) + .property("conaffinity", &MjsFlex::conaffinity, &MjsFlex::set_conaffinity, reference()) + .property("condim", &MjsFlex::condim, &MjsFlex::set_condim, reference()) + .property("priority", &MjsFlex::priority, &MjsFlex::set_priority, reference()) + .property("friction", &MjsFlex::friction) + .property("solmix", &MjsFlex::solmix, &MjsFlex::set_solmix, reference()) + .property("solref", &MjsFlex::solref) + .property("solimp", &MjsFlex::solimp) + .property("margin", &MjsFlex::margin, &MjsFlex::set_margin, reference()) + .property("gap", &MjsFlex::gap, &MjsFlex::set_gap, reference()) + .property("dim", &MjsFlex::dim, &MjsFlex::set_dim, reference()) + .property("radius", &MjsFlex::radius, &MjsFlex::set_radius, reference()) + .property("internal", &MjsFlex::internal, &MjsFlex::set_internal, reference()) + .property("flatskin", &MjsFlex::flatskin, &MjsFlex::set_flatskin, reference()) + .property("selfcollide", &MjsFlex::selfcollide, &MjsFlex::set_selfcollide, reference()) + .property("vertcollide", &MjsFlex::vertcollide, &MjsFlex::set_vertcollide, reference()) + .property("passive", &MjsFlex::passive, &MjsFlex::set_passive, reference()) + .property("activelayers", &MjsFlex::activelayers, &MjsFlex::set_activelayers, reference()) + .property("group", &MjsFlex::group, &MjsFlex::set_group, reference()) + .property("edgestiffness", &MjsFlex::edgestiffness, &MjsFlex::set_edgestiffness, reference()) + .property("edgedamping", &MjsFlex::edgedamping, &MjsFlex::set_edgedamping, reference()) + .property("rgba", &MjsFlex::rgba) + .property("material", &MjsFlex::material, &MjsFlex::set_material, reference()) + .property("young", &MjsFlex::young, &MjsFlex::set_young, reference()) + .property("poisson", &MjsFlex::poisson, &MjsFlex::set_poisson, reference()) + .property("damping", &MjsFlex::damping, &MjsFlex::set_damping, reference()) + .property("thickness", &MjsFlex::thickness, &MjsFlex::set_thickness, reference()) + .property("elastic2d", &MjsFlex::elastic2d, &MjsFlex::set_elastic2d, reference()) + .property("nodebody", &MjsFlex::nodebody, reference()) + .property("vertbody", &MjsFlex::vertbody, reference()) + .property("node", &MjsFlex::node, reference()) + .property("vert", &MjsFlex::vert, reference()) + .property("elem", &MjsFlex::elem, reference()) + .property("texcoord", &MjsFlex::texcoord, reference()) + .property("elemtexcoord", &MjsFlex::elemtexcoord, reference()) + .property("info", &MjsFlex::info, &MjsFlex::set_info, reference()) + ; + + emscripten::class_("MjsMesh") + .property("element", &MjsMesh::element, reference()) + .property("content_type", &MjsMesh::content_type, &MjsMesh::set_content_type, reference()) + .property("file", &MjsMesh::file, &MjsMesh::set_file, reference()) + .property("refpos", &MjsMesh::refpos) + .property("refquat", &MjsMesh::refquat) + .property("scale", &MjsMesh::scale) + .property("inertia", &MjsMesh::inertia, &MjsMesh::set_inertia, reference()) + .property("smoothnormal", &MjsMesh::smoothnormal, &MjsMesh::set_smoothnormal, reference()) + .property("needsdf", &MjsMesh::needsdf, &MjsMesh::set_needsdf, reference()) + .property("maxhullvert", &MjsMesh::maxhullvert, &MjsMesh::set_maxhullvert, reference()) + .property("uservert", &MjsMesh::uservert, reference()) + .property("usernormal", &MjsMesh::usernormal, reference()) + .property("usertexcoord", &MjsMesh::usertexcoord, reference()) + .property("userface", &MjsMesh::userface, reference()) + .property("userfacenormal", &MjsMesh::userfacenormal, reference()) + .property("userfacetexcoord", &MjsMesh::userfacetexcoord, reference()) + .property("plugin", &MjsMesh::plugin, reference()) + .property("material", &MjsMesh::material, &MjsMesh::set_material, reference()) + .property("info", &MjsMesh::info, &MjsMesh::set_info, reference()) + ; + + emscripten::class_("MjsHField") + .property("element", &MjsHField::element, reference()) + .property("content_type", &MjsHField::content_type, &MjsHField::set_content_type, reference()) + .property("file", &MjsHField::file, &MjsHField::set_file, reference()) + .property("size", &MjsHField::size) + .property("nrow", &MjsHField::nrow, &MjsHField::set_nrow, reference()) + .property("ncol", &MjsHField::ncol, &MjsHField::set_ncol, reference()) + .property("userdata", &MjsHField::userdata, reference()) + .property("info", &MjsHField::info, &MjsHField::set_info, reference()) + ; + + emscripten::class_("MjsSkin") + .property("element", &MjsSkin::element, reference()) + .property("file", &MjsSkin::file, &MjsSkin::set_file, reference()) + .property("material", &MjsSkin::material, &MjsSkin::set_material, reference()) + .property("rgba", &MjsSkin::rgba) + .property("inflate", &MjsSkin::inflate, &MjsSkin::set_inflate, reference()) + .property("group", &MjsSkin::group, &MjsSkin::set_group, reference()) + .property("vert", &MjsSkin::vert, reference()) + .property("texcoord", &MjsSkin::texcoord, reference()) + .property("face", &MjsSkin::face, reference()) + .property("bodyname", &MjsSkin::bodyname, reference()) + .property("bindpos", &MjsSkin::bindpos, reference()) + .property("bindquat", &MjsSkin::bindquat, reference()) + .property("vertid", &MjsSkin::vertid, reference()) + .property("vertweight", &MjsSkin::vertweight, reference()) + .property("info", &MjsSkin::info, &MjsSkin::set_info, reference()) + ; + + emscripten::class_("MjsTexture") + .property("element", &MjsTexture::element, reference()) + .property("type", &MjsTexture::type, &MjsTexture::set_type, reference()) + .property("colorspace", &MjsTexture::colorspace, &MjsTexture::set_colorspace, reference()) + .property("builtin", &MjsTexture::builtin, &MjsTexture::set_builtin, reference()) + .property("mark", &MjsTexture::mark, &MjsTexture::set_mark, reference()) + .property("rgb1", &MjsTexture::rgb1) + .property("rgb2", &MjsTexture::rgb2) + .property("markrgb", &MjsTexture::markrgb) + .property("random", &MjsTexture::random, &MjsTexture::set_random, reference()) + .property("height", &MjsTexture::height, &MjsTexture::set_height, reference()) + .property("width", &MjsTexture::width, &MjsTexture::set_width, reference()) + .property("nchannel", &MjsTexture::nchannel, &MjsTexture::set_nchannel, reference()) + .property("content_type", &MjsTexture::content_type, &MjsTexture::set_content_type, reference()) + .property("file", &MjsTexture::file, &MjsTexture::set_file, reference()) + .property("gridsize", &MjsTexture::gridsize) + .property("gridlayout", &MjsTexture::gridlayout) + .property("cubefiles", &MjsTexture::cubefiles, reference()) + .property("data", &MjsTexture::data, reference()) + .property("hflip", &MjsTexture::hflip, &MjsTexture::set_hflip, reference()) + .property("vflip", &MjsTexture::vflip, &MjsTexture::set_vflip, reference()) + .property("info", &MjsTexture::info, &MjsTexture::set_info, reference()) + ; + + emscripten::class_("MjsMaterial") + .property("element", &MjsMaterial::element, reference()) + .property("textures", &MjsMaterial::textures, reference()) + .property("texuniform", &MjsMaterial::texuniform, &MjsMaterial::set_texuniform, reference()) + .property("texrepeat", &MjsMaterial::texrepeat) + .property("emission", &MjsMaterial::emission, &MjsMaterial::set_emission, reference()) + .property("specular", &MjsMaterial::specular, &MjsMaterial::set_specular, reference()) + .property("shininess", &MjsMaterial::shininess, &MjsMaterial::set_shininess, reference()) + .property("reflectance", &MjsMaterial::reflectance, &MjsMaterial::set_reflectance, reference()) + .property("metallic", &MjsMaterial::metallic, &MjsMaterial::set_metallic, reference()) + .property("roughness", &MjsMaterial::roughness, &MjsMaterial::set_roughness, reference()) + .property("rgba", &MjsMaterial::rgba) + .property("info", &MjsMaterial::info, &MjsMaterial::set_info, reference()) + ; + + emscripten::class_("MjsPair") + .property("element", &MjsPair::element, reference()) + .property("geomname1", &MjsPair::geomname1, &MjsPair::set_geomname1, reference()) + .property("geomname2", &MjsPair::geomname2, &MjsPair::set_geomname2, reference()) + .property("condim", &MjsPair::condim, &MjsPair::set_condim, reference()) + .property("solref", &MjsPair::solref) + .property("solreffriction", &MjsPair::solreffriction) + .property("solimp", &MjsPair::solimp) + .property("margin", &MjsPair::margin, &MjsPair::set_margin, reference()) + .property("gap", &MjsPair::gap, &MjsPair::set_gap, reference()) + .property("friction", &MjsPair::friction) + .property("info", &MjsPair::info, &MjsPair::set_info, reference()) + ; + + emscripten::class_("MjsExclude") + .property("element", &MjsExclude::element, reference()) + .property("bodyname1", &MjsExclude::bodyname1, &MjsExclude::set_bodyname1, reference()) + .property("bodyname2", &MjsExclude::bodyname2, &MjsExclude::set_bodyname2, reference()) + .property("info", &MjsExclude::info, &MjsExclude::set_info, reference()) + ; + + emscripten::class_("MjsEquality") + .property("element", &MjsEquality::element, reference()) + .property("type", &MjsEquality::type, &MjsEquality::set_type, reference()) + .property("data", &MjsEquality::data) + .property("active", &MjsEquality::active, &MjsEquality::set_active, reference()) + .property("name1", &MjsEquality::name1, &MjsEquality::set_name1, reference()) + .property("name2", &MjsEquality::name2, &MjsEquality::set_name2, reference()) + .property("objtype", &MjsEquality::objtype, &MjsEquality::set_objtype, reference()) + .property("solref", &MjsEquality::solref) + .property("solimp", &MjsEquality::solimp) + .property("info", &MjsEquality::info, &MjsEquality::set_info, reference()) + ; + + emscripten::class_("MjsTendon") + .property("element", &MjsTendon::element, reference()) + .property("stiffness", &MjsTendon::stiffness, &MjsTendon::set_stiffness, reference()) + .property("springlength", &MjsTendon::springlength) + .property("damping", &MjsTendon::damping, &MjsTendon::set_damping, reference()) + .property("frictionloss", &MjsTendon::frictionloss, &MjsTendon::set_frictionloss, reference()) + .property("solref_friction", &MjsTendon::solref_friction) + .property("solimp_friction", &MjsTendon::solimp_friction) + .property("armature", &MjsTendon::armature, &MjsTendon::set_armature, reference()) + .property("limited", &MjsTendon::limited, &MjsTendon::set_limited, reference()) + .property("actfrclimited", &MjsTendon::actfrclimited, &MjsTendon::set_actfrclimited, reference()) + .property("range", &MjsTendon::range) + .property("actfrcrange", &MjsTendon::actfrcrange) + .property("margin", &MjsTendon::margin, &MjsTendon::set_margin, reference()) + .property("solref_limit", &MjsTendon::solref_limit) + .property("solimp_limit", &MjsTendon::solimp_limit) + .property("material", &MjsTendon::material, &MjsTendon::set_material, reference()) + .property("width", &MjsTendon::width, &MjsTendon::set_width, reference()) + .property("rgba", &MjsTendon::rgba) + .property("group", &MjsTendon::group, &MjsTendon::set_group, reference()) + .property("userdata", &MjsTendon::userdata, reference()) + .property("info", &MjsTendon::info, &MjsTendon::set_info, reference()) + ; + + emscripten::class_("MjsWrap") + .property("element", &MjsWrap::element, reference()) + .property("type", &MjsWrap::type, &MjsWrap::set_type, reference()) + .property("info", &MjsWrap::info, &MjsWrap::set_info, reference()) + ; + + emscripten::class_("MjsActuator") + .property("element", &MjsActuator::element, reference()) + .property("gaintype", &MjsActuator::gaintype, &MjsActuator::set_gaintype, reference()) + .property("gainprm", &MjsActuator::gainprm) + .property("biastype", &MjsActuator::biastype, &MjsActuator::set_biastype, reference()) + .property("biasprm", &MjsActuator::biasprm) + .property("dyntype", &MjsActuator::dyntype, &MjsActuator::set_dyntype, reference()) + .property("dynprm", &MjsActuator::dynprm) + .property("actdim", &MjsActuator::actdim, &MjsActuator::set_actdim, reference()) + .property("actearly", &MjsActuator::actearly, &MjsActuator::set_actearly, reference()) + .property("trntype", &MjsActuator::trntype, &MjsActuator::set_trntype, reference()) + .property("gear", &MjsActuator::gear) + .property("target", &MjsActuator::target, &MjsActuator::set_target, reference()) + .property("refsite", &MjsActuator::refsite, &MjsActuator::set_refsite, reference()) + .property("slidersite", &MjsActuator::slidersite, &MjsActuator::set_slidersite, reference()) + .property("cranklength", &MjsActuator::cranklength, &MjsActuator::set_cranklength, reference()) + .property("lengthrange", &MjsActuator::lengthrange) + .property("inheritrange", &MjsActuator::inheritrange, &MjsActuator::set_inheritrange, reference()) + .property("ctrllimited", &MjsActuator::ctrllimited, &MjsActuator::set_ctrllimited, reference()) + .property("ctrlrange", &MjsActuator::ctrlrange) + .property("forcelimited", &MjsActuator::forcelimited, &MjsActuator::set_forcelimited, reference()) + .property("forcerange", &MjsActuator::forcerange) + .property("actlimited", &MjsActuator::actlimited, &MjsActuator::set_actlimited, reference()) + .property("actrange", &MjsActuator::actrange) + .property("group", &MjsActuator::group, &MjsActuator::set_group, reference()) + .property("userdata", &MjsActuator::userdata, reference()) + .property("plugin", &MjsActuator::plugin, reference()) + .property("info", &MjsActuator::info, &MjsActuator::set_info, reference()) + ; + + emscripten::class_("MjsSensor") + .property("element", &MjsSensor::element, reference()) + .property("type", &MjsSensor::type, &MjsSensor::set_type, reference()) + .property("objtype", &MjsSensor::objtype, &MjsSensor::set_objtype, reference()) + .property("objname", &MjsSensor::objname, &MjsSensor::set_objname, reference()) + .property("reftype", &MjsSensor::reftype, &MjsSensor::set_reftype, reference()) + .property("refname", &MjsSensor::refname, &MjsSensor::set_refname, reference()) + .property("intprm", &MjsSensor::intprm) + .property("datatype", &MjsSensor::datatype, &MjsSensor::set_datatype, reference()) + .property("needstage", &MjsSensor::needstage, &MjsSensor::set_needstage, reference()) + .property("dim", &MjsSensor::dim, &MjsSensor::set_dim, reference()) + .property("cutoff", &MjsSensor::cutoff, &MjsSensor::set_cutoff, reference()) + .property("noise", &MjsSensor::noise, &MjsSensor::set_noise, reference()) + .property("userdata", &MjsSensor::userdata, reference()) + .property("plugin", &MjsSensor::plugin, reference()) + .property("info", &MjsSensor::info, &MjsSensor::set_info, reference()) + ; + + emscripten::class_("MjsNumeric") + .property("element", &MjsNumeric::element, reference()) + .property("data", &MjsNumeric::data, reference()) + .property("size", &MjsNumeric::size, &MjsNumeric::set_size, reference()) + .property("info", &MjsNumeric::info, &MjsNumeric::set_info, reference()) + ; + + emscripten::class_("MjsText") + .property("element", &MjsText::element, reference()) + .property("data", &MjsText::data, &MjsText::set_data, reference()) + .property("info", &MjsText::info, &MjsText::set_info, reference()) + ; + + emscripten::class_("MjsTuple") + .property("element", &MjsTuple::element, reference()) + .property("objtype", &MjsTuple::objtype, reference()) + .property("objname", &MjsTuple::objname, reference()) + .property("objprm", &MjsTuple::objprm, reference()) + .property("info", &MjsTuple::info, &MjsTuple::set_info, reference()) + ; + + emscripten::class_("MjsKey") + .property("element", &MjsKey::element, reference()) + .property("time", &MjsKey::time, &MjsKey::set_time, reference()) + .property("qpos", &MjsKey::qpos, reference()) + .property("qvel", &MjsKey::qvel, reference()) + .property("act", &MjsKey::act, reference()) + .property("mpos", &MjsKey::mpos, reference()) + .property("mquat", &MjsKey::mquat, reference()) + .property("ctrl", &MjsKey::ctrl, reference()) + .property("info", &MjsKey::info, &MjsKey::set_info, reference()) + ; + + emscripten::class_("MjsDefault") + .property("element", &MjsDefault::element, reference()) + .property("joint", &MjsDefault::joint, reference()) + .property("geom", &MjsDefault::geom, reference()) + .property("site", &MjsDefault::site, reference()) + .property("camera", &MjsDefault::camera, reference()) + .property("light", &MjsDefault::light, reference()) + .property("flex", &MjsDefault::flex, reference()) + .property("mesh", &MjsDefault::mesh, reference()) + .property("material", &MjsDefault::material, reference()) + .property("pair", &MjsDefault::pair, reference()) + .property("equality", &MjsDefault::equality, reference()) + .property("tendon", &MjsDefault::tendon, reference()) + .property("actuator", &MjsDefault::actuator, reference()) + ; + + emscripten::class_("MjsPlugin") + .property("element", &MjsPlugin::element, reference()) + .property("name", &MjsPlugin::name, &MjsPlugin::set_name, reference()) + .property("plugin_name", &MjsPlugin::plugin_name, &MjsPlugin::set_plugin_name, reference()) + .property("active", &MjsPlugin::active, &MjsPlugin::set_active, reference()) + .property("info", &MjsPlugin::info, &MjsPlugin::set_info, reference()) + ; + + emscripten::class_("MjVFS").constructor<>() + // TODO: .property("impl_", &MjVFS::impl_) + ; + + // TODO: should be generated in future CLs -- // + emscripten::register_vector("MjSolverStatVec"); + emscripten::register_vector("MjTimerStatVec"); + emscripten::register_vector("MjWarningStatVec"); + emscripten::register_vector("MjContactVec"); + emscripten::register_vector("MjvLightVec"); + emscripten::register_vector("MjvGLCameraVec"); + emscripten::register_vector("MjvGeomVec"); +} + +// FUNCTIONS +EMSCRIPTEN_DECLARE_VAL_TYPE(NumberArray); +EMSCRIPTEN_DECLARE_VAL_TYPE(String); + +// Raises an error if the given val is null or undefined. +// A macro is used so that the error contains the name of the variable. +// TODO(matijak): Remove this when we can handle strings using UNPACK_STRING? +#define CHECK_VAL(val) \ + if (val.isNull()) { \ + mju_error("Invalid argument: %s is null", #val); \ + } else if (val.isUndefined()) { \ + mju_error("Invalid argument: %s is undefined", #val); \ + } +void error_wrapper(const String& msg) { mju_error("%s\n", msg.as().data()); } + +int mj_copyBack_wrapper(MjSpec& s, const MjModel& m) +{ + return mj_copyBack(s.get(), m.get()); +} + +void mj_step_wrapper(const MjModel& m, MjData& d) +{ + mj_step(m.get(), d.get()); +} + +void mj_step1_wrapper(const MjModel& m, MjData& d) +{ + mj_step1(m.get(), d.get()); +} + +void mj_step2_wrapper(const MjModel& m, MjData& d) +{ + mj_step2(m.get(), d.get()); +} + +void mj_forward_wrapper(const MjModel& m, MjData& d) +{ + mj_forward(m.get(), d.get()); +} + +void mj_inverse_wrapper(const MjModel& m, MjData& d) +{ + mj_inverse(m.get(), d.get()); +} + +void mj_forwardSkip_wrapper(const MjModel& m, MjData& d, int skipstage, int skipsensor) +{ + mj_forwardSkip(m.get(), d.get(), skipstage, skipsensor); +} + +void mj_inverseSkip_wrapper(const MjModel& m, MjData& d, int skipstage, int skipsensor) +{ + mj_inverseSkip(m.get(), d.get(), skipstage, skipsensor); +} + +void mj_defaultLROpt_wrapper(MjLROpt& opt) +{ + mj_defaultLROpt(opt.get()); +} + +void mj_defaultSolRefImp_wrapper(const val& solref, const val& solimp) +{ + UNPACK_VALUE(mjtNum, solref); + UNPACK_VALUE(mjtNum, solimp); + mj_defaultSolRefImp(solref_.data(), solimp_.data()); +} + +void mj_defaultOption_wrapper(MjOption& opt) +{ + mj_defaultOption(opt.get()); +} + +void mj_defaultVisual_wrapper(MjVisual& vis) +{ + mj_defaultVisual(vis.get()); +} + +int mj_sizeModel_wrapper(const MjModel& m) +{ + return mj_sizeModel(m.get()); +} + +void mj_resetData_wrapper(const MjModel& m, MjData& d) +{ + mj_resetData(m.get(), d.get()); +} + +void mj_resetDataDebug_wrapper(const MjModel& m, MjData& d, unsigned char debug_value) +{ + mj_resetDataDebug(m.get(), d.get(), debug_value); +} + +void mj_resetDataKeyframe_wrapper(const MjModel& m, MjData& d, int key) +{ + mj_resetDataKeyframe(m.get(), d.get(), key); +} + +void mj_setConst_wrapper(MjModel& m, MjData& d) +{ + mj_setConst(m.get(), d.get()); +} + +int mjs_activatePlugin_wrapper(MjSpec& s, const String& name) +{ + CHECK_VAL(name); + return mjs_activatePlugin(s.get(), name.as().data()); +} + +int mjs_setDeepCopy_wrapper(MjSpec& s, int deepcopy) +{ + return mjs_setDeepCopy(s.get(), deepcopy); +} + +void mj_printFormattedModel_wrapper(const MjModel& m, const String& filename, const String& float_format) +{ + CHECK_VAL(filename); + CHECK_VAL(float_format); + mj_printFormattedModel(m.get(), filename.as().data(), float_format.as().data()); +} + +void mj_printModel_wrapper(const MjModel& m, const String& filename) +{ + CHECK_VAL(filename); + mj_printModel(m.get(), filename.as().data()); +} + +void mj_printFormattedData_wrapper(const MjModel& m, const MjData& d, const String& filename, const String& float_format) +{ + CHECK_VAL(filename); + CHECK_VAL(float_format); + mj_printFormattedData(m.get(), d.get(), filename.as().data(), float_format.as().data()); +} + +void mj_printData_wrapper(const MjModel& m, const MjData& d, const String& filename) +{ + CHECK_VAL(filename); + mj_printData(m.get(), d.get(), filename.as().data()); +} + +void mju_printMat_wrapper(const NumberArray& mat, int nr, int nc) +{ + UNPACK_ARRAY(mjtNum, mat); + mju_printMat(mat_.data(), nr, nc); +} + +void mj_printScene_wrapper(const MjvScene& s, const String& filename) +{ + CHECK_VAL(filename); + mj_printScene(s.get(), filename.as().data()); +} + +void mj_printFormattedScene_wrapper(const MjvScene& s, const String& filename, const String& float_format) +{ + CHECK_VAL(filename); + CHECK_VAL(float_format); + mj_printFormattedScene(s.get(), filename.as().data(), float_format.as().data()); +} + +void mj_fwdPosition_wrapper(const MjModel& m, MjData& d) +{ + mj_fwdPosition(m.get(), d.get()); +} + +void mj_fwdVelocity_wrapper(const MjModel& m, MjData& d) +{ + mj_fwdVelocity(m.get(), d.get()); +} + +void mj_fwdActuation_wrapper(const MjModel& m, MjData& d) +{ + mj_fwdActuation(m.get(), d.get()); +} + +void mj_fwdAcceleration_wrapper(const MjModel& m, MjData& d) +{ + mj_fwdAcceleration(m.get(), d.get()); +} + +void mj_fwdConstraint_wrapper(const MjModel& m, MjData& d) +{ + mj_fwdConstraint(m.get(), d.get()); +} + +void mj_Euler_wrapper(const MjModel& m, MjData& d) +{ + mj_Euler(m.get(), d.get()); +} + +void mj_RungeKutta_wrapper(const MjModel& m, MjData& d, int N) +{ + mj_RungeKutta(m.get(), d.get(), N); +} + +void mj_implicit_wrapper(const MjModel& m, MjData& d) +{ + mj_implicit(m.get(), d.get()); +} + +void mj_invPosition_wrapper(const MjModel& m, MjData& d) +{ + mj_invPosition(m.get(), d.get()); +} + +void mj_invVelocity_wrapper(const MjModel& m, MjData& d) +{ + mj_invVelocity(m.get(), d.get()); +} + +void mj_invConstraint_wrapper(const MjModel& m, MjData& d) +{ + mj_invConstraint(m.get(), d.get()); +} + +void mj_compareFwdInv_wrapper(const MjModel& m, MjData& d) +{ + mj_compareFwdInv(m.get(), d.get()); +} + +void mj_sensorPos_wrapper(const MjModel& m, MjData& d) +{ + mj_sensorPos(m.get(), d.get()); +} + +void mj_sensorVel_wrapper(const MjModel& m, MjData& d) +{ + mj_sensorVel(m.get(), d.get()); +} + +void mj_sensorAcc_wrapper(const MjModel& m, MjData& d) +{ + mj_sensorAcc(m.get(), d.get()); +} + +void mj_energyPos_wrapper(const MjModel& m, MjData& d) +{ + mj_energyPos(m.get(), d.get()); +} + +void mj_energyVel_wrapper(const MjModel& m, MjData& d) +{ + mj_energyVel(m.get(), d.get()); +} + +void mj_checkPos_wrapper(const MjModel& m, MjData& d) +{ + mj_checkPos(m.get(), d.get()); +} + +void mj_checkVel_wrapper(const MjModel& m, MjData& d) +{ + mj_checkVel(m.get(), d.get()); +} + +void mj_checkAcc_wrapper(const MjModel& m, MjData& d) +{ + mj_checkAcc(m.get(), d.get()); +} + +void mj_kinematics_wrapper(const MjModel& m, MjData& d) +{ + mj_kinematics(m.get(), d.get()); +} + +void mj_comPos_wrapper(const MjModel& m, MjData& d) +{ + mj_comPos(m.get(), d.get()); +} + +void mj_camlight_wrapper(const MjModel& m, MjData& d) +{ + mj_camlight(m.get(), d.get()); +} + +void mj_flex_wrapper(const MjModel& m, MjData& d) +{ + mj_flex(m.get(), d.get()); +} + +void mj_tendon_wrapper(const MjModel& m, MjData& d) +{ + mj_tendon(m.get(), d.get()); +} + +void mj_transmission_wrapper(const MjModel& m, MjData& d) +{ + mj_transmission(m.get(), d.get()); +} + +void mj_crb_wrapper(const MjModel& m, MjData& d) +{ + mj_crb(m.get(), d.get()); +} + +void mj_makeM_wrapper(const MjModel& m, MjData& d) +{ + mj_makeM(m.get(), d.get()); +} + +void mj_factorM_wrapper(const MjModel& m, MjData& d) +{ + mj_factorM(m.get(), d.get()); +} + +void mj_comVel_wrapper(const MjModel& m, MjData& d) +{ + mj_comVel(m.get(), d.get()); +} + +void mj_passive_wrapper(const MjModel& m, MjData& d) +{ + mj_passive(m.get(), d.get()); +} + +void mj_subtreeVel_wrapper(const MjModel& m, MjData& d) +{ + mj_subtreeVel(m.get(), d.get()); +} + +void mj_rnePostConstraint_wrapper(const MjModel& m, MjData& d) +{ + mj_rnePostConstraint(m.get(), d.get()); +} + +void mj_collision_wrapper(const MjModel& m, MjData& d) +{ + mj_collision(m.get(), d.get()); +} + +void mj_makeConstraint_wrapper(const MjModel& m, MjData& d) +{ + mj_makeConstraint(m.get(), d.get()); +} + +void mj_island_wrapper(const MjModel& m, MjData& d) +{ + mj_island(m.get(), d.get()); +} + +void mj_projectConstraint_wrapper(const MjModel& m, MjData& d) +{ + mj_projectConstraint(m.get(), d.get()); +} + +void mj_referenceConstraint_wrapper(const MjModel& m, MjData& d) +{ + mj_referenceConstraint(m.get(), d.get()); +} + +int mj_stateSize_wrapper(const MjModel& m, unsigned int sig) +{ + return mj_stateSize(m.get(), sig); +} + +void mj_extractState_wrapper(const MjModel& m, const NumberArray& src, unsigned int srcsig, const val& dst, unsigned int dstsig) +{ + UNPACK_ARRAY(mjtNum, src); + UNPACK_VALUE(mjtNum, dst); + mj_extractState(m.get(), src_.data(), srcsig, dst_.data(), dstsig); +} + +void mj_setKeyframe_wrapper(MjModel& m, const MjData& d, int k) +{ + mj_setKeyframe(m.get(), d.get(), k); +} + +int mj_addContact_wrapper(const MjModel& m, MjData& d, const MjContact& con) +{ + return mj_addContact(m.get(), d.get(), con.get()); +} + +int mj_isPyramidal_wrapper(const MjModel& m) +{ + return mj_isPyramidal(m.get()); +} + +int mj_isSparse_wrapper(const MjModel& m) +{ + return mj_isSparse(m.get()); +} + +int mj_isDual_wrapper(const MjModel& m) +{ + return mj_isDual(m.get()); +} + +int mj_name2id_wrapper(const MjModel& m, int type, const String& name) +{ + CHECK_VAL(name); + return mj_name2id(m.get(), type, name.as().data()); +} + +std::string mj_id2name_wrapper(const MjModel& m, int type, int id) +{ + return std::string(mj_id2name(m.get(), type, id)); +} + +void mj_objectVelocity_wrapper(const MjModel& m, const MjData& d, int objtype, int objid, const val& res, int flg_local) +{ + UNPACK_VALUE(mjtNum, res); + mj_objectVelocity(m.get(), d.get(), objtype, objid, res_.data(), flg_local); +} + +void mj_objectAcceleration_wrapper(const MjModel& m, const MjData& d, int objtype, int objid, const val& res, int flg_local) +{ + UNPACK_VALUE(mjtNum, res); + mj_objectAcceleration(m.get(), d.get(), objtype, objid, res_.data(), flg_local); +} + +void mj_contactForce_wrapper(const MjModel& m, const MjData& d, int id, const val& result) +{ + UNPACK_VALUE(mjtNum, result); + mj_contactForce(m.get(), d.get(), id, result_.data()); +} + +void mj_local2Global_wrapper(MjData& d, const val& xpos, const val& xmat, const NumberArray& pos, const NumberArray& quat, int body, mjtByte sameframe) +{ + UNPACK_VALUE(mjtNum, xpos); + UNPACK_VALUE(mjtNum, xmat); + UNPACK_ARRAY(mjtNum, pos); + UNPACK_ARRAY(mjtNum, quat); + mj_local2Global(d.get(), xpos_.data(), xmat_.data(), pos_.data(), quat_.data(), body, sameframe); +} + +mjtNum mj_getTotalmass_wrapper(const MjModel& m) +{ + return mj_getTotalmass(m.get()); +} + +void mj_setTotalmass_wrapper(MjModel& m, mjtNum newmass) +{ + mj_setTotalmass(m.get(), newmass); +} + +std::string mj_versionString_wrapper() +{ + return std::string(mj_versionString()); +} + +mjtNum mj_ray_wrapper(const MjModel& m, const MjData& d, const NumberArray& pnt, const NumberArray& vec, const NumberArray& geomgroup, mjtByte flg_static, int bodyexclude, const val& geomid) +{ + UNPACK_ARRAY(mjtNum, pnt); + UNPACK_ARRAY(mjtNum, vec); + UNPACK_ARRAY(mjtByte, geomgroup); + UNPACK_VALUE(int, geomid); + return mj_ray(m.get(), d.get(), pnt_.data(), vec_.data(), geomgroup_.data(), flg_static, bodyexclude, geomid_.data()); +} + +mjtNum mj_rayHfield_wrapper(const MjModel& m, const MjData& d, int geomid, const NumberArray& pnt, const NumberArray& vec) +{ + UNPACK_ARRAY(mjtNum, pnt); + UNPACK_ARRAY(mjtNum, vec); + return mj_rayHfield(m.get(), d.get(), geomid, pnt_.data(), vec_.data()); +} + +mjtNum mj_rayMesh_wrapper(const MjModel& m, const MjData& d, int geomid, const NumberArray& pnt, const NumberArray& vec) +{ + UNPACK_ARRAY(mjtNum, pnt); + UNPACK_ARRAY(mjtNum, vec); + return mj_rayMesh(m.get(), d.get(), geomid, pnt_.data(), vec_.data()); +} + +mjtNum mju_rayGeom_wrapper(const NumberArray& pos, const NumberArray& mat, const NumberArray& size, const NumberArray& pnt, const NumberArray& vec, int geomtype) +{ + UNPACK_ARRAY(mjtNum, pos); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, size); + UNPACK_ARRAY(mjtNum, pnt); + UNPACK_ARRAY(mjtNum, vec); + return mju_rayGeom(pos_.data(), mat_.data(), size_.data(), pnt_.data(), vec_.data(), geomtype); +} + +mjtNum mju_rayFlex_wrapper(const MjModel& m, const MjData& d, int flex_layer, mjtByte flg_vert, mjtByte flg_edge, mjtByte flg_face, mjtByte flg_skin, int flexid, const NumberArray& pnt, const NumberArray& vec, const val& vertid) +{ + UNPACK_ARRAY(mjtNum, pnt); + UNPACK_ARRAY(mjtNum, vec); + UNPACK_VALUE(int, vertid); + return mju_rayFlex(m.get(), d.get(), flex_layer, flg_vert, flg_edge, flg_face, flg_skin, flexid, pnt_.data(), vec_.data(), vertid_.data()); +} + +mjtNum mju_raySkin_wrapper(int nface, int nvert, const NumberArray& face, const NumberArray& vert, const NumberArray& pnt, const NumberArray& vec, const val& vertid) +{ + UNPACK_ARRAY(int, face); + UNPACK_ARRAY(float, vert); + UNPACK_ARRAY(mjtNum, pnt); + UNPACK_ARRAY(mjtNum, vec); + UNPACK_VALUE(int, vertid); + return mju_raySkin(nface, nvert, face_.data(), vert_.data(), pnt_.data(), vec_.data(), vertid_.data()); +} + +void mjv_defaultCamera_wrapper(MjvCamera& cam) +{ + mjv_defaultCamera(cam.get()); +} + +void mjv_defaultFreeCamera_wrapper(const MjModel& m, MjvCamera& cam) +{ + mjv_defaultFreeCamera(m.get(), cam.get()); +} + +void mjv_defaultPerturb_wrapper(MjvPerturb& pert) +{ + mjv_defaultPerturb(pert.get()); +} + +void mjv_room2model_wrapper(const val& modelpos, const val& modelquat, const NumberArray& roompos, const NumberArray& roomquat, const MjvScene& scn) +{ + UNPACK_VALUE(mjtNum, modelpos); + UNPACK_VALUE(mjtNum, modelquat); + UNPACK_ARRAY(mjtNum, roompos); + UNPACK_ARRAY(mjtNum, roomquat); + mjv_room2model(modelpos_.data(), modelquat_.data(), roompos_.data(), roomquat_.data(), scn.get()); +} + +void mjv_model2room_wrapper(const val& roompos, const val& roomquat, const NumberArray& modelpos, const NumberArray& modelquat, const MjvScene& scn) +{ + UNPACK_VALUE(mjtNum, roompos); + UNPACK_VALUE(mjtNum, roomquat); + UNPACK_ARRAY(mjtNum, modelpos); + UNPACK_ARRAY(mjtNum, modelquat); + mjv_model2room(roompos_.data(), roomquat_.data(), modelpos_.data(), modelquat_.data(), scn.get()); +} + +void mjv_cameraInModel_wrapper(const val& headpos, const val& forward, const val& up, const MjvScene& scn) +{ + UNPACK_VALUE(mjtNum, headpos); + UNPACK_VALUE(mjtNum, forward); + UNPACK_VALUE(mjtNum, up); + mjv_cameraInModel(headpos_.data(), forward_.data(), up_.data(), scn.get()); +} + +void mjv_cameraInRoom_wrapper(const val& headpos, const val& forward, const val& up, const MjvScene& scn) +{ + UNPACK_VALUE(mjtNum, headpos); + UNPACK_VALUE(mjtNum, forward); + UNPACK_VALUE(mjtNum, up); + mjv_cameraInRoom(headpos_.data(), forward_.data(), up_.data(), scn.get()); +} + +mjtNum mjv_frustumHeight_wrapper(const MjvScene& scn) +{ + return mjv_frustumHeight(scn.get()); +} + +void mjv_alignToCamera_wrapper(const val& res, const NumberArray& vec, const NumberArray& forward) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + UNPACK_ARRAY(mjtNum, forward); + mjv_alignToCamera(res_.data(), vec_.data(), forward_.data()); +} + +void mjv_moveCamera_wrapper(const MjModel& m, int action, mjtNum reldx, mjtNum reldy, const MjvScene& scn, MjvCamera& cam) +{ + mjv_moveCamera(m.get(), action, reldx, reldy, scn.get(), cam.get()); +} + +void mjv_movePerturb_wrapper(const MjModel& m, const MjData& d, int action, mjtNum reldx, mjtNum reldy, const MjvScene& scn, MjvPerturb& pert) +{ + mjv_movePerturb(m.get(), d.get(), action, reldx, reldy, scn.get(), pert.get()); +} + +void mjv_moveModel_wrapper(const MjModel& m, int action, mjtNum reldx, mjtNum reldy, const NumberArray& roomup, MjvScene& scn) +{ + UNPACK_ARRAY(mjtNum, roomup); + mjv_moveModel(m.get(), action, reldx, reldy, roomup_.data(), scn.get()); +} + +void mjv_initPerturb_wrapper(const MjModel& m, MjData& d, const MjvScene& scn, MjvPerturb& pert) +{ + mjv_initPerturb(m.get(), d.get(), scn.get(), pert.get()); +} + +void mjv_applyPerturbPose_wrapper(const MjModel& m, MjData& d, const MjvPerturb& pert, int flg_paused) +{ + mjv_applyPerturbPose(m.get(), d.get(), pert.get(), flg_paused); +} + +void mjv_applyPerturbForce_wrapper(const MjModel& m, MjData& d, const MjvPerturb& pert) +{ + mjv_applyPerturbForce(m.get(), d.get(), pert.get()); +} + +int mjv_select_wrapper(const MjModel& m, const MjData& d, const MjvOption& vopt, mjtNum aspectratio, mjtNum relx, mjtNum rely, const MjvScene& scn, const val& selpnt, const val& geomid, const val& flexid, const val& skinid) +{ + UNPACK_VALUE(mjtNum, selpnt); + UNPACK_VALUE(int, geomid); + UNPACK_VALUE(int, flexid); + UNPACK_VALUE(int, skinid); + return mjv_select(m.get(), d.get(), vopt.get(), aspectratio, relx, rely, scn.get(), selpnt_.data(), geomid_.data(), flexid_.data(), skinid_.data()); +} + +void mjv_defaultOption_wrapper(MjvOption& opt) +{ + mjv_defaultOption(opt.get()); +} + +void mjv_defaultFigure_wrapper(MjvFigure& fig) +{ + mjv_defaultFigure(fig.get()); +} + +void mjv_initGeom_wrapper(MjvGeom& geom, int type, const NumberArray& size, const NumberArray& pos, const NumberArray& mat, const NumberArray& rgba) +{ + UNPACK_ARRAY(mjtNum, size); + UNPACK_ARRAY(mjtNum, pos); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(float, rgba); + mjv_initGeom(geom.get(), type, size_.data(), pos_.data(), mat_.data(), rgba_.data()); +} + +void mjv_connector_wrapper(MjvGeom& geom, int type, mjtNum width, const NumberArray& from, const NumberArray& to) +{ + UNPACK_ARRAY(mjtNum, from); + UNPACK_ARRAY(mjtNum, to); + mjv_connector(geom.get(), type, width, from_.data(), to_.data()); +} + +void mjv_updateScene_wrapper(const MjModel& m, MjData& d, const MjvOption& opt, const MjvPerturb& pert, MjvCamera& cam, int catmask, MjvScene& scn) +{ + mjv_updateScene(m.get(), d.get(), opt.get(), pert.get(), cam.get(), catmask, scn.get()); +} + +void mjv_addGeoms_wrapper(const MjModel& m, MjData& d, const MjvOption& opt, const MjvPerturb& pert, int catmask, MjvScene& scn) +{ + mjv_addGeoms(m.get(), d.get(), opt.get(), pert.get(), catmask, scn.get()); +} + +void mjv_makeLights_wrapper(const MjModel& m, const MjData& d, MjvScene& scn) +{ + mjv_makeLights(m.get(), d.get(), scn.get()); +} + +void mjv_updateCamera_wrapper(const MjModel& m, const MjData& d, MjvCamera& cam, MjvScene& scn) +{ + mjv_updateCamera(m.get(), d.get(), cam.get(), scn.get()); +} + +void mjv_updateSkin_wrapper(const MjModel& m, const MjData& d, MjvScene& scn) +{ + mjv_updateSkin(m.get(), d.get(), scn.get()); +} + +void mju_writeLog_wrapper(const String& type, const String& msg) +{ + CHECK_VAL(type); + CHECK_VAL(msg); + mju_writeLog(type.as().data(), msg.as().data()); +} + +std::string mjs_getError_wrapper(MjSpec& s) +{ + return std::string(mjs_getError(s.get())); +} + +int mjs_isWarning_wrapper(MjSpec& s) +{ + return mjs_isWarning(s.get()); +} + +void mju_zero3_wrapper(const val& res) +{ + UNPACK_VALUE(mjtNum, res); + mju_zero3(res_.data()); +} + +void mju_copy3_wrapper(const val& res, const NumberArray& data) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, data); + mju_copy3(res_.data(), data_.data()); +} + +void mju_scl3_wrapper(const val& res, const NumberArray& vec, mjtNum scl) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + mju_scl3(res_.data(), vec_.data(), scl); +} + +void mju_add3_wrapper(const val& res, const NumberArray& vec1, const NumberArray& vec2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + mju_add3(res_.data(), vec1_.data(), vec2_.data()); +} + +void mju_sub3_wrapper(const val& res, const NumberArray& vec1, const NumberArray& vec2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + mju_sub3(res_.data(), vec1_.data(), vec2_.data()); +} + +void mju_addTo3_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + mju_addTo3(res_.data(), vec_.data()); +} + +void mju_subFrom3_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + mju_subFrom3(res_.data(), vec_.data()); +} + +void mju_addToScl3_wrapper(const val& res, const NumberArray& vec, mjtNum scl) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + mju_addToScl3(res_.data(), vec_.data(), scl); +} + +void mju_addScl3_wrapper(const val& res, const NumberArray& vec1, const NumberArray& vec2, mjtNum scl) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + mju_addScl3(res_.data(), vec1_.data(), vec2_.data(), scl); +} + +mjtNum mju_normalize3_wrapper(const val& vec) +{ + UNPACK_VALUE(mjtNum, vec); + return mju_normalize3(vec_.data()); +} + +mjtNum mju_norm3_wrapper(const NumberArray& vec) +{ + UNPACK_ARRAY(mjtNum, vec); + return mju_norm3(vec_.data()); +} + +mjtNum mju_dot3_wrapper(const NumberArray& vec1, const NumberArray& vec2) +{ + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + return mju_dot3(vec1_.data(), vec2_.data()); +} + +mjtNum mju_dist3_wrapper(const NumberArray& pos1, const NumberArray& pos2) +{ + UNPACK_ARRAY(mjtNum, pos1); + UNPACK_ARRAY(mjtNum, pos2); + return mju_dist3(pos1_.data(), pos2_.data()); +} + +void mju_mulMatVec3_wrapper(const val& res, const NumberArray& mat, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + mju_mulMatVec3(res_.data(), mat_.data(), vec_.data()); +} + +void mju_mulMatTVec3_wrapper(const val& res, const NumberArray& mat, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + mju_mulMatTVec3(res_.data(), mat_.data(), vec_.data()); +} + +void mju_cross_wrapper(const val& res, const NumberArray& a, const NumberArray& b) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, a); + UNPACK_ARRAY(mjtNum, b); + mju_cross(res_.data(), a_.data(), b_.data()); +} + +void mju_zero4_wrapper(const val& res) +{ + UNPACK_VALUE(mjtNum, res); + mju_zero4(res_.data()); +} + +void mju_unit4_wrapper(const val& res) +{ + UNPACK_VALUE(mjtNum, res); + mju_unit4(res_.data()); +} + +void mju_copy4_wrapper(const val& res, const NumberArray& data) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, data); + mju_copy4(res_.data(), data_.data()); +} + +mjtNum mju_normalize4_wrapper(const val& vec) +{ + UNPACK_VALUE(mjtNum, vec); + return mju_normalize4(vec_.data()); +} + +void mju_transformSpatial_wrapper(const val& res, const NumberArray& vec, int flg_force, const NumberArray& newpos, const NumberArray& oldpos, const NumberArray& rotnew2old) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + UNPACK_ARRAY(mjtNum, newpos); + UNPACK_ARRAY(mjtNum, oldpos); + UNPACK_ARRAY(mjtNum, rotnew2old); + mju_transformSpatial(res_.data(), vec_.data(), flg_force, newpos_.data(), oldpos_.data(), rotnew2old_.data()); +} + +void mju_rotVecQuat_wrapper(const val& res, const NumberArray& vec, const NumberArray& quat) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + UNPACK_ARRAY(mjtNum, quat); + mju_rotVecQuat(res_.data(), vec_.data(), quat_.data()); +} + +void mju_negQuat_wrapper(const val& res, const NumberArray& quat) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, quat); + mju_negQuat(res_.data(), quat_.data()); +} + +void mju_mulQuat_wrapper(const val& res, const NumberArray& quat1, const NumberArray& quat2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, quat1); + UNPACK_ARRAY(mjtNum, quat2); + mju_mulQuat(res_.data(), quat1_.data(), quat2_.data()); +} + +void mju_mulQuatAxis_wrapper(const val& res, const NumberArray& quat, const NumberArray& axis) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, quat); + UNPACK_ARRAY(mjtNum, axis); + mju_mulQuatAxis(res_.data(), quat_.data(), axis_.data()); +} + +void mju_axisAngle2Quat_wrapper(const val& res, const NumberArray& axis, mjtNum angle) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, axis); + mju_axisAngle2Quat(res_.data(), axis_.data(), angle); +} + +void mju_quat2Vel_wrapper(const val& res, const NumberArray& quat, mjtNum dt) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, quat); + mju_quat2Vel(res_.data(), quat_.data(), dt); +} + +void mju_subQuat_wrapper(const val& res, const NumberArray& qa, const NumberArray& qb) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, qa); + UNPACK_ARRAY(mjtNum, qb); + mju_subQuat(res_.data(), qa_.data(), qb_.data()); +} + +void mju_quat2Mat_wrapper(const val& res, const NumberArray& quat) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, quat); + mju_quat2Mat(res_.data(), quat_.data()); +} + +void mju_mat2Quat_wrapper(const val& quat, const NumberArray& mat) +{ + UNPACK_VALUE(mjtNum, quat); + UNPACK_ARRAY(mjtNum, mat); + mju_mat2Quat(quat_.data(), mat_.data()); +} + +void mju_derivQuat_wrapper(const val& res, const NumberArray& quat, const NumberArray& vel) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, quat); + UNPACK_ARRAY(mjtNum, vel); + mju_derivQuat(res_.data(), quat_.data(), vel_.data()); +} + +void mju_quatIntegrate_wrapper(const val& quat, const NumberArray& vel, mjtNum scale) +{ + UNPACK_VALUE(mjtNum, quat); + UNPACK_ARRAY(mjtNum, vel); + mju_quatIntegrate(quat_.data(), vel_.data(), scale); +} + +void mju_quatZ2Vec_wrapper(const val& quat, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, quat); + UNPACK_ARRAY(mjtNum, vec); + mju_quatZ2Vec(quat_.data(), vec_.data()); +} + +int mju_mat2Rot_wrapper(const val& quat, const NumberArray& mat) +{ + UNPACK_VALUE(mjtNum, quat); + UNPACK_ARRAY(mjtNum, mat); + return mju_mat2Rot(quat_.data(), mat_.data()); +} + +void mju_euler2Quat_wrapper(const val& quat, const NumberArray& euler, const String& seq) +{ + CHECK_VAL(seq); + UNPACK_VALUE(mjtNum, quat); + UNPACK_ARRAY(mjtNum, euler); + mju_euler2Quat(quat_.data(), euler_.data(), seq.as().data()); +} + +void mju_mulPose_wrapper(const val& posres, const val& quatres, const NumberArray& pos1, const NumberArray& quat1, const NumberArray& pos2, const NumberArray& quat2) +{ + UNPACK_VALUE(mjtNum, posres); + UNPACK_VALUE(mjtNum, quatres); + UNPACK_ARRAY(mjtNum, pos1); + UNPACK_ARRAY(mjtNum, quat1); + UNPACK_ARRAY(mjtNum, pos2); + UNPACK_ARRAY(mjtNum, quat2); + mju_mulPose(posres_.data(), quatres_.data(), pos1_.data(), quat1_.data(), pos2_.data(), quat2_.data()); +} + +void mju_negPose_wrapper(const val& posres, const val& quatres, const NumberArray& pos, const NumberArray& quat) +{ + UNPACK_VALUE(mjtNum, posres); + UNPACK_VALUE(mjtNum, quatres); + UNPACK_ARRAY(mjtNum, pos); + UNPACK_ARRAY(mjtNum, quat); + mju_negPose(posres_.data(), quatres_.data(), pos_.data(), quat_.data()); +} + +void mju_trnVecPose_wrapper(const val& res, const NumberArray& pos, const NumberArray& quat, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, pos); + UNPACK_ARRAY(mjtNum, quat); + UNPACK_ARRAY(mjtNum, vec); + mju_trnVecPose(res_.data(), pos_.data(), quat_.data(), vec_.data()); +} + +int mju_eig3_wrapper(const val& eigval, const val& eigvec, const val& quat, const NumberArray& mat) +{ + UNPACK_VALUE(mjtNum, eigval); + UNPACK_VALUE(mjtNum, eigvec); + UNPACK_VALUE(mjtNum, quat); + UNPACK_ARRAY(mjtNum, mat); + return mju_eig3(eigval_.data(), eigvec_.data(), quat_.data(), mat_.data()); +} + +mjtNum mju_muscleGain_wrapper(mjtNum len, mjtNum vel, const NumberArray& lengthrange, mjtNum acc0, const NumberArray& prm) +{ + UNPACK_ARRAY(mjtNum, lengthrange); + UNPACK_ARRAY(mjtNum, prm); + return mju_muscleGain(len, vel, lengthrange_.data(), acc0, prm_.data()); +} + +mjtNum mju_muscleBias_wrapper(mjtNum len, const NumberArray& lengthrange, mjtNum acc0, const NumberArray& prm) +{ + UNPACK_ARRAY(mjtNum, lengthrange); + UNPACK_ARRAY(mjtNum, prm); + return mju_muscleBias(len, lengthrange_.data(), acc0, prm_.data()); +} + +mjtNum mju_muscleDynamics_wrapper(mjtNum ctrl, mjtNum act, const NumberArray& prm) +{ + UNPACK_ARRAY(mjtNum, prm); + return mju_muscleDynamics(ctrl, act, prm_.data()); +} + +std::string mju_type2Str_wrapper(int type) +{ + return std::string(mju_type2Str(type)); +} + +int mju_str2Type_wrapper(const String& str) +{ + CHECK_VAL(str); + return mju_str2Type(str.as().data()); +} + +std::string mju_writeNumBytes_wrapper(size_t nbytes) +{ + return std::string(mju_writeNumBytes(nbytes)); +} + +std::string mju_warningText_wrapper(int warning, size_t info) +{ + return std::string(mju_warningText(warning, info)); +} + +mjtNum mju_standardNormal_wrapper(const val& num2) +{ + UNPACK_VALUE(mjtNum, num2); + return mju_standardNormal(num2_.data()); +} + +void mjd_quatIntegrate_wrapper(const NumberArray& vel, mjtNum scale, const val& Dquat, const val& Dvel, const val& Dscale) +{ + UNPACK_ARRAY(mjtNum, vel); + UNPACK_VALUE(mjtNum, Dquat); + UNPACK_VALUE(mjtNum, Dvel); + UNPACK_VALUE(mjtNum, Dscale); + mjd_quatIntegrate(vel_.data(), scale, Dquat_.data(), Dvel_.data(), Dscale_.data()); +} + +std::optional mjs_attach_wrapper(MjsElement& parent, const MjsElement& child, const String& prefix, const String& suffix) +{ + CHECK_VAL(prefix); + CHECK_VAL(suffix); + mjsElement* result = mjs_attach(parent.get(), child.get(), prefix.as().data(), suffix.as().data()); + if (result == nullptr) { + return std::nullopt; + } + return MjsElement(result); +} + +std::optional mjs_addBody_wrapper(MjsBody& body, const MjsDefault& def) +{ + mjsBody* result = mjs_addBody(body.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsBody(result); +} + +std::optional mjs_addSite_wrapper(MjsBody& body, const MjsDefault& def) +{ + mjsSite* result = mjs_addSite(body.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsSite(result); +} + +std::optional mjs_addJoint_wrapper(MjsBody& body, const MjsDefault& def) +{ + mjsJoint* result = mjs_addJoint(body.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsJoint(result); +} + +std::optional mjs_addFreeJoint_wrapper(MjsBody& body) +{ + mjsJoint* result = mjs_addFreeJoint(body.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsJoint(result); +} + +std::optional mjs_addGeom_wrapper(MjsBody& body, const MjsDefault& def) +{ + mjsGeom* result = mjs_addGeom(body.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsGeom(result); +} + +std::optional mjs_addCamera_wrapper(MjsBody& body, const MjsDefault& def) +{ + mjsCamera* result = mjs_addCamera(body.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsCamera(result); +} + +std::optional mjs_addLight_wrapper(MjsBody& body, const MjsDefault& def) +{ + mjsLight* result = mjs_addLight(body.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsLight(result); +} + +std::optional mjs_addFrame_wrapper(MjsBody& body, MjsFrame& parentframe) +{ + mjsFrame* result = mjs_addFrame(body.get(), parentframe.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsFrame(result); +} + +int mjs_delete_wrapper(MjSpec& spec, MjsElement& element) +{ + return mjs_delete(spec.get(), element.get()); +} + +std::optional mjs_addActuator_wrapper(MjSpec& s, const MjsDefault& def) +{ + mjsActuator* result = mjs_addActuator(s.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsActuator(result); +} + +std::optional mjs_addSensor_wrapper(MjSpec& s) +{ + mjsSensor* result = mjs_addSensor(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsSensor(result); +} + +std::optional mjs_addFlex_wrapper(MjSpec& s) +{ + mjsFlex* result = mjs_addFlex(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsFlex(result); +} + +std::optional mjs_addPair_wrapper(MjSpec& s, const MjsDefault& def) +{ + mjsPair* result = mjs_addPair(s.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsPair(result); +} + +std::optional mjs_addExclude_wrapper(MjSpec& s) +{ + mjsExclude* result = mjs_addExclude(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsExclude(result); +} + +std::optional mjs_addEquality_wrapper(MjSpec& s, const MjsDefault& def) +{ + mjsEquality* result = mjs_addEquality(s.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsEquality(result); +} + +std::optional mjs_addTendon_wrapper(MjSpec& s, const MjsDefault& def) +{ + mjsTendon* result = mjs_addTendon(s.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsTendon(result); +} + +std::optional mjs_wrapSite_wrapper(MjsTendon& tendon, const String& name) +{ + CHECK_VAL(name); + mjsWrap* result = mjs_wrapSite(tendon.get(), name.as().data()); + if (result == nullptr) { + return std::nullopt; + } + return MjsWrap(result); +} + +std::optional mjs_wrapGeom_wrapper(MjsTendon& tendon, const String& name, const String& sidesite) +{ + CHECK_VAL(name); + CHECK_VAL(sidesite); + mjsWrap* result = mjs_wrapGeom(tendon.get(), name.as().data(), sidesite.as().data()); + if (result == nullptr) { + return std::nullopt; + } + return MjsWrap(result); +} + +std::optional mjs_wrapJoint_wrapper(MjsTendon& tendon, const String& name, double coef) +{ + CHECK_VAL(name); + mjsWrap* result = mjs_wrapJoint(tendon.get(), name.as().data(), coef); + if (result == nullptr) { + return std::nullopt; + } + return MjsWrap(result); +} + +std::optional mjs_wrapPulley_wrapper(MjsTendon& tendon, double divisor) +{ + mjsWrap* result = mjs_wrapPulley(tendon.get(), divisor); + if (result == nullptr) { + return std::nullopt; + } + return MjsWrap(result); +} + +std::optional mjs_addNumeric_wrapper(MjSpec& s) +{ + mjsNumeric* result = mjs_addNumeric(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsNumeric(result); +} + +std::optional mjs_addText_wrapper(MjSpec& s) +{ + mjsText* result = mjs_addText(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsText(result); +} + +std::optional mjs_addTuple_wrapper(MjSpec& s) +{ + mjsTuple* result = mjs_addTuple(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsTuple(result); +} + +std::optional mjs_addKey_wrapper(MjSpec& s) +{ + mjsKey* result = mjs_addKey(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsKey(result); +} + +std::optional mjs_addPlugin_wrapper(MjSpec& s) +{ + mjsPlugin* result = mjs_addPlugin(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsPlugin(result); +} + +std::optional mjs_addDefault_wrapper(MjSpec& s, const String& classname, const MjsDefault& parent) +{ + CHECK_VAL(classname); + mjsDefault* result = mjs_addDefault(s.get(), classname.as().data(), parent.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsDefault(result); +} + +std::string mjs_setToMotor_wrapper(MjsActuator& actuator) +{ + return std::string(mjs_setToMotor(actuator.get())); +} + +std::string mjs_setToPosition_wrapper(MjsActuator& actuator, double kp, const val& kv, const val& dampratio, const val& timeconst, double inheritrange) +{ + UNPACK_VALUE(double, kv); + UNPACK_VALUE(double, dampratio); + UNPACK_VALUE(double, timeconst); + return std::string(mjs_setToPosition(actuator.get(), kp, kv_.data(), dampratio_.data(), timeconst_.data(), inheritrange)); +} + +std::string mjs_setToIntVelocity_wrapper(MjsActuator& actuator, double kp, const val& kv, const val& dampratio, const val& timeconst, double inheritrange) +{ + UNPACK_VALUE(double, kv); + UNPACK_VALUE(double, dampratio); + UNPACK_VALUE(double, timeconst); + return std::string(mjs_setToIntVelocity(actuator.get(), kp, kv_.data(), dampratio_.data(), timeconst_.data(), inheritrange)); +} + +std::string mjs_setToVelocity_wrapper(MjsActuator& actuator, double kv) +{ + return std::string(mjs_setToVelocity(actuator.get(), kv)); +} + +std::string mjs_setToDamper_wrapper(MjsActuator& actuator, double kv) +{ + return std::string(mjs_setToDamper(actuator.get(), kv)); +} + +std::string mjs_setToCylinder_wrapper(MjsActuator& actuator, double timeconst, double bias, double area, double diameter) +{ + return std::string(mjs_setToCylinder(actuator.get(), timeconst, bias, area, diameter)); +} + +std::string mjs_setToMuscle_wrapper(MjsActuator& actuator, const val& timeconst, double tausmooth, const val& range, double force, double scale, double lmin, double lmax, double vmax, double fpmax, double fvmax) +{ + UNPACK_VALUE(double, timeconst); + UNPACK_VALUE(double, range); + return std::string(mjs_setToMuscle(actuator.get(), timeconst_.data(), tausmooth, range_.data(), force, scale, lmin, lmax, vmax, fpmax, fvmax)); +} + +std::string mjs_setToAdhesion_wrapper(MjsActuator& actuator, double gain) +{ + return std::string(mjs_setToAdhesion(actuator.get(), gain)); +} + +std::optional mjs_addMesh_wrapper(MjSpec& s, const MjsDefault& def) +{ + mjsMesh* result = mjs_addMesh(s.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsMesh(result); +} + +std::optional mjs_addHField_wrapper(MjSpec& s) +{ + mjsHField* result = mjs_addHField(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsHField(result); +} + +std::optional mjs_addSkin_wrapper(MjSpec& s) +{ + mjsSkin* result = mjs_addSkin(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsSkin(result); +} + +std::optional mjs_addTexture_wrapper(MjSpec& s) +{ + mjsTexture* result = mjs_addTexture(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsTexture(result); +} + +std::optional mjs_addMaterial_wrapper(MjSpec& s, const MjsDefault& def) +{ + mjsMaterial* result = mjs_addMaterial(s.get(), def.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsMaterial(result); +} + +int mjs_makeMesh_wrapper(MjsMesh& mesh, mjtMeshBuiltin builtin, const val& params, int nparams) +{ + UNPACK_VALUE(double, params); + return mjs_makeMesh(mesh.get(), builtin, params_.data(), nparams); +} + +std::optional mjs_getSpec_wrapper(MjsElement& element) +{ + mjSpec* result = mjs_getSpec(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjSpec(result); +} + +std::optional mjs_findSpec_wrapper(MjSpec& spec, const String& name) +{ + CHECK_VAL(name); + mjSpec* result = mjs_findSpec(spec.get(), name.as().data()); + if (result == nullptr) { + return std::nullopt; + } + return MjSpec(result); +} + +std::optional mjs_findBody_wrapper(MjSpec& s, const String& name) +{ + CHECK_VAL(name); + mjsBody* result = mjs_findBody(s.get(), name.as().data()); + if (result == nullptr) { + return std::nullopt; + } + return MjsBody(result); +} + +std::optional mjs_findElement_wrapper(MjSpec& s, mjtObj type, const String& name) +{ + CHECK_VAL(name); + mjsElement* result = mjs_findElement(s.get(), type, name.as().data()); + if (result == nullptr) { + return std::nullopt; + } + return MjsElement(result); +} + +std::optional mjs_findChild_wrapper(MjsBody& body, const String& name) +{ + CHECK_VAL(name); + mjsBody* result = mjs_findChild(body.get(), name.as().data()); + if (result == nullptr) { + return std::nullopt; + } + return MjsBody(result); +} + +std::optional mjs_getParent_wrapper(MjsElement& element) +{ + mjsBody* result = mjs_getParent(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsBody(result); +} + +std::optional mjs_getFrame_wrapper(MjsElement& element) +{ + mjsFrame* result = mjs_getFrame(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsFrame(result); +} + +std::optional mjs_findFrame_wrapper(MjSpec& s, const String& name) +{ + CHECK_VAL(name); + mjsFrame* result = mjs_findFrame(s.get(), name.as().data()); + if (result == nullptr) { + return std::nullopt; + } + return MjsFrame(result); +} + +std::optional mjs_getDefault_wrapper(MjsElement& element) +{ + mjsDefault* result = mjs_getDefault(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsDefault(result); +} + +std::optional mjs_findDefault_wrapper(MjSpec& s, const String& classname) +{ + CHECK_VAL(classname); + mjsDefault* result = mjs_findDefault(s.get(), classname.as().data()); + if (result == nullptr) { + return std::nullopt; + } + return MjsDefault(result); +} + +std::optional mjs_getSpecDefault_wrapper(MjSpec& s) +{ + mjsDefault* result = mjs_getSpecDefault(s.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsDefault(result); +} + +int mjs_getId_wrapper(MjsElement& element) +{ + return mjs_getId(element.get()); +} + +std::optional mjs_firstChild_wrapper(MjsBody& body, mjtObj type, int recurse) +{ + mjsElement* result = mjs_firstChild(body.get(), type, recurse); + if (result == nullptr) { + return std::nullopt; + } + return MjsElement(result); +} + +std::optional mjs_nextChild_wrapper(MjsBody& body, MjsElement& child, int recurse) +{ + mjsElement* result = mjs_nextChild(body.get(), child.get(), recurse); + if (result == nullptr) { + return std::nullopt; + } + return MjsElement(result); +} + +std::optional mjs_firstElement_wrapper(MjSpec& s, mjtObj type) +{ + mjsElement* result = mjs_firstElement(s.get(), type); + if (result == nullptr) { + return std::nullopt; + } + return MjsElement(result); +} + +std::optional mjs_nextElement_wrapper(MjSpec& s, MjsElement& element) +{ + mjsElement* result = mjs_nextElement(s.get(), element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsElement(result); +} + +std::optional mjs_getWrapTarget_wrapper(MjsWrap& wrap) +{ + mjsElement* result = mjs_getWrapTarget(wrap.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsElement(result); +} + +std::optional mjs_getWrapSideSite_wrapper(MjsWrap& wrap) +{ + mjsSite* result = mjs_getWrapSideSite(wrap.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsSite(result); +} + +double mjs_getWrapDivisor_wrapper(MjsWrap& wrap) +{ + return mjs_getWrapDivisor(wrap.get()); +} + +double mjs_getWrapCoef_wrapper(MjsWrap& wrap) +{ + return mjs_getWrapCoef(wrap.get()); +} + +int mjs_setName_wrapper(MjsElement& element, const String& name) +{ + CHECK_VAL(name); + return mjs_setName(element.get(), name.as().data()); +} + +std::string mjs_getName_wrapper(MjsElement& element) +{ + return *mjs_getName(element.get()); +} + +int mjs_getWrapNum_wrapper(const MjsTendon& tendonspec) +{ + return mjs_getWrapNum(tendonspec.get()); +} + +std::optional mjs_getWrap_wrapper(const MjsTendon& tendonspec, int i) +{ + mjsWrap* result = mjs_getWrap(tendonspec.get(), i); + if (result == nullptr) { + return std::nullopt; + } + return MjsWrap(result); +} + +void mjs_setDefault_wrapper(MjsElement& element, const MjsDefault& def) +{ + mjs_setDefault(element.get(), def.get()); +} + +int mjs_setFrame_wrapper(MjsElement& dest, MjsFrame& frame) +{ + return mjs_setFrame(dest.get(), frame.get()); +} + +std::string mjs_resolveOrientation_wrapper(const val& quat, mjtByte degree, const String& sequence, const MjsOrientation& orientation) +{ + CHECK_VAL(sequence); + UNPACK_VALUE(double, quat); + return std::string(mjs_resolveOrientation(quat_.data(), degree, sequence.as().data(), orientation.get())); +} + +void mjs_deleteUserValue_wrapper(MjsElement& element, const String& key) +{ + CHECK_VAL(key); + mjs_deleteUserValue(element.get(), key.as().data()); +} + +int mjs_sensorDim_wrapper(const MjsSensor& sensor) +{ + return mjs_sensorDim(sensor.get()); +} + +void mjs_defaultSpec_wrapper(MjSpec& spec) +{ + mjs_defaultSpec(spec.get()); +} + +void mjs_defaultOrientation_wrapper(MjsOrientation& orient) +{ + mjs_defaultOrientation(orient.get()); +} + +void mjs_defaultBody_wrapper(MjsBody& body) +{ + mjs_defaultBody(body.get()); +} + +void mjs_defaultFrame_wrapper(MjsFrame& frame) +{ + mjs_defaultFrame(frame.get()); +} + +void mjs_defaultJoint_wrapper(MjsJoint& joint) +{ + mjs_defaultJoint(joint.get()); +} + +void mjs_defaultGeom_wrapper(MjsGeom& geom) +{ + mjs_defaultGeom(geom.get()); +} + +void mjs_defaultSite_wrapper(MjsSite& site) +{ + mjs_defaultSite(site.get()); +} + +void mjs_defaultCamera_wrapper(MjsCamera& camera) +{ + mjs_defaultCamera(camera.get()); +} + +void mjs_defaultLight_wrapper(MjsLight& light) +{ + mjs_defaultLight(light.get()); +} + +void mjs_defaultFlex_wrapper(MjsFlex& flex) +{ + mjs_defaultFlex(flex.get()); +} + +void mjs_defaultMesh_wrapper(MjsMesh& mesh) +{ + mjs_defaultMesh(mesh.get()); +} + +void mjs_defaultHField_wrapper(MjsHField& hfield) +{ + mjs_defaultHField(hfield.get()); +} + +void mjs_defaultSkin_wrapper(MjsSkin& skin) +{ + mjs_defaultSkin(skin.get()); +} + +void mjs_defaultTexture_wrapper(MjsTexture& texture) +{ + mjs_defaultTexture(texture.get()); +} + +void mjs_defaultMaterial_wrapper(MjsMaterial& material) +{ + mjs_defaultMaterial(material.get()); +} + +void mjs_defaultPair_wrapper(MjsPair& pair) +{ + mjs_defaultPair(pair.get()); +} + +void mjs_defaultEquality_wrapper(MjsEquality& equality) +{ + mjs_defaultEquality(equality.get()); +} + +void mjs_defaultTendon_wrapper(MjsTendon& tendon) +{ + mjs_defaultTendon(tendon.get()); +} + +void mjs_defaultActuator_wrapper(MjsActuator& actuator) +{ + mjs_defaultActuator(actuator.get()); +} + +void mjs_defaultSensor_wrapper(MjsSensor& sensor) +{ + mjs_defaultSensor(sensor.get()); +} + +void mjs_defaultNumeric_wrapper(MjsNumeric& numeric) +{ + mjs_defaultNumeric(numeric.get()); +} + +void mjs_defaultText_wrapper(MjsText& text) +{ + mjs_defaultText(text.get()); +} + +void mjs_defaultTuple_wrapper(MjsTuple& tuple) +{ + mjs_defaultTuple(tuple.get()); +} + +void mjs_defaultKey_wrapper(MjsKey& key) +{ + mjs_defaultKey(key.get()); +} + +void mjs_defaultPlugin_wrapper(MjsPlugin& plugin) +{ + mjs_defaultPlugin(plugin.get()); +} + +std::optional mjs_asBody_wrapper(MjsElement& element) +{ + mjsBody* result = mjs_asBody(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsBody(result); +} + +std::optional mjs_asGeom_wrapper(MjsElement& element) +{ + mjsGeom* result = mjs_asGeom(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsGeom(result); +} + +std::optional mjs_asJoint_wrapper(MjsElement& element) +{ + mjsJoint* result = mjs_asJoint(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsJoint(result); +} + +std::optional mjs_asSite_wrapper(MjsElement& element) +{ + mjsSite* result = mjs_asSite(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsSite(result); +} + +std::optional mjs_asCamera_wrapper(MjsElement& element) +{ + mjsCamera* result = mjs_asCamera(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsCamera(result); +} + +std::optional mjs_asLight_wrapper(MjsElement& element) +{ + mjsLight* result = mjs_asLight(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsLight(result); +} + +std::optional mjs_asFrame_wrapper(MjsElement& element) +{ + mjsFrame* result = mjs_asFrame(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsFrame(result); +} + +std::optional mjs_asActuator_wrapper(MjsElement& element) +{ + mjsActuator* result = mjs_asActuator(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsActuator(result); +} + +std::optional mjs_asSensor_wrapper(MjsElement& element) +{ + mjsSensor* result = mjs_asSensor(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsSensor(result); +} + +std::optional mjs_asFlex_wrapper(MjsElement& element) +{ + mjsFlex* result = mjs_asFlex(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsFlex(result); +} + +std::optional mjs_asPair_wrapper(MjsElement& element) +{ + mjsPair* result = mjs_asPair(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsPair(result); +} + +std::optional mjs_asEquality_wrapper(MjsElement& element) +{ + mjsEquality* result = mjs_asEquality(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsEquality(result); +} + +std::optional mjs_asExclude_wrapper(MjsElement& element) +{ + mjsExclude* result = mjs_asExclude(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsExclude(result); +} + +std::optional mjs_asTendon_wrapper(MjsElement& element) +{ + mjsTendon* result = mjs_asTendon(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsTendon(result); +} + +std::optional mjs_asNumeric_wrapper(MjsElement& element) +{ + mjsNumeric* result = mjs_asNumeric(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsNumeric(result); +} + +std::optional mjs_asText_wrapper(MjsElement& element) +{ + mjsText* result = mjs_asText(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsText(result); +} + +std::optional mjs_asTuple_wrapper(MjsElement& element) +{ + mjsTuple* result = mjs_asTuple(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsTuple(result); +} + +std::optional mjs_asKey_wrapper(MjsElement& element) +{ + mjsKey* result = mjs_asKey(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsKey(result); +} + +std::optional mjs_asMesh_wrapper(MjsElement& element) +{ + mjsMesh* result = mjs_asMesh(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsMesh(result); +} + +std::optional mjs_asHField_wrapper(MjsElement& element) +{ + mjsHField* result = mjs_asHField(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsHField(result); +} + +std::optional mjs_asSkin_wrapper(MjsElement& element) +{ + mjsSkin* result = mjs_asSkin(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsSkin(result); +} + +std::optional mjs_asTexture_wrapper(MjsElement& element) +{ + mjsTexture* result = mjs_asTexture(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsTexture(result); +} + +std::optional mjs_asMaterial_wrapper(MjsElement& element) +{ + mjsMaterial* result = mjs_asMaterial(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsMaterial(result); +} + +std::optional mjs_asPlugin_wrapper(MjsElement& element) +{ + mjsPlugin* result = mjs_asPlugin(element.get()); + if (result == nullptr) { + return std::nullopt; + } + return MjsPlugin(result); +} + +void mju_printMatSparse_wrapper(const NumberArray& mat, const NumberArray& rownnz, const NumberArray& rowadr, const NumberArray& colind) +{ + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(int, rownnz); + UNPACK_ARRAY(int, rowadr); + UNPACK_ARRAY(int, colind); + CHECK_SIZES(rownnz, rowadr); + mju_printMatSparse(mat_.data(), rowadr_.size(), + rownnz_.data(), + rowadr_.data(), + colind_.data()); +} + +void mj_solveM_wrapper(const MjModel& m, MjData& d, const val& x, const NumberArray& y) +{ + UNPACK_VALUE(mjtNum, x); + UNPACK_ARRAY(mjtNum, y); + CHECK_SIZES(x, y); + CHECK_DIVISIBLE(x, m.nv()); + mj_solveM(m.get(), d.get(), x_.data(), y_.data(), x_div.quot); +} + +void mj_solveM2_wrapper(const MjModel& m, MjData& d, + const val& x, const NumberArray& y, + const NumberArray& sqrtInvD) { + UNPACK_VALUE(mjtNum, x); + UNPACK_ARRAY(mjtNum, y); + UNPACK_ARRAY(mjtNum, sqrtInvD); + CHECK_SIZES(x, y); + CHECK_SIZE(sqrtInvD, m.nv()); + CHECK_DIVISIBLE(x, m.nv()); + mj_solveM2(m.get(), d.get(), x_.data(), y_.data(), sqrtInvD_.data(), x_div.quot); +} + +void mj_rne_wrapper(const MjModel& m, MjData& d, int flg_acc, const val& result) +{ + UNPACK_VALUE(mjtNum, result); + CHECK_SIZE(result, m.nv()); + mj_rne(m.get(), d.get(), flg_acc, result_.data()); +} + +int mj_saveLastXML_wrapper(const String& filename, const MjModel& m) { + CHECK_VAL(filename); + std::array error; + int result = mj_saveLastXML(filename.as().data(), m.get(), error.data(), error.size()); + if (!result) { + mju_error("%s", error.data()); + } + return result; +} + +int mj_setLengthRange_wrapper(const MjModel& m, const MjData& d, int index, const MjLROpt& opt) { + std::array error; + int result = mj_setLengthRange(m.get(), d.get(), index, opt.get(), error.data(), error.size()); + if (!result) { + mju_error("%s", error.data()); + } + return result; +} + +void mj_constraintUpdate_wrapper(const MjModel& m, MjData& d, const NumberArray& jar, const val& cost, int flg_coneHessian) +{ + UNPACK_ARRAY(mjtNum, jar); + UNPACK_NULLABLE_VALUE(mjtNum, cost); + CHECK_SIZE(cost, 1); + CHECK_SIZE(jar, d.nefc()); + mj_constraintUpdate(m.get(), d.get(), jar_.data(), cost_.data(), flg_coneHessian); +} + +void mj_getState_wrapper(const MjModel& m, const MjData& d, const val& state, unsigned int spec) +{ + UNPACK_VALUE(mjtNum, state); + CHECK_SIZE(state, mj_stateSize(m.get(), spec)); + mj_getState(m.get(), d.get(), state_.data(), spec); +} + +void mj_setState_wrapper(const MjModel& m, MjData& d, const NumberArray& state, unsigned int spec) +{ + UNPACK_ARRAY(mjtNum, state); + CHECK_SIZE(state, mj_stateSize(m.get(), spec)); + mj_setState(m.get(), d.get(), state_.data(), spec); +} + +void mj_mulJacVec_wrapper(const MjModel& m, const MjData& d, const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(res, d.nefc()); + CHECK_SIZE(vec, m.nv()); + mj_mulJacVec(m.get(), d.get(), res_.data(), vec_.data()); +} + +void mj_mulJacTVec_wrapper(const MjModel& m, const MjData& d, const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(res, m.nv()); + CHECK_SIZE(vec, d.nefc()); + mj_mulJacTVec(m.get(), d.get(), res_.data(), vec_.data()); +} + +void mj_jac_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, const NumberArray& point, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + UNPACK_ARRAY(mjtNum, point); + CHECK_SIZE(point, 3); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jac(m.get(), d.get(), jacp_.data(), jacr_.data(), point_.data(), body); +} + +void mj_jacBody_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacBody(m.get(), d.get(), jacp_.data(), jacr_.data(), body); +} + +void mj_jacBodyCom_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacBodyCom(m.get(), d.get(), jacp_.data(), jacr_.data(), body); +} + +void mj_jacSubtreeCom_wrapper(const MjModel& m, MjData& d, const val& jacp, int body) +{ + UNPACK_VALUE(mjtNum, jacp); + CHECK_SIZE(jacp, m.nv() * 3); + mj_jacSubtreeCom(m.get(), d.get(), jacp_.data(), body); +} + +void mj_jacGeom_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, int geom) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacGeom(m.get(), d.get(), jacp_.data(), jacr_.data(), geom); +} + +void mj_jacSite_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, int site) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacSite(m.get(), d.get(), jacp_.data(), jacr_.data(), site); +} + +void mj_jacPointAxis_wrapper(const MjModel& m, MjData& d, const val& jacPoint, const val& jacAxis, const NumberArray& point, const NumberArray& axis, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacPoint); + UNPACK_NULLABLE_VALUE(mjtNum, jacAxis); + UNPACK_ARRAY(mjtNum, point); + UNPACK_ARRAY(mjtNum, axis); + CHECK_SIZE(point, 3); + CHECK_SIZE(axis, 3); + CHECK_SIZE(jacPoint, m.nv() * 3); + CHECK_SIZE(jacAxis, m.nv() * 3); + mj_jacPointAxis(m.get(), d.get(), jacPoint_.data(), jacAxis_.data(), point_.data(), axis_.data(), body); +} + +void mj_jacDot_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, const NumberArray& point, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + UNPACK_ARRAY(mjtNum, point); + CHECK_SIZE(point, 3); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacDot(m.get(), d.get(), jacp_.data(), jacr_.data(), point_.data(), body); +} + +void mj_angmomMat_wrapper(const MjModel& m, MjData& d, const val& mat, int body) +{ + UNPACK_VALUE(mjtNum, mat); + CHECK_SIZE(mat, m.nv() * 3); + mj_angmomMat(m.get(), d.get(), mat_.data(), body); +} + +void mj_fullM_wrapper(const MjModel& m, const val& dst, const NumberArray& M) +{ + UNPACK_VALUE(mjtNum, dst); + UNPACK_ARRAY(mjtNum, M); + CHECK_SIZE(M, m.nM()); + CHECK_SIZE(dst, m.nv() * m.nv()); + mj_fullM(m.get(), dst_.data(), M_.data()); +} + +void mj_mulM_wrapper(const MjModel& m, const MjData& d, const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(res, m.nv()); + CHECK_SIZE(vec, m.nv()); + mj_mulM(m.get(), d.get(), res_.data(), vec_.data()); +} + +void mj_mulM2_wrapper(const MjModel& m, const MjData& d, const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(res, m.nv()); + CHECK_SIZE(vec, m.nv()); + mj_mulM2(m.get(), d.get(), res_.data(), vec_.data()); +} + +void mj_addM_wrapper(const MjModel& m, MjData& d, const val& dst, const val& rownnz, const val& rowadr, const val& colind) +{ + UNPACK_VALUE(mjtNum, dst); + UNPACK_NULLABLE_VALUE(int, rownnz); + UNPACK_NULLABLE_VALUE(int, rowadr); + UNPACK_NULLABLE_VALUE(int, colind); + CHECK_SIZE(rownnz, m.nv()); + CHECK_SIZE(rowadr, m.nv()); + CHECK_SIZE(colind, m.nM()); + CHECK_SIZE(dst, m.nM()); + mj_addM(m.get(), d.get(), dst_.data(), rownnz_.data(), rowadr_.data(), colind_.data()); +} + +void mj_applyFT_wrapper(const MjModel& m, MjData& d, const NumberArray& force, const NumberArray& torque, const NumberArray& point, int body, const val& qfrc_target) +{ + UNPACK_NULLABLE_ARRAY(mjtNum, force); + UNPACK_NULLABLE_ARRAY(mjtNum, torque); + UNPACK_ARRAY(mjtNum, point); + UNPACK_VALUE(mjtNum, qfrc_target); + CHECK_SIZE(qfrc_target, m.nv()); + CHECK_SIZE(force, 3); + CHECK_SIZE(torque, 3); + CHECK_SIZE(point, 3); + mj_applyFT(m.get(), d.get(), force_.data(), torque_.data(), point_.data(), body, qfrc_target_.data()); +} + +mjtNum mj_geomDistance_wrapper(const MjModel& m, const MjData& d, int geom1, int geom2, mjtNum distmax, const val& fromto) +{ + UNPACK_NULLABLE_VALUE(mjtNum, fromto); + CHECK_SIZE(fromto, 6); + return mj_geomDistance(m.get(), d.get(), geom1, geom2, distmax, fromto_.data()); +} + +void mj_differentiatePos_wrapper(const MjModel& m, const val& qvel, mjtNum dt, const NumberArray& qpos1, const NumberArray& qpos2) +{ + UNPACK_VALUE(mjtNum, qvel); + UNPACK_ARRAY(mjtNum, qpos1); + UNPACK_ARRAY(mjtNum, qpos2); + CHECK_SIZE(qvel, m.nv()); + CHECK_SIZE(qpos1, m.nq()); + CHECK_SIZE(qpos2, m.nq()); + mj_differentiatePos(m.get(), qvel_.data(), dt, qpos1_.data(), qpos2_.data()); +} + +void mj_integratePos_wrapper(const MjModel& m, const val& qpos, const NumberArray& qvel, mjtNum dt) +{ + UNPACK_VALUE(mjtNum, qpos); + UNPACK_ARRAY(mjtNum, qvel); + CHECK_SIZE(qpos, m.nq()); + CHECK_SIZE(qvel, m.nv()); + mj_integratePos(m.get(), qpos_.data(), qvel_.data(), dt); +} + +void mj_normalizeQuat_wrapper(const MjModel& m, const val& qpos) +{ + UNPACK_VALUE(mjtNum, qpos); + CHECK_SIZE(qpos, m.nq()); + mj_normalizeQuat(m.get(), qpos_.data()); +} + +void mj_multiRay_wrapper(const MjModel& m, MjData& d, const NumberArray& pnt, const NumberArray& vec, const val& geomgroup, mjtByte flg_static, int bodyexclude, const val& geomid, const val& dist, int nray, mjtNum cutoff) +{ + UNPACK_ARRAY(mjtNum, pnt); + UNPACK_ARRAY(mjtNum, vec); + UNPACK_VALUE(mjtByte, geomgroup); + UNPACK_VALUE(int, geomid); + UNPACK_VALUE(mjtNum, dist); + CHECK_SIZE(dist, nray); + CHECK_SIZE(geomid, nray); + CHECK_SIZE(vec, 3 * nray); + mj_multiRay(m.get(), d.get(), pnt_.data(), vec_.data(), geomgroup_.data(), flg_static, bodyexclude, geomid_.data(), dist_.data(), nray, cutoff); +} + +void mju_zero_wrapper(const val& res) +{ + UNPACK_VALUE(mjtNum, res); + mju_zero(res_.data(), res_.size()); +} + +void mju_fill_wrapper(const val& res, mjtNum val) +{ + UNPACK_VALUE(mjtNum, res); + mju_fill(res_.data(), val, res_.size()); +} + +void mju_copy_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_copy(res_.data(), vec_.data(), res_.size()); +} + +mjtNum mju_sum_wrapper(const NumberArray& vec) +{ + UNPACK_ARRAY(mjtNum, vec); + return mju_sum(vec_.data(), vec_.size()); +} + +mjtNum mju_L1_wrapper(const NumberArray& vec) +{ + UNPACK_ARRAY(mjtNum, vec); + return mju_L1(vec_.data(), vec_.size()); +} + +void mju_scl_wrapper(const val& res, const NumberArray& vec, mjtNum scl) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_scl(res_.data(), vec_.data(), scl, res_.size()); +} + +void mju_add_wrapper(const val& res, const NumberArray& vec1, const NumberArray& vec2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + CHECK_SIZES(res, vec1); + CHECK_SIZES(res, vec2); + mju_add(res_.data(), vec1_.data(), vec2_.data(), res_.size()); +} + +void mju_sub_wrapper(const val& res, const NumberArray& vec1, const NumberArray& vec2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + CHECK_SIZES(res, vec1); + CHECK_SIZES(res, vec2); + mju_sub(res_.data(), vec1_.data(), vec2_.data(), res_.size()); +} + +void mju_addTo_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_addTo(res_.data(), vec_.data(), res_.size()); +} + +void mju_subFrom_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_subFrom(res_.data(), vec_.data(), res_.size()); +} + +void mju_addToScl_wrapper(const val& res, const NumberArray& vec, mjtNum scl) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_addToScl(res_.data(), vec_.data(), scl, res_.size()); +} + +void mju_addScl_wrapper(const val& res, const NumberArray& vec1, const NumberArray& vec2, mjtNum scl) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + CHECK_SIZES(res, vec1); + CHECK_SIZES(res, vec2); + mju_addScl(res_.data(), vec1_.data(), vec2_.data(), scl, res_.size()); +} + +mjtNum mju_normalize_wrapper(const val& res) +{ + UNPACK_VALUE(mjtNum, res); + return mju_normalize(res_.data(), res_.size()); +} + +mjtNum mju_norm_wrapper(const NumberArray& res) +{ + UNPACK_ARRAY(mjtNum, res); + return mju_norm(res_.data(), res_.size()); +} + +mjtNum mju_dot_wrapper(const NumberArray& vec1, const NumberArray& vec2) +{ + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + CHECK_SIZES(vec1, vec2); + return mju_dot(vec1_.data(), vec2_.data(), vec1_.size()); +} + +void mju_mulMatVec_wrapper(const val& res, const NumberArray& mat, + const NumberArray& vec, int nr, int nc) { + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(res, nr); + CHECK_SIZE(vec, nc); + mju_mulMatVec(res_.data(), mat_.data(), vec_.data(), nr, nc); +} + +void mju_mulMatTVec_wrapper(const val& res, const NumberArray& mat, + const NumberArray& vec, int nr, int nc) { + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(res, nc); + CHECK_SIZE(vec, nr); + mju_mulMatTVec(res_.data(), mat_.data(), vec_.data(), nr, nc); +} + +mjtNum mju_mulVecMatVec_wrapper(const NumberArray& vec1, const NumberArray& mat, const NumberArray& vec2) +{ + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec2); + int64_t vec1_times_vec2 = vec1_.size() * vec2_.size(); + CHECK_SIZES(vec1, vec2); + CHECK_SIZE(mat, vec1_times_vec2); + return mju_mulVecMatVec(vec1_.data(), mat_.data(), vec2_.data(), vec1_.size()); +} + +void mju_transpose_wrapper(const val& res, const NumberArray& mat, int nr, int nc) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(res, nr * nc); + mju_transpose(res_.data(), mat_.data(), nr, nc); +} + +void mju_symmetrize_wrapper(const val& res, const NumberArray& mat, int n) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + CHECK_SIZE(mat, n * n); + CHECK_SIZE(res, n * n); + mju_symmetrize(res_.data(), mat_.data(), n); +} + +void mju_eye_wrapper(const val& mat) +{ + UNPACK_VALUE(mjtNum, mat); + CHECK_PERFECT_SQUARE(mat); + mju_eye(mat_.data(), mat_sqrt); +} + +void mju_mulMatMat_wrapper(const val& res, const NumberArray& mat1, const NumberArray& mat2, int r1, int c1, int c2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat1); + UNPACK_ARRAY(mjtNum, mat2); + CHECK_SIZE(res, r1 * c2); + CHECK_SIZE(mat1, r1 * c1); + CHECK_SIZE(mat2, c1 * c2); + mju_mulMatMat(res_.data(), mat1_.data(), mat2_.data(), r1, c1, c2); +} + +void mju_mulMatMatT_wrapper(const val& res, const NumberArray& mat1, const NumberArray& mat2, int r1, int c1, int r2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat1); + UNPACK_ARRAY(mjtNum, mat2); + CHECK_SIZE(res, r1 * r2); + CHECK_SIZE(mat1, r1 * c1); + CHECK_SIZE(mat2, r2 * c1); + mju_mulMatMatT(res_.data(), mat1_.data(), mat2_.data(), r1, c1, r2); +} + +void mju_mulMatTMat_wrapper(const val& res, const NumberArray& mat1, const NumberArray& mat2, int r1, int c1, int c2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat1); + UNPACK_ARRAY(mjtNum, mat2); + CHECK_SIZE(res, c1 * c2); + CHECK_SIZE(mat1, r1 * c1); + CHECK_SIZE(mat2, r1 * c2); + mju_mulMatTMat(res_.data(), mat1_.data(), mat2_.data(), r1, c1, c2); +} + +void mju_sqrMatTD_wrapper(const val& res, const NumberArray& mat, const NumberArray& diag, int nr, int nc) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, diag); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(res, nc * nc); + CHECK_SIZE(diag, nr); + mju_sqrMatTD(res_.data(), mat_.data(), diag_.data(), nr, nc); +} + +int mju_dense2sparse_wrapper(const val& res, const NumberArray& mat, int nr, int nc, const val& rownnz, const val& rowadr, const val& colind) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_VALUE(int, rownnz); + UNPACK_VALUE(int, rowadr); + UNPACK_VALUE(int, colind); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(rownnz, nr); + CHECK_SIZE(rowadr, nr); + CHECK_SIZE(colind, res_.size()); + return mju_dense2sparse(res_.data(), mat_.data(), nr, nc, rownnz_.data(), rowadr_.data(), colind_.data(), res_.size()); +} + +void mju_sparse2dense_wrapper(const val& res, const NumberArray& mat, int nr, int nc, const NumberArray& rownnz, const NumberArray& rowadr, const NumberArray& colind) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(int, rownnz); + UNPACK_ARRAY(int, rowadr); + UNPACK_ARRAY(int, colind); + CHECK_SIZE(res, nr * nc); + CHECK_SIZE(rownnz, nr); + CHECK_SIZE(rowadr, nr); + mju_sparse2dense(res_.data(), mat_.data(), nr, nc, rownnz_.data(), rowadr_.data(), colind_.data()); +} + +int mju_cholFactor_wrapper(const val& mat, mjtNum mindiag) +{ + UNPACK_VALUE(mjtNum, mat); + CHECK_PERFECT_SQUARE(mat); + return mju_cholFactor(mat_.data(), mat_sqrt, mindiag); +} + +void mju_cholSolve_wrapper(const val& res, const NumberArray& mat, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_PERFECT_SQUARE(mat); + CHECK_SIZE(res, mat_sqrt); + CHECK_SIZE(vec, mat_sqrt); + mju_cholSolve(res_.data(), mat_.data(), vec_.data(), mat_sqrt); +} + +int mju_cholUpdate_wrapper(const val& mat, const val& x, int flg_plus) +{ + UNPACK_VALUE(mjtNum, mat); + UNPACK_VALUE(mjtNum, x); + CHECK_PERFECT_SQUARE(mat); + CHECK_SIZE(x, mat_sqrt); + return mju_cholUpdate(mat_.data(), x_.data(), mat_sqrt, flg_plus); +} + +mjtNum mju_cholFactorBand_wrapper(const val& mat, int ntotal, int nband, int ndense, mjtNum diagadd, mjtNum diagmul) +{ + UNPACK_VALUE(mjtNum, mat); + CHECK_SIZE(mat, (ntotal - ndense) * nband + ndense * ntotal); + return mju_cholFactorBand(mat_.data(), ntotal, nband, ndense, diagadd, diagmul); +} + +void mju_cholSolveBand_wrapper(const val& res, const NumberArray& mat, const NumberArray& vec, int ntotal, int nband, int ndense) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(mat, (ntotal - ndense) * nband + ndense * ntotal); + CHECK_SIZE(res, ntotal); + CHECK_SIZE(vec, ntotal); + mju_cholSolveBand(res_.data(), mat_.data(), vec_.data(), ntotal, nband, ndense); +} + +void mju_band2Dense_wrapper(const val& res, const NumberArray& mat, int ntotal, int nband, int ndense, mjtByte flg_sym) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + CHECK_SIZE(mat, (ntotal - ndense) * nband + ndense * ntotal); + CHECK_SIZE(res, ntotal * ntotal); + mju_band2Dense(res_.data(), mat_.data(), ntotal, nband, ndense, flg_sym); +} + +void mju_dense2Band_wrapper(const val& res, const NumberArray& mat, int ntotal, int nband, int ndense) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + CHECK_SIZE(mat, ntotal * ntotal); + CHECK_SIZE(res, (ntotal - ndense) * nband + ndense * ntotal); + mju_dense2Band(res_.data(), mat_.data(), ntotal, nband, ndense); +} + +void mju_bandMulMatVec_wrapper(const val& res, const NumberArray& mat, const NumberArray& vec, int ntotal, int nband, int ndense, int nvec, mjtByte flg_sym) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(mat, (ntotal - ndense) * nband + ndense * ntotal); + CHECK_SIZE(res, ntotal * nvec); + CHECK_SIZE(vec, ntotal * nvec); + mju_bandMulMatVec(res_.data(), mat_.data(), vec_.data(), ntotal, nband, ndense, nvec, flg_sym); +} + +int mju_boxQP_wrapper(const val& res, const val& R, const val& index, const NumberArray& H, const NumberArray& g, const NumberArray& lower, const NumberArray& upper) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_VALUE(mjtNum, R); + UNPACK_NULLABLE_VALUE(int, index); + UNPACK_ARRAY(mjtNum, H); + UNPACK_ARRAY(mjtNum, g); + UNPACK_NULLABLE_ARRAY(mjtNum, lower); + UNPACK_NULLABLE_ARRAY(mjtNum, upper); + CHECK_SIZES(lower, res); + CHECK_SIZES(upper, res); + CHECK_SIZES(index, res); + CHECK_SIZE(R, res_.size() * (res_.size() + 7)) + CHECK_PERFECT_SQUARE(H); + CHECK_SIZES(g, res); + return mju_boxQP(res_.data(), R_.data(), index_.data(), H_.data(), g_.data(), res_.size(), lower_.data(), upper_.data()); +} + +void mju_encodePyramid_wrapper(const val& pyramid, const NumberArray& force, const NumberArray& mu) +{ + UNPACK_VALUE(mjtNum, pyramid); + UNPACK_ARRAY(mjtNum, force); + UNPACK_ARRAY(mjtNum, mu); + CHECK_SIZE(pyramid, 2 * mu_.size()); + CHECK_SIZE(force, mu_.size() + 1); + mju_encodePyramid(pyramid_.data(), force_.data(), mu_.data(), mu_.size()); +} + +void mju_decodePyramid_wrapper(const val& force, const NumberArray& pyramid, const NumberArray& mu) +{ + UNPACK_VALUE(mjtNum, force); + UNPACK_ARRAY(mjtNum, pyramid); + UNPACK_ARRAY(mjtNum, mu); + CHECK_SIZE(pyramid, 2 * mu_.size()); + CHECK_SIZE(force, mu_.size() + 1); + mju_decodePyramid(force_.data(), pyramid_.data(), mu_.data(), mu_.size()); +} + +int mju_isZero_wrapper(const val& vec) +{ + UNPACK_VALUE(mjtNum, vec); + return mju_isZero(vec_.data(), vec_.size()); +} + +void mju_f2n_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(float, vec); + CHECK_SIZES(res, vec); + mju_f2n(res_.data(), vec_.data(), res_.size()); +} + +void mju_n2f_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(float, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_n2f(res_.data(), vec_.data(), res_.size()); +} + +void mju_d2n_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(double, vec); + CHECK_SIZES(res, vec); + mju_d2n(res_.data(), vec_.data(), res_.size()); +} + +void mju_n2d_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(double, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_n2d(res_.data(), vec_.data(), res_.size()); +} + +void mju_insertionSort_wrapper(const val& list) +{ + UNPACK_VALUE(mjtNum, list); + mju_insertionSort(list_.data(), list_.size()); +} + +void mju_insertionSortInt_wrapper(const val& list) +{ + UNPACK_VALUE(int, list); + mju_insertionSortInt(list_.data(), list_.size()); +} + +void mjd_transitionFD_wrapper(const MjModel& m, MjData& d, mjtNum eps, mjtByte flg_centered, const val& A, const val& B, const val& C, const val& D) +{ + UNPACK_NULLABLE_VALUE(mjtNum, A); + UNPACK_NULLABLE_VALUE(mjtNum, B); + UNPACK_NULLABLE_VALUE(mjtNum, C); + UNPACK_NULLABLE_VALUE(mjtNum, D); + CHECK_SIZE(A, (2 * m.nv() + m.na()) * (2 * m.nv() + m.na())); + CHECK_SIZE(B, (2 * m.nv() + m.na()) * m.nu()); + CHECK_SIZE(C, m.nsensordata() * (2 * m.nv() + m.na())); + CHECK_SIZE(D, m.nsensordata() * m.nu()); + mjd_transitionFD(m.get(), d.get(), eps, flg_centered, A_.data(), B_.data(), C_.data(), D_.data()); +} + +void mjd_inverseFD_wrapper(const MjModel& m, MjData& d, mjtNum eps, mjtByte flg_actuation, const val& DfDq, const val& DfDv, const val& DfDa, const val& DsDq, const val& DsDv, const val& DsDa, const val& DmDq) +{ + UNPACK_NULLABLE_VALUE(mjtNum, DfDq); + UNPACK_NULLABLE_VALUE(mjtNum, DfDv); + UNPACK_NULLABLE_VALUE(mjtNum, DfDa); + UNPACK_NULLABLE_VALUE(mjtNum, DsDq); + UNPACK_NULLABLE_VALUE(mjtNum, DsDv); + UNPACK_NULLABLE_VALUE(mjtNum, DsDa); + UNPACK_NULLABLE_VALUE(mjtNum, DmDq); + CHECK_SIZE(DfDq, m.nv() * m.nv()); + CHECK_SIZE(DfDv, m.nv() * m.nv()); + CHECK_SIZE(DfDa, m.nv() * m.nv()); + CHECK_SIZE(DsDq, m.nv() * m.nsensordata()); + CHECK_SIZE(DsDv, m.nv() * m.nsensordata()); + CHECK_SIZE(DsDa, m.nv() * m.nsensordata()); + CHECK_SIZE(DmDq, m.nv() * m.nM()); + mjd_inverseFD(m.get(), d.get(), eps, flg_actuation, DfDq_.data(), DfDv_.data(), DfDa_.data(), + DsDq_.data(), DsDv_.data(), DsDa_.data(), DmDq_.data()); +} + +void mjd_subQuat_wrapper(const NumberArray& qa, const NumberArray& qb, const val& Da, const val& Db) +{ + UNPACK_ARRAY(mjtNum, qa); + UNPACK_ARRAY(mjtNum, qb); + UNPACK_NULLABLE_VALUE(mjtNum, Da); + UNPACK_NULLABLE_VALUE(mjtNum, Db); + CHECK_SIZE(qa, 4); + CHECK_SIZE(qb, 4); + CHECK_SIZE(Da, 9); + CHECK_SIZE(Db, 9); + mjd_subQuat(qa_.data(), qb_.data(), Da_.data(), Db_.data()); +} + +EMSCRIPTEN_BINDINGS(mujoco_functions) { + function("mj_resetCallbacks", &mj_resetCallbacks); + function("mj_version", &mj_version); + function("mju_bandDiag", &mju_bandDiag); + function("mju_springDamper", &mju_springDamper); + function("mju_min", &mju_min); + function("mju_max", &mju_max); + function("mju_clip", &mju_clip); + function("mju_sign", &mju_sign); + function("mju_round", &mju_round); + function("mju_isBad", &mju_isBad); + function("mju_Halton", &mju_Halton); + function("mju_sigmoid", &mju_sigmoid); + function("mj_copyBack", &mj_copyBack_wrapper); + function("mj_step", &mj_step_wrapper); + function("mj_step1", &mj_step1_wrapper); + function("mj_step2", &mj_step2_wrapper); + function("mj_forward", &mj_forward_wrapper); + function("mj_inverse", &mj_inverse_wrapper); + function("mj_forwardSkip", &mj_forwardSkip_wrapper); + function("mj_inverseSkip", &mj_inverseSkip_wrapper); + function("mj_defaultLROpt", &mj_defaultLROpt_wrapper); + function("mj_defaultSolRefImp", &mj_defaultSolRefImp_wrapper); + function("mj_defaultOption", &mj_defaultOption_wrapper); + function("mj_defaultVisual", &mj_defaultVisual_wrapper); + function("mj_sizeModel", &mj_sizeModel_wrapper); + function("mj_resetData", &mj_resetData_wrapper); + function("mj_resetDataDebug", &mj_resetDataDebug_wrapper); + function("mj_resetDataKeyframe", &mj_resetDataKeyframe_wrapper); + function("mj_setConst", &mj_setConst_wrapper); + function("mjs_activatePlugin", &mjs_activatePlugin_wrapper); + function("mjs_setDeepCopy", &mjs_setDeepCopy_wrapper); + function("mj_printFormattedModel", &mj_printFormattedModel_wrapper); + function("mj_printModel", &mj_printModel_wrapper); + function("mj_printFormattedData", &mj_printFormattedData_wrapper); + function("mj_printData", &mj_printData_wrapper); + function("mju_printMat", &mju_printMat_wrapper); + function("mj_printScene", &mj_printScene_wrapper); + function("mj_printFormattedScene", &mj_printFormattedScene_wrapper); + function("mj_fwdPosition", &mj_fwdPosition_wrapper); + function("mj_fwdVelocity", &mj_fwdVelocity_wrapper); + function("mj_fwdActuation", &mj_fwdActuation_wrapper); + function("mj_fwdAcceleration", &mj_fwdAcceleration_wrapper); + function("mj_fwdConstraint", &mj_fwdConstraint_wrapper); + function("mj_Euler", &mj_Euler_wrapper); + function("mj_RungeKutta", &mj_RungeKutta_wrapper); + function("mj_implicit", &mj_implicit_wrapper); + function("mj_invPosition", &mj_invPosition_wrapper); + function("mj_invVelocity", &mj_invVelocity_wrapper); + function("mj_invConstraint", &mj_invConstraint_wrapper); + function("mj_compareFwdInv", &mj_compareFwdInv_wrapper); + function("mj_sensorPos", &mj_sensorPos_wrapper); + function("mj_sensorVel", &mj_sensorVel_wrapper); + function("mj_sensorAcc", &mj_sensorAcc_wrapper); + function("mj_energyPos", &mj_energyPos_wrapper); + function("mj_energyVel", &mj_energyVel_wrapper); + function("mj_checkPos", &mj_checkPos_wrapper); + function("mj_checkVel", &mj_checkVel_wrapper); + function("mj_checkAcc", &mj_checkAcc_wrapper); + function("mj_kinematics", &mj_kinematics_wrapper); + function("mj_comPos", &mj_comPos_wrapper); + function("mj_camlight", &mj_camlight_wrapper); + function("mj_flex", &mj_flex_wrapper); + function("mj_tendon", &mj_tendon_wrapper); + function("mj_transmission", &mj_transmission_wrapper); + function("mj_crb", &mj_crb_wrapper); + function("mj_makeM", &mj_makeM_wrapper); + function("mj_factorM", &mj_factorM_wrapper); + function("mj_comVel", &mj_comVel_wrapper); + function("mj_passive", &mj_passive_wrapper); + function("mj_subtreeVel", &mj_subtreeVel_wrapper); + function("mj_rnePostConstraint", &mj_rnePostConstraint_wrapper); + function("mj_collision", &mj_collision_wrapper); + function("mj_makeConstraint", &mj_makeConstraint_wrapper); + function("mj_island", &mj_island_wrapper); + function("mj_projectConstraint", &mj_projectConstraint_wrapper); + function("mj_referenceConstraint", &mj_referenceConstraint_wrapper); + function("mj_stateSize", &mj_stateSize_wrapper); + function("mj_extractState", &mj_extractState_wrapper); + function("mj_setKeyframe", &mj_setKeyframe_wrapper); + function("mj_addContact", &mj_addContact_wrapper); + function("mj_isPyramidal", &mj_isPyramidal_wrapper); + function("mj_isSparse", &mj_isSparse_wrapper); + function("mj_isDual", &mj_isDual_wrapper); + function("mj_name2id", &mj_name2id_wrapper); + function("mj_id2name", &mj_id2name_wrapper); + function("mj_objectVelocity", &mj_objectVelocity_wrapper); + function("mj_objectAcceleration", &mj_objectAcceleration_wrapper); + function("mj_contactForce", &mj_contactForce_wrapper); + function("mj_local2Global", &mj_local2Global_wrapper); + function("mj_getTotalmass", &mj_getTotalmass_wrapper); + function("mj_setTotalmass", &mj_setTotalmass_wrapper); + function("mj_versionString", &mj_versionString_wrapper); + function("mj_ray", &mj_ray_wrapper); + function("mj_rayHfield", &mj_rayHfield_wrapper); + function("mj_rayMesh", &mj_rayMesh_wrapper); + function("mju_rayGeom", &mju_rayGeom_wrapper); + function("mju_rayFlex", &mju_rayFlex_wrapper); + function("mju_raySkin", &mju_raySkin_wrapper); + function("mjv_defaultCamera", &mjv_defaultCamera_wrapper); + function("mjv_defaultFreeCamera", &mjv_defaultFreeCamera_wrapper); + function("mjv_defaultPerturb", &mjv_defaultPerturb_wrapper); + function("mjv_room2model", &mjv_room2model_wrapper); + function("mjv_model2room", &mjv_model2room_wrapper); + function("mjv_cameraInModel", &mjv_cameraInModel_wrapper); + function("mjv_cameraInRoom", &mjv_cameraInRoom_wrapper); + function("mjv_frustumHeight", &mjv_frustumHeight_wrapper); + function("mjv_alignToCamera", &mjv_alignToCamera_wrapper); + function("mjv_moveCamera", &mjv_moveCamera_wrapper); + function("mjv_movePerturb", &mjv_movePerturb_wrapper); + function("mjv_moveModel", &mjv_moveModel_wrapper); + function("mjv_initPerturb", &mjv_initPerturb_wrapper); + function("mjv_applyPerturbPose", &mjv_applyPerturbPose_wrapper); + function("mjv_applyPerturbForce", &mjv_applyPerturbForce_wrapper); + function("mjv_select", &mjv_select_wrapper); + function("mjv_defaultOption", &mjv_defaultOption_wrapper); + function("mjv_defaultFigure", &mjv_defaultFigure_wrapper); + function("mjv_initGeom", &mjv_initGeom_wrapper); + function("mjv_connector", &mjv_connector_wrapper); + function("mjv_updateScene", &mjv_updateScene_wrapper); + function("mjv_addGeoms", &mjv_addGeoms_wrapper); + function("mjv_makeLights", &mjv_makeLights_wrapper); + function("mjv_updateCamera", &mjv_updateCamera_wrapper); + function("mjv_updateSkin", &mjv_updateSkin_wrapper); + function("mju_writeLog", &mju_writeLog_wrapper); + function("mjs_getError", &mjs_getError_wrapper); + function("mjs_isWarning", &mjs_isWarning_wrapper); + function("mju_zero3", &mju_zero3_wrapper); + function("mju_copy3", &mju_copy3_wrapper); + function("mju_scl3", &mju_scl3_wrapper); + function("mju_add3", &mju_add3_wrapper); + function("mju_sub3", &mju_sub3_wrapper); + function("mju_addTo3", &mju_addTo3_wrapper); + function("mju_subFrom3", &mju_subFrom3_wrapper); + function("mju_addToScl3", &mju_addToScl3_wrapper); + function("mju_addScl3", &mju_addScl3_wrapper); + function("mju_normalize3", &mju_normalize3_wrapper); + function("mju_norm3", &mju_norm3_wrapper); + function("mju_dot3", &mju_dot3_wrapper); + function("mju_dist3", &mju_dist3_wrapper); + function("mju_mulMatVec3", &mju_mulMatVec3_wrapper); + function("mju_mulMatTVec3", &mju_mulMatTVec3_wrapper); + function("mju_cross", &mju_cross_wrapper); + function("mju_zero4", &mju_zero4_wrapper); + function("mju_unit4", &mju_unit4_wrapper); + function("mju_copy4", &mju_copy4_wrapper); + function("mju_normalize4", &mju_normalize4_wrapper); + function("mju_transformSpatial", &mju_transformSpatial_wrapper); + function("mju_rotVecQuat", &mju_rotVecQuat_wrapper); + function("mju_negQuat", &mju_negQuat_wrapper); + function("mju_mulQuat", &mju_mulQuat_wrapper); + function("mju_mulQuatAxis", &mju_mulQuatAxis_wrapper); + function("mju_axisAngle2Quat", &mju_axisAngle2Quat_wrapper); + function("mju_quat2Vel", &mju_quat2Vel_wrapper); + function("mju_subQuat", &mju_subQuat_wrapper); + function("mju_quat2Mat", &mju_quat2Mat_wrapper); + function("mju_mat2Quat", &mju_mat2Quat_wrapper); + function("mju_derivQuat", &mju_derivQuat_wrapper); + function("mju_quatIntegrate", &mju_quatIntegrate_wrapper); + function("mju_quatZ2Vec", &mju_quatZ2Vec_wrapper); + function("mju_mat2Rot", &mju_mat2Rot_wrapper); + function("mju_euler2Quat", &mju_euler2Quat_wrapper); + function("mju_mulPose", &mju_mulPose_wrapper); + function("mju_negPose", &mju_negPose_wrapper); + function("mju_trnVecPose", &mju_trnVecPose_wrapper); + function("mju_eig3", &mju_eig3_wrapper); + function("mju_muscleGain", &mju_muscleGain_wrapper); + function("mju_muscleBias", &mju_muscleBias_wrapper); + function("mju_muscleDynamics", &mju_muscleDynamics_wrapper); + function("mju_type2Str", &mju_type2Str_wrapper); + function("mju_str2Type", &mju_str2Type_wrapper); + function("mju_writeNumBytes", &mju_writeNumBytes_wrapper); + function("mju_warningText", &mju_warningText_wrapper); + function("mju_standardNormal", &mju_standardNormal_wrapper); + function("mjd_quatIntegrate", &mjd_quatIntegrate_wrapper); + function("mjs_attach", &mjs_attach_wrapper); + function("mjs_addBody", &mjs_addBody_wrapper); + function("mjs_addSite", &mjs_addSite_wrapper); + function("mjs_addJoint", &mjs_addJoint_wrapper); + function("mjs_addFreeJoint", &mjs_addFreeJoint_wrapper); + function("mjs_addGeom", &mjs_addGeom_wrapper); + function("mjs_addCamera", &mjs_addCamera_wrapper); + function("mjs_addLight", &mjs_addLight_wrapper); + function("mjs_addFrame", &mjs_addFrame_wrapper); + function("mjs_delete", &mjs_delete_wrapper); + function("mjs_addActuator", &mjs_addActuator_wrapper); + function("mjs_addSensor", &mjs_addSensor_wrapper); + function("mjs_addFlex", &mjs_addFlex_wrapper); + function("mjs_addPair", &mjs_addPair_wrapper); + function("mjs_addExclude", &mjs_addExclude_wrapper); + function("mjs_addEquality", &mjs_addEquality_wrapper); + function("mjs_addTendon", &mjs_addTendon_wrapper); + function("mjs_wrapSite", &mjs_wrapSite_wrapper); + function("mjs_wrapGeom", &mjs_wrapGeom_wrapper); + function("mjs_wrapJoint", &mjs_wrapJoint_wrapper); + function("mjs_wrapPulley", &mjs_wrapPulley_wrapper); + function("mjs_addNumeric", &mjs_addNumeric_wrapper); + function("mjs_addText", &mjs_addText_wrapper); + function("mjs_addTuple", &mjs_addTuple_wrapper); + function("mjs_addKey", &mjs_addKey_wrapper); + function("mjs_addPlugin", &mjs_addPlugin_wrapper); + function("mjs_addDefault", &mjs_addDefault_wrapper); + function("mjs_setToMotor", &mjs_setToMotor_wrapper); + function("mjs_setToPosition", &mjs_setToPosition_wrapper); + function("mjs_setToIntVelocity", &mjs_setToIntVelocity_wrapper); + function("mjs_setToVelocity", &mjs_setToVelocity_wrapper); + function("mjs_setToDamper", &mjs_setToDamper_wrapper); + function("mjs_setToCylinder", &mjs_setToCylinder_wrapper); + function("mjs_setToMuscle", &mjs_setToMuscle_wrapper); + function("mjs_setToAdhesion", &mjs_setToAdhesion_wrapper); + function("mjs_addMesh", &mjs_addMesh_wrapper); + function("mjs_addHField", &mjs_addHField_wrapper); + function("mjs_addSkin", &mjs_addSkin_wrapper); + function("mjs_addTexture", &mjs_addTexture_wrapper); + function("mjs_addMaterial", &mjs_addMaterial_wrapper); + function("mjs_makeMesh", &mjs_makeMesh_wrapper); + function("mjs_getSpec", &mjs_getSpec_wrapper); + function("mjs_findSpec", &mjs_findSpec_wrapper); + function("mjs_findBody", &mjs_findBody_wrapper); + function("mjs_findElement", &mjs_findElement_wrapper); + function("mjs_findChild", &mjs_findChild_wrapper); + function("mjs_getParent", &mjs_getParent_wrapper); + function("mjs_getFrame", &mjs_getFrame_wrapper); + function("mjs_findFrame", &mjs_findFrame_wrapper); + function("mjs_getDefault", &mjs_getDefault_wrapper); + function("mjs_findDefault", &mjs_findDefault_wrapper); + function("mjs_getSpecDefault", &mjs_getSpecDefault_wrapper); + function("mjs_getId", &mjs_getId_wrapper); + function("mjs_firstChild", &mjs_firstChild_wrapper); + function("mjs_nextChild", &mjs_nextChild_wrapper); + function("mjs_firstElement", &mjs_firstElement_wrapper); + function("mjs_nextElement", &mjs_nextElement_wrapper); + function("mjs_getWrapTarget", &mjs_getWrapTarget_wrapper); + function("mjs_getWrapSideSite", &mjs_getWrapSideSite_wrapper); + function("mjs_getWrapDivisor", &mjs_getWrapDivisor_wrapper); + function("mjs_getWrapCoef", &mjs_getWrapCoef_wrapper); + function("mjs_setName", &mjs_setName_wrapper); + function("mjs_getName", &mjs_getName_wrapper); + function("mjs_getWrapNum", &mjs_getWrapNum_wrapper); + function("mjs_getWrap", &mjs_getWrap_wrapper); + function("mjs_setDefault", &mjs_setDefault_wrapper); + function("mjs_setFrame", &mjs_setFrame_wrapper); + function("mjs_resolveOrientation", &mjs_resolveOrientation_wrapper); + function("mjs_deleteUserValue", &mjs_deleteUserValue_wrapper); + function("mjs_sensorDim", &mjs_sensorDim_wrapper); + function("mjs_defaultSpec", &mjs_defaultSpec_wrapper); + function("mjs_defaultOrientation", &mjs_defaultOrientation_wrapper); + function("mjs_defaultBody", &mjs_defaultBody_wrapper); + function("mjs_defaultFrame", &mjs_defaultFrame_wrapper); + function("mjs_defaultJoint", &mjs_defaultJoint_wrapper); + function("mjs_defaultGeom", &mjs_defaultGeom_wrapper); + function("mjs_defaultSite", &mjs_defaultSite_wrapper); + function("mjs_defaultCamera", &mjs_defaultCamera_wrapper); + function("mjs_defaultLight", &mjs_defaultLight_wrapper); + function("mjs_defaultFlex", &mjs_defaultFlex_wrapper); + function("mjs_defaultMesh", &mjs_defaultMesh_wrapper); + function("mjs_defaultHField", &mjs_defaultHField_wrapper); + function("mjs_defaultSkin", &mjs_defaultSkin_wrapper); + function("mjs_defaultTexture", &mjs_defaultTexture_wrapper); + function("mjs_defaultMaterial", &mjs_defaultMaterial_wrapper); + function("mjs_defaultPair", &mjs_defaultPair_wrapper); + function("mjs_defaultEquality", &mjs_defaultEquality_wrapper); + function("mjs_defaultTendon", &mjs_defaultTendon_wrapper); + function("mjs_defaultActuator", &mjs_defaultActuator_wrapper); + function("mjs_defaultSensor", &mjs_defaultSensor_wrapper); + function("mjs_defaultNumeric", &mjs_defaultNumeric_wrapper); + function("mjs_defaultText", &mjs_defaultText_wrapper); + function("mjs_defaultTuple", &mjs_defaultTuple_wrapper); + function("mjs_defaultKey", &mjs_defaultKey_wrapper); + function("mjs_defaultPlugin", &mjs_defaultPlugin_wrapper); + function("mjs_asBody", &mjs_asBody_wrapper); + function("mjs_asGeom", &mjs_asGeom_wrapper); + function("mjs_asJoint", &mjs_asJoint_wrapper); + function("mjs_asSite", &mjs_asSite_wrapper); + function("mjs_asCamera", &mjs_asCamera_wrapper); + function("mjs_asLight", &mjs_asLight_wrapper); + function("mjs_asFrame", &mjs_asFrame_wrapper); + function("mjs_asActuator", &mjs_asActuator_wrapper); + function("mjs_asSensor", &mjs_asSensor_wrapper); + function("mjs_asFlex", &mjs_asFlex_wrapper); + function("mjs_asPair", &mjs_asPair_wrapper); + function("mjs_asEquality", &mjs_asEquality_wrapper); + function("mjs_asExclude", &mjs_asExclude_wrapper); + function("mjs_asTendon", &mjs_asTendon_wrapper); + function("mjs_asNumeric", &mjs_asNumeric_wrapper); + function("mjs_asText", &mjs_asText_wrapper); + function("mjs_asTuple", &mjs_asTuple_wrapper); + function("mjs_asKey", &mjs_asKey_wrapper); + function("mjs_asMesh", &mjs_asMesh_wrapper); + function("mjs_asHField", &mjs_asHField_wrapper); + function("mjs_asSkin", &mjs_asSkin_wrapper); + function("mjs_asTexture", &mjs_asTexture_wrapper); + function("mjs_asMaterial", &mjs_asMaterial_wrapper); + function("mjs_asPlugin", &mjs_asPlugin_wrapper); + function("error", &error_wrapper); + function("mju_printMatSparse", &mju_printMatSparse_wrapper); + function("mj_solveM", &mj_solveM_wrapper); + function("mj_solveM2", &mj_solveM2_wrapper); + function("mj_rne", &mj_rne_wrapper); + function("mj_saveLastXML", &mj_saveLastXML_wrapper); + function("mj_setLengthRange", &mj_setLengthRange_wrapper); + function("mj_constraintUpdate", &mj_constraintUpdate_wrapper); + function("mj_getState", &mj_getState_wrapper); + function("mj_setState", &mj_setState_wrapper); + function("mj_mulJacVec", &mj_mulJacVec_wrapper); + function("mj_mulJacTVec", &mj_mulJacTVec_wrapper); + function("mj_jac", &mj_jac_wrapper); + function("mj_jacBody", &mj_jacBody_wrapper); + function("mj_jacBodyCom", &mj_jacBodyCom_wrapper); + function("mj_jacSubtreeCom", &mj_jacSubtreeCom_wrapper); + function("mj_jacGeom", &mj_jacGeom_wrapper); + function("mj_jacSite", &mj_jacSite_wrapper); + function("mj_jacPointAxis", &mj_jacPointAxis_wrapper); + function("mj_jacDot", &mj_jacDot_wrapper); + function("mj_angmomMat", &mj_angmomMat_wrapper); + function("mj_fullM", &mj_fullM_wrapper); + function("mj_mulM", &mj_mulM_wrapper); + function("mj_mulM2", &mj_mulM2_wrapper); + function("mj_addM", &mj_addM_wrapper); + function("mj_applyFT", &mj_applyFT_wrapper); + function("mj_geomDistance", &mj_geomDistance_wrapper); + function("mj_differentiatePos", &mj_differentiatePos_wrapper); + function("mj_integratePos", &mj_integratePos_wrapper); + function("mj_normalizeQuat", &mj_normalizeQuat_wrapper); + function("mj_multiRay", &mj_multiRay_wrapper); + function("mju_zero", &mju_zero_wrapper); + function("mju_fill", &mju_fill_wrapper); + function("mju_copy", &mju_copy_wrapper); + function("mju_sum", &mju_sum_wrapper); + function("mju_L1", &mju_L1_wrapper); + function("mju_scl", &mju_scl_wrapper); + function("mju_add", &mju_add_wrapper); + function("mju_sub", &mju_sub_wrapper); + function("mju_addTo", &mju_addTo_wrapper); + function("mju_subFrom", &mju_subFrom_wrapper); + function("mju_addToScl", &mju_addToScl_wrapper); + function("mju_addScl", &mju_addScl_wrapper); + function("mju_normalize", &mju_normalize_wrapper); + function("mju_norm", &mju_norm_wrapper); + function("mju_dot", &mju_dot_wrapper); + function("mju_mulMatVec", &mju_mulMatVec_wrapper); + function("mju_mulMatTVec", &mju_mulMatTVec_wrapper); + function("mju_mulVecMatVec", &mju_mulVecMatVec_wrapper); + function("mju_transpose", &mju_transpose_wrapper); + function("mju_symmetrize", &mju_symmetrize_wrapper); + function("mju_eye", &mju_eye_wrapper); + function("mju_mulMatMat", &mju_mulMatMat_wrapper); + function("mju_mulMatMatT", &mju_mulMatMatT_wrapper); + function("mju_mulMatTMat", &mju_mulMatTMat_wrapper); + function("mju_sqrMatTD", &mju_sqrMatTD_wrapper); + function("mju_dense2sparse", &mju_dense2sparse_wrapper); + function("mju_sparse2dense", &mju_sparse2dense_wrapper); + function("mju_cholFactor", &mju_cholFactor_wrapper); + function("mju_cholSolve", &mju_cholSolve_wrapper); + function("mju_cholUpdate", &mju_cholUpdate_wrapper); + function("mju_cholFactorBand", &mju_cholFactorBand_wrapper); + function("mju_cholSolveBand", &mju_cholSolveBand_wrapper); + function("mju_band2Dense", &mju_band2Dense_wrapper); + function("mju_dense2Band", &mju_dense2Band_wrapper); + function("mju_bandMulMatVec", &mju_bandMulMatVec_wrapper); + function("mju_boxQP", &mju_boxQP_wrapper); + function("mju_encodePyramid", &mju_encodePyramid_wrapper); + function("mju_decodePyramid", &mju_decodePyramid_wrapper); + function("mju_isZero", &mju_isZero_wrapper); + function("mju_f2n", &mju_f2n_wrapper); + function("mju_n2f", &mju_n2f_wrapper); + function("mju_d2n", &mju_d2n_wrapper); + function("mju_n2d", &mju_n2d_wrapper); + function("mju_insertionSort", &mju_insertionSort_wrapper); + function("mju_insertionSortInt", &mju_insertionSortInt_wrapper); + function("mjd_transitionFD", &mjd_transitionFD_wrapper); + function("mjd_inverseFD", &mjd_inverseFD_wrapper); + function("mjd_subQuat", &mjd_subQuat_wrapper); + class_>("FloatBuffer") + .constructor() + .class_function("FromArray", &WasmBuffer::FromArray) + .function("GetPointer", &WasmBuffer::GetPointer) + .function("GetElementCount", &WasmBuffer::GetElementCount) + .function("GetView", &WasmBuffer::GetView); + class_>("DoubleBuffer") + .constructor() + .class_function("FromArray", &WasmBuffer::FromArray) + .function("GetPointer", &WasmBuffer::GetPointer) + .function("GetElementCount", &WasmBuffer::GetElementCount) + .function("GetView", &WasmBuffer::GetView); + class_>("IntBuffer") + .constructor() + .class_function("FromArray", &WasmBuffer::FromArray) + .function("GetPointer", &WasmBuffer::GetPointer) + .function("GetElementCount", &WasmBuffer::GetElementCount) + .function("GetView", &WasmBuffer::GetView); + register_vector("mjStringVec"); + register_vector("mjIntVec"); + register_vector("mjIntVecVec"); + register_vector("mjFloatVec"); + register_vector("mjFloatVecVec"); + register_vector("mjDoubleVec"); + // register_type gives better type information (val is mapped to any by default) + register_type("number[]"); + register_type("string"); + register_vector("mjByteVec"); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); +} + +} // namespace mujoco::wasm +// NOLINTEND(whitespace/semicolon) +// NOLINTEND(whitespace/line_length) diff --git a/wasm/codegen/generated/bindings.h b/wasm/codegen/generated/bindings.h new file mode 100644 index 00000000..901a780a --- /dev/null +++ b/wasm/codegen/generated/bindings.h @@ -0,0 +1,6712 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// NOLINTBEGIN(whitespace/line_length) +#ifndef MUJOCO_WASM_CODEGEN_GENERATED_BINDINGS_H_ +#define MUJOCO_WASM_CODEGEN_GENERATED_BINDINGS_H_ +#include +#include + +#include +#include +#include + +#include + +namespace mujoco::wasm { + +// Create the types for anonymous structs +using mjVisualGlobal = decltype(::mjVisual::global); +using mjVisualQuality = decltype(::mjVisual::quality); +using mjVisualHeadlight = decltype(::mjVisual::headlight); +using mjVisualMap = decltype(::mjVisual::map); +using mjVisualScale = decltype(::mjVisual::scale); +using mjVisualRgba = decltype(::mjVisual::rgba); + +struct MjContact { + MjContact(); + MjContact(const MjContact &); + MjContact &operator=(const MjContact &); + explicit MjContact(mjContact *ptr); + ~MjContact(); + std::unique_ptr copy(); + mjtNum dist() const { + return ptr_->dist; + } + void set_dist(mjtNum value) { + ptr_->dist = value; + } + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val frame() const { + return emscripten::val(emscripten::typed_memory_view(9, ptr_->frame)); + } + mjtNum includemargin() const { + return ptr_->includemargin; + } + void set_includemargin(mjtNum value) { + ptr_->includemargin = value; + } + emscripten::val friction() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->friction)); + } + emscripten::val solref() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solref)); + } + emscripten::val solreffriction() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solreffriction)); + } + emscripten::val solimp() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->solimp)); + } + mjtNum mu() const { + return ptr_->mu; + } + void set_mu(mjtNum value) { + ptr_->mu = value; + } + emscripten::val H() const { + return emscripten::val(emscripten::typed_memory_view(36, ptr_->H)); + } + int dim() const { + return ptr_->dim; + } + void set_dim(int value) { + ptr_->dim = value; + } + int geom1() const { + return ptr_->geom1; + } + void set_geom1(int value) { + ptr_->geom1 = value; + } + int geom2() const { + return ptr_->geom2; + } + void set_geom2(int value) { + ptr_->geom2 = value; + } + emscripten::val geom() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->geom)); + } + emscripten::val flex() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->flex)); + } + emscripten::val elem() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->elem)); + } + emscripten::val vert() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->vert)); + } + int exclude() const { + return ptr_->exclude; + } + void set_exclude(int value) { + ptr_->exclude = value; + } + int efc_address() const { + return ptr_->efc_address; + } + void set_efc_address(int value) { + ptr_->efc_address = value; + } + mjContact* get() const { return ptr_; } + void set(mjContact* ptr) { ptr_ = ptr; } + + private: + mjContact* ptr_; + bool owned_ = false; +}; + +struct MjLROpt { + MjLROpt(); + MjLROpt(const MjLROpt &); + MjLROpt &operator=(const MjLROpt &); + explicit MjLROpt(mjLROpt *ptr); + ~MjLROpt(); + std::unique_ptr copy(); + int mode() const { + return ptr_->mode; + } + void set_mode(int value) { + ptr_->mode = value; + } + int useexisting() const { + return ptr_->useexisting; + } + void set_useexisting(int value) { + ptr_->useexisting = value; + } + int uselimit() const { + return ptr_->uselimit; + } + void set_uselimit(int value) { + ptr_->uselimit = value; + } + mjtNum accel() const { + return ptr_->accel; + } + void set_accel(mjtNum value) { + ptr_->accel = value; + } + mjtNum maxforce() const { + return ptr_->maxforce; + } + void set_maxforce(mjtNum value) { + ptr_->maxforce = value; + } + mjtNum timeconst() const { + return ptr_->timeconst; + } + void set_timeconst(mjtNum value) { + ptr_->timeconst = value; + } + mjtNum timestep() const { + return ptr_->timestep; + } + void set_timestep(mjtNum value) { + ptr_->timestep = value; + } + mjtNum inttotal() const { + return ptr_->inttotal; + } + void set_inttotal(mjtNum value) { + ptr_->inttotal = value; + } + mjtNum interval() const { + return ptr_->interval; + } + void set_interval(mjtNum value) { + ptr_->interval = value; + } + mjtNum tolrange() const { + return ptr_->tolrange; + } + void set_tolrange(mjtNum value) { + ptr_->tolrange = value; + } + mjLROpt* get() const { return ptr_; } + void set(mjLROpt* ptr) { ptr_ = ptr; } + + private: + mjLROpt* ptr_; + bool owned_ = false; +}; + +struct MjOption { + MjOption(); + MjOption(const MjOption &); + MjOption &operator=(const MjOption &); + explicit MjOption(mjOption *ptr); + ~MjOption(); + std::unique_ptr copy(); + mjtNum timestep() const { + return ptr_->timestep; + } + void set_timestep(mjtNum value) { + ptr_->timestep = value; + } + mjtNum impratio() const { + return ptr_->impratio; + } + void set_impratio(mjtNum value) { + ptr_->impratio = value; + } + mjtNum tolerance() const { + return ptr_->tolerance; + } + void set_tolerance(mjtNum value) { + ptr_->tolerance = value; + } + mjtNum ls_tolerance() const { + return ptr_->ls_tolerance; + } + void set_ls_tolerance(mjtNum value) { + ptr_->ls_tolerance = value; + } + mjtNum noslip_tolerance() const { + return ptr_->noslip_tolerance; + } + void set_noslip_tolerance(mjtNum value) { + ptr_->noslip_tolerance = value; + } + mjtNum ccd_tolerance() const { + return ptr_->ccd_tolerance; + } + void set_ccd_tolerance(mjtNum value) { + ptr_->ccd_tolerance = value; + } + emscripten::val gravity() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->gravity)); + } + emscripten::val wind() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->wind)); + } + emscripten::val magnetic() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->magnetic)); + } + mjtNum density() const { + return ptr_->density; + } + void set_density(mjtNum value) { + ptr_->density = value; + } + mjtNum viscosity() const { + return ptr_->viscosity; + } + void set_viscosity(mjtNum value) { + ptr_->viscosity = value; + } + mjtNum o_margin() const { + return ptr_->o_margin; + } + void set_o_margin(mjtNum value) { + ptr_->o_margin = value; + } + emscripten::val o_solref() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->o_solref)); + } + emscripten::val o_solimp() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->o_solimp)); + } + emscripten::val o_friction() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->o_friction)); + } + int integrator() const { + return ptr_->integrator; + } + void set_integrator(int value) { + ptr_->integrator = value; + } + int cone() const { + return ptr_->cone; + } + void set_cone(int value) { + ptr_->cone = value; + } + int jacobian() const { + return ptr_->jacobian; + } + void set_jacobian(int value) { + ptr_->jacobian = value; + } + int solver() const { + return ptr_->solver; + } + void set_solver(int value) { + ptr_->solver = value; + } + int iterations() const { + return ptr_->iterations; + } + void set_iterations(int value) { + ptr_->iterations = value; + } + int ls_iterations() const { + return ptr_->ls_iterations; + } + void set_ls_iterations(int value) { + ptr_->ls_iterations = value; + } + int noslip_iterations() const { + return ptr_->noslip_iterations; + } + void set_noslip_iterations(int value) { + ptr_->noslip_iterations = value; + } + int ccd_iterations() const { + return ptr_->ccd_iterations; + } + void set_ccd_iterations(int value) { + ptr_->ccd_iterations = value; + } + int disableflags() const { + return ptr_->disableflags; + } + void set_disableflags(int value) { + ptr_->disableflags = value; + } + int enableflags() const { + return ptr_->enableflags; + } + void set_enableflags(int value) { + ptr_->enableflags = value; + } + int disableactuator() const { + return ptr_->disableactuator; + } + void set_disableactuator(int value) { + ptr_->disableactuator = value; + } + int sdf_initpoints() const { + return ptr_->sdf_initpoints; + } + void set_sdf_initpoints(int value) { + ptr_->sdf_initpoints = value; + } + int sdf_iterations() const { + return ptr_->sdf_iterations; + } + void set_sdf_iterations(int value) { + ptr_->sdf_iterations = value; + } + mjOption* get() const { return ptr_; } + void set(mjOption* ptr) { ptr_ = ptr; } + + private: + mjOption* ptr_; + bool owned_ = false; +}; + +struct MjSolverStat { + MjSolverStat(); + MjSolverStat(const MjSolverStat &); + MjSolverStat &operator=(const MjSolverStat &); + explicit MjSolverStat(mjSolverStat *ptr); + ~MjSolverStat(); + std::unique_ptr copy(); + mjtNum improvement() const { + return ptr_->improvement; + } + void set_improvement(mjtNum value) { + ptr_->improvement = value; + } + mjtNum gradient() const { + return ptr_->gradient; + } + void set_gradient(mjtNum value) { + ptr_->gradient = value; + } + mjtNum lineslope() const { + return ptr_->lineslope; + } + void set_lineslope(mjtNum value) { + ptr_->lineslope = value; + } + int nactive() const { + return ptr_->nactive; + } + void set_nactive(int value) { + ptr_->nactive = value; + } + int nchange() const { + return ptr_->nchange; + } + void set_nchange(int value) { + ptr_->nchange = value; + } + int neval() const { + return ptr_->neval; + } + void set_neval(int value) { + ptr_->neval = value; + } + int nupdate() const { + return ptr_->nupdate; + } + void set_nupdate(int value) { + ptr_->nupdate = value; + } + mjSolverStat* get() const { return ptr_; } + void set(mjSolverStat* ptr) { ptr_ = ptr; } + + private: + mjSolverStat* ptr_; + bool owned_ = false; +}; + +struct MjStatistic { + MjStatistic(); + MjStatistic(const MjStatistic &); + MjStatistic &operator=(const MjStatistic &); + explicit MjStatistic(mjStatistic *ptr); + ~MjStatistic(); + std::unique_ptr copy(); + mjtNum meaninertia() const { + return ptr_->meaninertia; + } + void set_meaninertia(mjtNum value) { + ptr_->meaninertia = value; + } + mjtNum meanmass() const { + return ptr_->meanmass; + } + void set_meanmass(mjtNum value) { + ptr_->meanmass = value; + } + mjtNum meansize() const { + return ptr_->meansize; + } + void set_meansize(mjtNum value) { + ptr_->meansize = value; + } + mjtNum extent() const { + return ptr_->extent; + } + void set_extent(mjtNum value) { + ptr_->extent = value; + } + emscripten::val center() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->center)); + } + mjStatistic* get() const { return ptr_; } + void set(mjStatistic* ptr) { ptr_ = ptr; } + + private: + mjStatistic* ptr_; + bool owned_ = false; +}; + +struct MjTimerStat { + MjTimerStat(); + MjTimerStat(const MjTimerStat &); + MjTimerStat &operator=(const MjTimerStat &); + explicit MjTimerStat(mjTimerStat *ptr); + ~MjTimerStat(); + std::unique_ptr copy(); + mjtNum duration() const { + return ptr_->duration; + } + void set_duration(mjtNum value) { + ptr_->duration = value; + } + int number() const { + return ptr_->number; + } + void set_number(int value) { + ptr_->number = value; + } + mjTimerStat* get() const { return ptr_; } + void set(mjTimerStat* ptr) { ptr_ = ptr; } + + private: + mjTimerStat* ptr_; + bool owned_ = false; +}; + +struct MjVFS { + MjVFS(); + MjVFS(const MjVFS &); + MjVFS &operator=(const MjVFS &); + explicit MjVFS(mjVFS *ptr); + ~MjVFS(); + // TODO: Define primitive pointer field with complex extents manually for impl_ + mjVFS* get() const { return ptr_; } + void set(mjVFS* ptr) { ptr_ = ptr; } + + private: + mjVFS* ptr_; + bool owned_ = false; +}; + +struct MjWarningStat { + MjWarningStat(); + MjWarningStat(const MjWarningStat &); + MjWarningStat &operator=(const MjWarningStat &); + explicit MjWarningStat(mjWarningStat *ptr); + ~MjWarningStat(); + std::unique_ptr copy(); + int lastinfo() const { + return ptr_->lastinfo; + } + void set_lastinfo(int value) { + ptr_->lastinfo = value; + } + int number() const { + return ptr_->number; + } + void set_number(int value) { + ptr_->number = value; + } + mjWarningStat* get() const { return ptr_; } + void set(mjWarningStat* ptr) { ptr_ = ptr; } + + private: + mjWarningStat* ptr_; + bool owned_ = false; +}; + +struct MjsElement { + explicit MjsElement(mjsElement *ptr); + ~MjsElement(); + std::unique_ptr copy(); + mjtObj elemtype() const { + return ptr_->elemtype; + } + void set_elemtype(mjtObj value) { + ptr_->elemtype = value; + } + uint64_t signature() const { + return ptr_->signature; + } + void set_signature(uint64_t value) { + ptr_->signature = value; + } + mjsElement* get() const { return ptr_; } + void set(mjsElement* ptr) { ptr_ = ptr; } + + private: + mjsElement* ptr_; + bool owned_ = false; +}; + +struct MjsOrientation { + explicit MjsOrientation(mjsOrientation *ptr); + ~MjsOrientation(); + std::unique_ptr copy(); + mjtOrientation type() const { + return ptr_->type; + } + void set_type(mjtOrientation value) { + ptr_->type = value; + } + emscripten::val axisangle() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->axisangle)); + } + emscripten::val xyaxes() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->xyaxes)); + } + emscripten::val zaxis() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->zaxis)); + } + emscripten::val euler() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->euler)); + } + mjsOrientation* get() const { return ptr_; } + void set(mjsOrientation* ptr) { ptr_ = ptr; } + + private: + mjsOrientation* ptr_; + bool owned_ = false; +}; + +struct MjvCamera { + MjvCamera(); + MjvCamera(const MjvCamera &); + MjvCamera &operator=(const MjvCamera &); + explicit MjvCamera(mjvCamera *ptr); + ~MjvCamera(); + std::unique_ptr copy(); + int type() const { + return ptr_->type; + } + void set_type(int value) { + ptr_->type = value; + } + int fixedcamid() const { + return ptr_->fixedcamid; + } + void set_fixedcamid(int value) { + ptr_->fixedcamid = value; + } + int trackbodyid() const { + return ptr_->trackbodyid; + } + void set_trackbodyid(int value) { + ptr_->trackbodyid = value; + } + emscripten::val lookat() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->lookat)); + } + mjtNum distance() const { + return ptr_->distance; + } + void set_distance(mjtNum value) { + ptr_->distance = value; + } + mjtNum azimuth() const { + return ptr_->azimuth; + } + void set_azimuth(mjtNum value) { + ptr_->azimuth = value; + } + mjtNum elevation() const { + return ptr_->elevation; + } + void set_elevation(mjtNum value) { + ptr_->elevation = value; + } + int orthographic() const { + return ptr_->orthographic; + } + void set_orthographic(int value) { + ptr_->orthographic = value; + } + mjvCamera* get() const { return ptr_; } + void set(mjvCamera* ptr) { ptr_ = ptr; } + + private: + mjvCamera* ptr_; + bool owned_ = false; +}; + +struct MjvFigure { + MjvFigure(); + MjvFigure(const MjvFigure &); + MjvFigure &operator=(const MjvFigure &); + explicit MjvFigure(mjvFigure *ptr); + ~MjvFigure(); + std::unique_ptr copy(); + int flg_legend() const { + return ptr_->flg_legend; + } + void set_flg_legend(int value) { + ptr_->flg_legend = value; + } + emscripten::val flg_ticklabel() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->flg_ticklabel)); + } + int flg_extend() const { + return ptr_->flg_extend; + } + void set_flg_extend(int value) { + ptr_->flg_extend = value; + } + int flg_barplot() const { + return ptr_->flg_barplot; + } + void set_flg_barplot(int value) { + ptr_->flg_barplot = value; + } + int flg_selection() const { + return ptr_->flg_selection; + } + void set_flg_selection(int value) { + ptr_->flg_selection = value; + } + int flg_symmetric() const { + return ptr_->flg_symmetric; + } + void set_flg_symmetric(int value) { + ptr_->flg_symmetric = value; + } + float linewidth() const { + return ptr_->linewidth; + } + void set_linewidth(float value) { + ptr_->linewidth = value; + } + float gridwidth() const { + return ptr_->gridwidth; + } + void set_gridwidth(float value) { + ptr_->gridwidth = value; + } + emscripten::val gridsize() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->gridsize)); + } + emscripten::val gridrgb() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->gridrgb)); + } + emscripten::val figurergba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->figurergba)); + } + emscripten::val panergba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->panergba)); + } + emscripten::val legendrgba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->legendrgba)); + } + emscripten::val textrgb() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->textrgb)); + } + emscripten::val linergb() const { + return emscripten::val(emscripten::typed_memory_view(300, reinterpret_cast(ptr_->linergb))); + } + emscripten::val range() const { + return emscripten::val(emscripten::typed_memory_view(4, reinterpret_cast(ptr_->range))); + } + emscripten::val xformat() const { + return emscripten::val(emscripten::typed_memory_view(20, ptr_->xformat)); + } + emscripten::val yformat() const { + return emscripten::val(emscripten::typed_memory_view(20, ptr_->yformat)); + } + emscripten::val minwidth() const { + return emscripten::val(emscripten::typed_memory_view(20, ptr_->minwidth)); + } + emscripten::val title() const { + return emscripten::val(emscripten::typed_memory_view(1000, ptr_->title)); + } + emscripten::val xlabel() const { + return emscripten::val(emscripten::typed_memory_view(100, ptr_->xlabel)); + } + emscripten::val linename() const { + return emscripten::val(emscripten::typed_memory_view(10000, reinterpret_cast(ptr_->linename))); + } + int legendoffset() const { + return ptr_->legendoffset; + } + void set_legendoffset(int value) { + ptr_->legendoffset = value; + } + int subplot() const { + return ptr_->subplot; + } + void set_subplot(int value) { + ptr_->subplot = value; + } + emscripten::val highlight() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->highlight)); + } + int highlightid() const { + return ptr_->highlightid; + } + void set_highlightid(int value) { + ptr_->highlightid = value; + } + float selection() const { + return ptr_->selection; + } + void set_selection(float value) { + ptr_->selection = value; + } + emscripten::val linepnt() const { + return emscripten::val(emscripten::typed_memory_view(100, ptr_->linepnt)); + } + emscripten::val linedata() const { + return emscripten::val(emscripten::typed_memory_view(200200, reinterpret_cast(ptr_->linedata))); + } + emscripten::val xaxispixel() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->xaxispixel)); + } + emscripten::val yaxispixel() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->yaxispixel)); + } + emscripten::val xaxisdata() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->xaxisdata)); + } + emscripten::val yaxisdata() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->yaxisdata)); + } + mjvFigure* get() const { return ptr_; } + void set(mjvFigure* ptr) { ptr_ = ptr; } + + private: + mjvFigure* ptr_; + bool owned_ = false; +}; + +struct MjvGLCamera { + MjvGLCamera(); + MjvGLCamera(const MjvGLCamera &); + MjvGLCamera &operator=(const MjvGLCamera &); + explicit MjvGLCamera(mjvGLCamera *ptr); + ~MjvGLCamera(); + std::unique_ptr copy(); + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val forward() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->forward)); + } + emscripten::val up() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->up)); + } + float frustum_center() const { + return ptr_->frustum_center; + } + void set_frustum_center(float value) { + ptr_->frustum_center = value; + } + float frustum_width() const { + return ptr_->frustum_width; + } + void set_frustum_width(float value) { + ptr_->frustum_width = value; + } + float frustum_bottom() const { + return ptr_->frustum_bottom; + } + void set_frustum_bottom(float value) { + ptr_->frustum_bottom = value; + } + float frustum_top() const { + return ptr_->frustum_top; + } + void set_frustum_top(float value) { + ptr_->frustum_top = value; + } + float frustum_near() const { + return ptr_->frustum_near; + } + void set_frustum_near(float value) { + ptr_->frustum_near = value; + } + float frustum_far() const { + return ptr_->frustum_far; + } + void set_frustum_far(float value) { + ptr_->frustum_far = value; + } + int orthographic() const { + return ptr_->orthographic; + } + void set_orthographic(int value) { + ptr_->orthographic = value; + } + mjvGLCamera* get() const { return ptr_; } + void set(mjvGLCamera* ptr) { ptr_ = ptr; } + + private: + mjvGLCamera* ptr_; + bool owned_ = false; +}; + +struct MjvGeom { + MjvGeom(); + MjvGeom(const MjvGeom &); + MjvGeom &operator=(const MjvGeom &); + explicit MjvGeom(mjvGeom *ptr); + ~MjvGeom(); + std::unique_ptr copy(); + int type() const { + return ptr_->type; + } + void set_type(int value) { + ptr_->type = value; + } + int dataid() const { + return ptr_->dataid; + } + void set_dataid(int value) { + ptr_->dataid = value; + } + int objtype() const { + return ptr_->objtype; + } + void set_objtype(int value) { + ptr_->objtype = value; + } + int objid() const { + return ptr_->objid; + } + void set_objid(int value) { + ptr_->objid = value; + } + int category() const { + return ptr_->category; + } + void set_category(int value) { + ptr_->category = value; + } + int matid() const { + return ptr_->matid; + } + void set_matid(int value) { + ptr_->matid = value; + } + int texcoord() const { + return ptr_->texcoord; + } + void set_texcoord(int value) { + ptr_->texcoord = value; + } + int segid() const { + return ptr_->segid; + } + void set_segid(int value) { + ptr_->segid = value; + } + emscripten::val size() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->size)); + } + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val mat() const { + return emscripten::val(emscripten::typed_memory_view(9, ptr_->mat)); + } + emscripten::val rgba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->rgba)); + } + float emission() const { + return ptr_->emission; + } + void set_emission(float value) { + ptr_->emission = value; + } + float specular() const { + return ptr_->specular; + } + void set_specular(float value) { + ptr_->specular = value; + } + float shininess() const { + return ptr_->shininess; + } + void set_shininess(float value) { + ptr_->shininess = value; + } + float reflectance() const { + return ptr_->reflectance; + } + void set_reflectance(float value) { + ptr_->reflectance = value; + } + emscripten::val label() const { + return emscripten::val(emscripten::typed_memory_view(100, ptr_->label)); + } + float camdist() const { + return ptr_->camdist; + } + void set_camdist(float value) { + ptr_->camdist = value; + } + float modelrbound() const { + return ptr_->modelrbound; + } + void set_modelrbound(float value) { + ptr_->modelrbound = value; + } + mjtByte transparent() const { + return ptr_->transparent; + } + void set_transparent(mjtByte value) { + ptr_->transparent = value; + } + mjvGeom* get() const { return ptr_; } + void set(mjvGeom* ptr) { ptr_ = ptr; } + + private: + mjvGeom* ptr_; + bool owned_ = false; +}; + +struct MjvLight { + MjvLight(); + MjvLight(const MjvLight &); + MjvLight &operator=(const MjvLight &); + explicit MjvLight(mjvLight *ptr); + ~MjvLight(); + std::unique_ptr copy(); + int id() const { + return ptr_->id; + } + void set_id(int value) { + ptr_->id = value; + } + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val dir() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->dir)); + } + int type() const { + return ptr_->type; + } + void set_type(int value) { + ptr_->type = value; + } + int texid() const { + return ptr_->texid; + } + void set_texid(int value) { + ptr_->texid = value; + } + emscripten::val attenuation() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->attenuation)); + } + float cutoff() const { + return ptr_->cutoff; + } + void set_cutoff(float value) { + ptr_->cutoff = value; + } + float exponent() const { + return ptr_->exponent; + } + void set_exponent(float value) { + ptr_->exponent = value; + } + emscripten::val ambient() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->ambient)); + } + emscripten::val diffuse() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->diffuse)); + } + emscripten::val specular() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->specular)); + } + mjtByte headlight() const { + return ptr_->headlight; + } + void set_headlight(mjtByte value) { + ptr_->headlight = value; + } + mjtByte castshadow() const { + return ptr_->castshadow; + } + void set_castshadow(mjtByte value) { + ptr_->castshadow = value; + } + float bulbradius() const { + return ptr_->bulbradius; + } + void set_bulbradius(float value) { + ptr_->bulbradius = value; + } + float intensity() const { + return ptr_->intensity; + } + void set_intensity(float value) { + ptr_->intensity = value; + } + float range() const { + return ptr_->range; + } + void set_range(float value) { + ptr_->range = value; + } + mjvLight* get() const { return ptr_; } + void set(mjvLight* ptr) { ptr_ = ptr; } + + private: + mjvLight* ptr_; + bool owned_ = false; +}; + +struct MjvOption { + MjvOption(); + MjvOption(const MjvOption &); + MjvOption &operator=(const MjvOption &); + explicit MjvOption(mjvOption *ptr); + ~MjvOption(); + std::unique_ptr copy(); + int label() const { + return ptr_->label; + } + void set_label(int value) { + ptr_->label = value; + } + int frame() const { + return ptr_->frame; + } + void set_frame(int value) { + ptr_->frame = value; + } + emscripten::val geomgroup() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->geomgroup)); + } + emscripten::val sitegroup() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->sitegroup)); + } + emscripten::val jointgroup() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->jointgroup)); + } + emscripten::val tendongroup() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->tendongroup)); + } + emscripten::val actuatorgroup() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->actuatorgroup)); + } + emscripten::val flexgroup() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->flexgroup)); + } + emscripten::val skingroup() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->skingroup)); + } + emscripten::val flags() const { + return emscripten::val(emscripten::typed_memory_view(31, ptr_->flags)); + } + int bvh_depth() const { + return ptr_->bvh_depth; + } + void set_bvh_depth(int value) { + ptr_->bvh_depth = value; + } + int flex_layer() const { + return ptr_->flex_layer; + } + void set_flex_layer(int value) { + ptr_->flex_layer = value; + } + mjvOption* get() const { return ptr_; } + void set(mjvOption* ptr) { ptr_ = ptr; } + + private: + mjvOption* ptr_; + bool owned_ = false; +}; + +struct MjvPerturb { + MjvPerturb(); + MjvPerturb(const MjvPerturb &); + MjvPerturb &operator=(const MjvPerturb &); + explicit MjvPerturb(mjvPerturb *ptr); + ~MjvPerturb(); + std::unique_ptr copy(); + int select() const { + return ptr_->select; + } + void set_select(int value) { + ptr_->select = value; + } + int flexselect() const { + return ptr_->flexselect; + } + void set_flexselect(int value) { + ptr_->flexselect = value; + } + int skinselect() const { + return ptr_->skinselect; + } + void set_skinselect(int value) { + ptr_->skinselect = value; + } + int active() const { + return ptr_->active; + } + void set_active(int value) { + ptr_->active = value; + } + int active2() const { + return ptr_->active2; + } + void set_active2(int value) { + ptr_->active2 = value; + } + emscripten::val refpos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->refpos)); + } + emscripten::val refquat() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->refquat)); + } + emscripten::val refselpos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->refselpos)); + } + emscripten::val localpos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->localpos)); + } + mjtNum localmass() const { + return ptr_->localmass; + } + void set_localmass(mjtNum value) { + ptr_->localmass = value; + } + mjtNum scale() const { + return ptr_->scale; + } + void set_scale(mjtNum value) { + ptr_->scale = value; + } + mjvPerturb* get() const { return ptr_; } + void set(mjvPerturb* ptr) { ptr_ = ptr; } + + private: + mjvPerturb* ptr_; + bool owned_ = false; +}; + +struct MjsCompiler { + explicit MjsCompiler(mjsCompiler *ptr); + ~MjsCompiler(); + mjtByte autolimits() const { + return ptr_->autolimits; + } + void set_autolimits(mjtByte value) { + ptr_->autolimits = value; + } + double boundmass() const { + return ptr_->boundmass; + } + void set_boundmass(double value) { + ptr_->boundmass = value; + } + double boundinertia() const { + return ptr_->boundinertia; + } + void set_boundinertia(double value) { + ptr_->boundinertia = value; + } + double settotalmass() const { + return ptr_->settotalmass; + } + void set_settotalmass(double value) { + ptr_->settotalmass = value; + } + mjtByte balanceinertia() const { + return ptr_->balanceinertia; + } + void set_balanceinertia(mjtByte value) { + ptr_->balanceinertia = value; + } + mjtByte fitaabb() const { + return ptr_->fitaabb; + } + void set_fitaabb(mjtByte value) { + ptr_->fitaabb = value; + } + mjtByte degree() const { + return ptr_->degree; + } + void set_degree(mjtByte value) { + ptr_->degree = value; + } + emscripten::val eulerseq() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->eulerseq)); + } + mjtByte discardvisual() const { + return ptr_->discardvisual; + } + void set_discardvisual(mjtByte value) { + ptr_->discardvisual = value; + } + mjtByte usethread() const { + return ptr_->usethread; + } + void set_usethread(mjtByte value) { + ptr_->usethread = value; + } + mjtByte fusestatic() const { + return ptr_->fusestatic; + } + void set_fusestatic(mjtByte value) { + ptr_->fusestatic = value; + } + int inertiafromgeom() const { + return ptr_->inertiafromgeom; + } + void set_inertiafromgeom(int value) { + ptr_->inertiafromgeom = value; + } + emscripten::val inertiagrouprange() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->inertiagrouprange)); + } + mjtByte saveinertial() const { + return ptr_->saveinertial; + } + void set_saveinertial(mjtByte value) { + ptr_->saveinertial = value; + } + int alignfree() const { + return ptr_->alignfree; + } + void set_alignfree(int value) { + ptr_->alignfree = value; + } + mjString meshdir() const { + return (ptr_ && ptr_->meshdir) ? *(ptr_->meshdir) : ""; + } + void set_meshdir(const mjString& value) { + if (ptr_ && ptr_->meshdir) { + *(ptr_->meshdir) = value; + } + } + mjString texturedir() const { + return (ptr_ && ptr_->texturedir) ? *(ptr_->texturedir) : ""; + } + void set_texturedir(const mjString& value) { + if (ptr_ && ptr_->texturedir) { + *(ptr_->texturedir) = value; + } + } + mjsCompiler* get() const { return ptr_; } + void set(mjsCompiler* ptr) { ptr_ = ptr; } + + private: + mjsCompiler* ptr_; + bool owned_ = false; + + public: + MjLROpt LRopt; +}; + +struct MjsEquality { + explicit MjsEquality(mjsEquality *ptr); + ~MjsEquality(); + mjtEq type() const { + return ptr_->type; + } + void set_type(mjtEq value) { + ptr_->type = value; + } + emscripten::val data() const { + return emscripten::val(emscripten::typed_memory_view(11, ptr_->data)); + } + mjtByte active() const { + return ptr_->active; + } + void set_active(mjtByte value) { + ptr_->active = value; + } + mjString name1() const { + return (ptr_ && ptr_->name1) ? *(ptr_->name1) : ""; + } + void set_name1(const mjString& value) { + if (ptr_ && ptr_->name1) { + *(ptr_->name1) = value; + } + } + mjString name2() const { + return (ptr_ && ptr_->name2) ? *(ptr_->name2) : ""; + } + void set_name2(const mjString& value) { + if (ptr_ && ptr_->name2) { + *(ptr_->name2) = value; + } + } + mjtObj objtype() const { + return ptr_->objtype; + } + void set_objtype(mjtObj value) { + ptr_->objtype = value; + } + emscripten::val solref() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solref)); + } + emscripten::val solimp() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->solimp)); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsEquality* get() const { return ptr_; } + void set(mjsEquality* ptr) { ptr_ = ptr; } + + private: + mjsEquality* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsExclude { + explicit MjsExclude(mjsExclude *ptr); + ~MjsExclude(); + mjString bodyname1() const { + return (ptr_ && ptr_->bodyname1) ? *(ptr_->bodyname1) : ""; + } + void set_bodyname1(const mjString& value) { + if (ptr_ && ptr_->bodyname1) { + *(ptr_->bodyname1) = value; + } + } + mjString bodyname2() const { + return (ptr_ && ptr_->bodyname2) ? *(ptr_->bodyname2) : ""; + } + void set_bodyname2(const mjString& value) { + if (ptr_ && ptr_->bodyname2) { + *(ptr_->bodyname2) = value; + } + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsExclude* get() const { return ptr_; } + void set(mjsExclude* ptr) { ptr_ = ptr; } + + private: + mjsExclude* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsFlex { + explicit MjsFlex(mjsFlex *ptr); + ~MjsFlex(); + int contype() const { + return ptr_->contype; + } + void set_contype(int value) { + ptr_->contype = value; + } + int conaffinity() const { + return ptr_->conaffinity; + } + void set_conaffinity(int value) { + ptr_->conaffinity = value; + } + int condim() const { + return ptr_->condim; + } + void set_condim(int value) { + ptr_->condim = value; + } + int priority() const { + return ptr_->priority; + } + void set_priority(int value) { + ptr_->priority = value; + } + emscripten::val friction() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->friction)); + } + double solmix() const { + return ptr_->solmix; + } + void set_solmix(double value) { + ptr_->solmix = value; + } + emscripten::val solref() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solref)); + } + emscripten::val solimp() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->solimp)); + } + double margin() const { + return ptr_->margin; + } + void set_margin(double value) { + ptr_->margin = value; + } + double gap() const { + return ptr_->gap; + } + void set_gap(double value) { + ptr_->gap = value; + } + int dim() const { + return ptr_->dim; + } + void set_dim(int value) { + ptr_->dim = value; + } + double radius() const { + return ptr_->radius; + } + void set_radius(double value) { + ptr_->radius = value; + } + mjtByte internal() const { + return ptr_->internal; + } + void set_internal(mjtByte value) { + ptr_->internal = value; + } + mjtByte flatskin() const { + return ptr_->flatskin; + } + void set_flatskin(mjtByte value) { + ptr_->flatskin = value; + } + int selfcollide() const { + return ptr_->selfcollide; + } + void set_selfcollide(int value) { + ptr_->selfcollide = value; + } + int vertcollide() const { + return ptr_->vertcollide; + } + void set_vertcollide(int value) { + ptr_->vertcollide = value; + } + int passive() const { + return ptr_->passive; + } + void set_passive(int value) { + ptr_->passive = value; + } + int activelayers() const { + return ptr_->activelayers; + } + void set_activelayers(int value) { + ptr_->activelayers = value; + } + int group() const { + return ptr_->group; + } + void set_group(int value) { + ptr_->group = value; + } + double edgestiffness() const { + return ptr_->edgestiffness; + } + void set_edgestiffness(double value) { + ptr_->edgestiffness = value; + } + double edgedamping() const { + return ptr_->edgedamping; + } + void set_edgedamping(double value) { + ptr_->edgedamping = value; + } + emscripten::val rgba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->rgba)); + } + mjString material() const { + return (ptr_ && ptr_->material) ? *(ptr_->material) : ""; + } + void set_material(const mjString& value) { + if (ptr_ && ptr_->material) { + *(ptr_->material) = value; + } + } + double young() const { + return ptr_->young; + } + void set_young(double value) { + ptr_->young = value; + } + double poisson() const { + return ptr_->poisson; + } + void set_poisson(double value) { + ptr_->poisson = value; + } + double damping() const { + return ptr_->damping; + } + void set_damping(double value) { + ptr_->damping = value; + } + double thickness() const { + return ptr_->thickness; + } + void set_thickness(double value) { + ptr_->thickness = value; + } + int elastic2d() const { + return ptr_->elastic2d; + } + void set_elastic2d(int value) { + ptr_->elastic2d = value; + } + mjStringVec &nodebody() const { + return *(ptr_->nodebody); + } + mjStringVec &vertbody() const { + return *(ptr_->vertbody); + } + mjDoubleVec &node() const { + return *(ptr_->node); + } + mjDoubleVec &vert() const { + return *(ptr_->vert); + } + mjIntVec &elem() const { + return *(ptr_->elem); + } + mjFloatVec &texcoord() const { + return *(ptr_->texcoord); + } + mjIntVec &elemtexcoord() const { + return *(ptr_->elemtexcoord); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsFlex* get() const { return ptr_; } + void set(mjsFlex* ptr) { ptr_ = ptr; } + + private: + mjsFlex* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsHField { + explicit MjsHField(mjsHField *ptr); + ~MjsHField(); + mjString content_type() const { + return (ptr_ && ptr_->content_type) ? *(ptr_->content_type) : ""; + } + void set_content_type(const mjString& value) { + if (ptr_ && ptr_->content_type) { + *(ptr_->content_type) = value; + } + } + mjString file() const { + return (ptr_ && ptr_->file) ? *(ptr_->file) : ""; + } + void set_file(const mjString& value) { + if (ptr_ && ptr_->file) { + *(ptr_->file) = value; + } + } + emscripten::val size() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->size)); + } + int nrow() const { + return ptr_->nrow; + } + void set_nrow(int value) { + ptr_->nrow = value; + } + int ncol() const { + return ptr_->ncol; + } + void set_ncol(int value) { + ptr_->ncol = value; + } + mjFloatVec &userdata() const { + return *(ptr_->userdata); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsHField* get() const { return ptr_; } + void set(mjsHField* ptr) { ptr_ = ptr; } + + private: + mjsHField* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsJoint { + explicit MjsJoint(mjsJoint *ptr); + ~MjsJoint(); + mjtJoint type() const { + return ptr_->type; + } + void set_type(mjtJoint value) { + ptr_->type = value; + } + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val axis() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->axis)); + } + double ref() const { + return ptr_->ref; + } + void set_ref(double value) { + ptr_->ref = value; + } + int align() const { + return ptr_->align; + } + void set_align(int value) { + ptr_->align = value; + } + double stiffness() const { + return ptr_->stiffness; + } + void set_stiffness(double value) { + ptr_->stiffness = value; + } + double springref() const { + return ptr_->springref; + } + void set_springref(double value) { + ptr_->springref = value; + } + emscripten::val springdamper() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->springdamper)); + } + int limited() const { + return ptr_->limited; + } + void set_limited(int value) { + ptr_->limited = value; + } + emscripten::val range() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->range)); + } + double margin() const { + return ptr_->margin; + } + void set_margin(double value) { + ptr_->margin = value; + } + emscripten::val solref_limit() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solref_limit)); + } + emscripten::val solimp_limit() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->solimp_limit)); + } + int actfrclimited() const { + return ptr_->actfrclimited; + } + void set_actfrclimited(int value) { + ptr_->actfrclimited = value; + } + emscripten::val actfrcrange() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->actfrcrange)); + } + double armature() const { + return ptr_->armature; + } + void set_armature(double value) { + ptr_->armature = value; + } + double damping() const { + return ptr_->damping; + } + void set_damping(double value) { + ptr_->damping = value; + } + double frictionloss() const { + return ptr_->frictionloss; + } + void set_frictionloss(double value) { + ptr_->frictionloss = value; + } + emscripten::val solref_friction() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solref_friction)); + } + emscripten::val solimp_friction() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->solimp_friction)); + } + int group() const { + return ptr_->group; + } + void set_group(int value) { + ptr_->group = value; + } + mjtByte actgravcomp() const { + return ptr_->actgravcomp; + } + void set_actgravcomp(mjtByte value) { + ptr_->actgravcomp = value; + } + mjDoubleVec &userdata() const { + return *(ptr_->userdata); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsJoint* get() const { return ptr_; } + void set(mjsJoint* ptr) { ptr_ = ptr; } + + private: + mjsJoint* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsKey { + explicit MjsKey(mjsKey *ptr); + ~MjsKey(); + double time() const { + return ptr_->time; + } + void set_time(double value) { + ptr_->time = value; + } + mjDoubleVec &qpos() const { + return *(ptr_->qpos); + } + mjDoubleVec &qvel() const { + return *(ptr_->qvel); + } + mjDoubleVec &act() const { + return *(ptr_->act); + } + mjDoubleVec &mpos() const { + return *(ptr_->mpos); + } + mjDoubleVec &mquat() const { + return *(ptr_->mquat); + } + mjDoubleVec &ctrl() const { + return *(ptr_->ctrl); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsKey* get() const { return ptr_; } + void set(mjsKey* ptr) { ptr_ = ptr; } + + private: + mjsKey* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsLight { + explicit MjsLight(mjsLight *ptr); + ~MjsLight(); + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val dir() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->dir)); + } + mjtCamLight mode() const { + return ptr_->mode; + } + void set_mode(mjtCamLight value) { + ptr_->mode = value; + } + mjString targetbody() const { + return (ptr_ && ptr_->targetbody) ? *(ptr_->targetbody) : ""; + } + void set_targetbody(const mjString& value) { + if (ptr_ && ptr_->targetbody) { + *(ptr_->targetbody) = value; + } + } + mjtByte active() const { + return ptr_->active; + } + void set_active(mjtByte value) { + ptr_->active = value; + } + mjtLightType type() const { + return ptr_->type; + } + void set_type(mjtLightType value) { + ptr_->type = value; + } + mjString texture() const { + return (ptr_ && ptr_->texture) ? *(ptr_->texture) : ""; + } + void set_texture(const mjString& value) { + if (ptr_ && ptr_->texture) { + *(ptr_->texture) = value; + } + } + mjtByte castshadow() const { + return ptr_->castshadow; + } + void set_castshadow(mjtByte value) { + ptr_->castshadow = value; + } + float bulbradius() const { + return ptr_->bulbradius; + } + void set_bulbradius(float value) { + ptr_->bulbradius = value; + } + float intensity() const { + return ptr_->intensity; + } + void set_intensity(float value) { + ptr_->intensity = value; + } + float range() const { + return ptr_->range; + } + void set_range(float value) { + ptr_->range = value; + } + emscripten::val attenuation() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->attenuation)); + } + float cutoff() const { + return ptr_->cutoff; + } + void set_cutoff(float value) { + ptr_->cutoff = value; + } + float exponent() const { + return ptr_->exponent; + } + void set_exponent(float value) { + ptr_->exponent = value; + } + emscripten::val ambient() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->ambient)); + } + emscripten::val diffuse() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->diffuse)); + } + emscripten::val specular() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->specular)); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsLight* get() const { return ptr_; } + void set(mjsLight* ptr) { ptr_ = ptr; } + + private: + mjsLight* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsMaterial { + explicit MjsMaterial(mjsMaterial *ptr); + ~MjsMaterial(); + mjStringVec &textures() const { + return *(ptr_->textures); + } + mjtByte texuniform() const { + return ptr_->texuniform; + } + void set_texuniform(mjtByte value) { + ptr_->texuniform = value; + } + emscripten::val texrepeat() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->texrepeat)); + } + float emission() const { + return ptr_->emission; + } + void set_emission(float value) { + ptr_->emission = value; + } + float specular() const { + return ptr_->specular; + } + void set_specular(float value) { + ptr_->specular = value; + } + float shininess() const { + return ptr_->shininess; + } + void set_shininess(float value) { + ptr_->shininess = value; + } + float reflectance() const { + return ptr_->reflectance; + } + void set_reflectance(float value) { + ptr_->reflectance = value; + } + float metallic() const { + return ptr_->metallic; + } + void set_metallic(float value) { + ptr_->metallic = value; + } + float roughness() const { + return ptr_->roughness; + } + void set_roughness(float value) { + ptr_->roughness = value; + } + emscripten::val rgba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->rgba)); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsMaterial* get() const { return ptr_; } + void set(mjsMaterial* ptr) { ptr_ = ptr; } + + private: + mjsMaterial* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsNumeric { + explicit MjsNumeric(mjsNumeric *ptr); + ~MjsNumeric(); + mjDoubleVec &data() const { + return *(ptr_->data); + } + int size() const { + return ptr_->size; + } + void set_size(int value) { + ptr_->size = value; + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsNumeric* get() const { return ptr_; } + void set(mjsNumeric* ptr) { ptr_ = ptr; } + + private: + mjsNumeric* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsPair { + explicit MjsPair(mjsPair *ptr); + ~MjsPair(); + mjString geomname1() const { + return (ptr_ && ptr_->geomname1) ? *(ptr_->geomname1) : ""; + } + void set_geomname1(const mjString& value) { + if (ptr_ && ptr_->geomname1) { + *(ptr_->geomname1) = value; + } + } + mjString geomname2() const { + return (ptr_ && ptr_->geomname2) ? *(ptr_->geomname2) : ""; + } + void set_geomname2(const mjString& value) { + if (ptr_ && ptr_->geomname2) { + *(ptr_->geomname2) = value; + } + } + int condim() const { + return ptr_->condim; + } + void set_condim(int value) { + ptr_->condim = value; + } + emscripten::val solref() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solref)); + } + emscripten::val solreffriction() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solreffriction)); + } + emscripten::val solimp() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->solimp)); + } + double margin() const { + return ptr_->margin; + } + void set_margin(double value) { + ptr_->margin = value; + } + double gap() const { + return ptr_->gap; + } + void set_gap(double value) { + ptr_->gap = value; + } + emscripten::val friction() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->friction)); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsPair* get() const { return ptr_; } + void set(mjsPair* ptr) { ptr_ = ptr; } + + private: + mjsPair* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsPlugin { + explicit MjsPlugin(mjsPlugin *ptr); + ~MjsPlugin(); + mjString name() const { + return (ptr_ && ptr_->name) ? *(ptr_->name) : ""; + } + void set_name(const mjString& value) { + if (ptr_ && ptr_->name) { + *(ptr_->name) = value; + } + } + mjString plugin_name() const { + return (ptr_ && ptr_->plugin_name) ? *(ptr_->plugin_name) : ""; + } + void set_plugin_name(const mjString& value) { + if (ptr_ && ptr_->plugin_name) { + *(ptr_->plugin_name) = value; + } + } + mjtByte active() const { + return ptr_->active; + } + void set_active(mjtByte value) { + ptr_->active = value; + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsPlugin* get() const { return ptr_; } + void set(mjsPlugin* ptr) { ptr_ = ptr; } + + private: + mjsPlugin* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsSkin { + explicit MjsSkin(mjsSkin *ptr); + ~MjsSkin(); + mjString file() const { + return (ptr_ && ptr_->file) ? *(ptr_->file) : ""; + } + void set_file(const mjString& value) { + if (ptr_ && ptr_->file) { + *(ptr_->file) = value; + } + } + mjString material() const { + return (ptr_ && ptr_->material) ? *(ptr_->material) : ""; + } + void set_material(const mjString& value) { + if (ptr_ && ptr_->material) { + *(ptr_->material) = value; + } + } + emscripten::val rgba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->rgba)); + } + float inflate() const { + return ptr_->inflate; + } + void set_inflate(float value) { + ptr_->inflate = value; + } + int group() const { + return ptr_->group; + } + void set_group(int value) { + ptr_->group = value; + } + mjFloatVec &vert() const { + return *(ptr_->vert); + } + mjFloatVec &texcoord() const { + return *(ptr_->texcoord); + } + mjIntVec &face() const { + return *(ptr_->face); + } + mjStringVec &bodyname() const { + return *(ptr_->bodyname); + } + mjFloatVec &bindpos() const { + return *(ptr_->bindpos); + } + mjFloatVec &bindquat() const { + return *(ptr_->bindquat); + } + mjIntVecVec &vertid() const { + return *(ptr_->vertid); + } + mjFloatVecVec &vertweight() const { + return *(ptr_->vertweight); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsSkin* get() const { return ptr_; } + void set(mjsSkin* ptr) { ptr_ = ptr; } + + private: + mjsSkin* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsTendon { + explicit MjsTendon(mjsTendon *ptr); + ~MjsTendon(); + double stiffness() const { + return ptr_->stiffness; + } + void set_stiffness(double value) { + ptr_->stiffness = value; + } + emscripten::val springlength() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->springlength)); + } + double damping() const { + return ptr_->damping; + } + void set_damping(double value) { + ptr_->damping = value; + } + double frictionloss() const { + return ptr_->frictionloss; + } + void set_frictionloss(double value) { + ptr_->frictionloss = value; + } + emscripten::val solref_friction() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solref_friction)); + } + emscripten::val solimp_friction() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->solimp_friction)); + } + double armature() const { + return ptr_->armature; + } + void set_armature(double value) { + ptr_->armature = value; + } + int limited() const { + return ptr_->limited; + } + void set_limited(int value) { + ptr_->limited = value; + } + int actfrclimited() const { + return ptr_->actfrclimited; + } + void set_actfrclimited(int value) { + ptr_->actfrclimited = value; + } + emscripten::val range() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->range)); + } + emscripten::val actfrcrange() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->actfrcrange)); + } + double margin() const { + return ptr_->margin; + } + void set_margin(double value) { + ptr_->margin = value; + } + emscripten::val solref_limit() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solref_limit)); + } + emscripten::val solimp_limit() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->solimp_limit)); + } + mjString material() const { + return (ptr_ && ptr_->material) ? *(ptr_->material) : ""; + } + void set_material(const mjString& value) { + if (ptr_ && ptr_->material) { + *(ptr_->material) = value; + } + } + double width() const { + return ptr_->width; + } + void set_width(double value) { + ptr_->width = value; + } + emscripten::val rgba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->rgba)); + } + int group() const { + return ptr_->group; + } + void set_group(int value) { + ptr_->group = value; + } + mjDoubleVec &userdata() const { + return *(ptr_->userdata); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsTendon* get() const { return ptr_; } + void set(mjsTendon* ptr) { ptr_ = ptr; } + + private: + mjsTendon* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsText { + explicit MjsText(mjsText *ptr); + ~MjsText(); + mjString data() const { + return (ptr_ && ptr_->data) ? *(ptr_->data) : ""; + } + void set_data(const mjString& value) { + if (ptr_ && ptr_->data) { + *(ptr_->data) = value; + } + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsText* get() const { return ptr_; } + void set(mjsText* ptr) { ptr_ = ptr; } + + private: + mjsText* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsTexture { + explicit MjsTexture(mjsTexture *ptr); + ~MjsTexture(); + mjtTexture type() const { + return ptr_->type; + } + void set_type(mjtTexture value) { + ptr_->type = value; + } + mjtColorSpace colorspace() const { + return ptr_->colorspace; + } + void set_colorspace(mjtColorSpace value) { + ptr_->colorspace = value; + } + int builtin() const { + return ptr_->builtin; + } + void set_builtin(int value) { + ptr_->builtin = value; + } + int mark() const { + return ptr_->mark; + } + void set_mark(int value) { + ptr_->mark = value; + } + emscripten::val rgb1() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->rgb1)); + } + emscripten::val rgb2() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->rgb2)); + } + emscripten::val markrgb() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->markrgb)); + } + double random() const { + return ptr_->random; + } + void set_random(double value) { + ptr_->random = value; + } + int height() const { + return ptr_->height; + } + void set_height(int value) { + ptr_->height = value; + } + int width() const { + return ptr_->width; + } + void set_width(int value) { + ptr_->width = value; + } + int nchannel() const { + return ptr_->nchannel; + } + void set_nchannel(int value) { + ptr_->nchannel = value; + } + mjString content_type() const { + return (ptr_ && ptr_->content_type) ? *(ptr_->content_type) : ""; + } + void set_content_type(const mjString& value) { + if (ptr_ && ptr_->content_type) { + *(ptr_->content_type) = value; + } + } + mjString file() const { + return (ptr_ && ptr_->file) ? *(ptr_->file) : ""; + } + void set_file(const mjString& value) { + if (ptr_ && ptr_->file) { + *(ptr_->file) = value; + } + } + emscripten::val gridsize() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->gridsize)); + } + emscripten::val gridlayout() const { + return emscripten::val(emscripten::typed_memory_view(13, ptr_->gridlayout)); + } + mjStringVec &cubefiles() const { + return *(ptr_->cubefiles); + } + std::vector &data() const { + return *(reinterpret_cast*>(ptr_->data)); + } + mjtByte hflip() const { + return ptr_->hflip; + } + void set_hflip(mjtByte value) { + ptr_->hflip = value; + } + mjtByte vflip() const { + return ptr_->vflip; + } + void set_vflip(mjtByte value) { + ptr_->vflip = value; + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsTexture* get() const { return ptr_; } + void set(mjsTexture* ptr) { ptr_ = ptr; } + + private: + mjsTexture* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsTuple { + explicit MjsTuple(mjsTuple *ptr); + ~MjsTuple(); + mjIntVec &objtype() const { + return *(ptr_->objtype); + } + mjStringVec &objname() const { + return *(ptr_->objname); + } + mjDoubleVec &objprm() const { + return *(ptr_->objprm); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsTuple* get() const { return ptr_; } + void set(mjsTuple* ptr) { ptr_ = ptr; } + + private: + mjsTuple* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsWrap { + explicit MjsWrap(mjsWrap *ptr); + ~MjsWrap(); + mjtWrap type() const { + return ptr_->type; + } + void set_type(mjtWrap value) { + ptr_->type = value; + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsWrap* get() const { return ptr_; } + void set(mjsWrap* ptr) { ptr_ = ptr; } + + private: + mjsWrap* ptr_; + bool owned_ = false; + + public: + MjsElement element; +}; + +struct MjsCamera { + explicit MjsCamera(mjsCamera *ptr); + ~MjsCamera(); + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val quat() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->quat)); + } + mjtCamLight mode() const { + return ptr_->mode; + } + void set_mode(mjtCamLight value) { + ptr_->mode = value; + } + mjString targetbody() const { + return (ptr_ && ptr_->targetbody) ? *(ptr_->targetbody) : ""; + } + void set_targetbody(const mjString& value) { + if (ptr_ && ptr_->targetbody) { + *(ptr_->targetbody) = value; + } + } + int orthographic() const { + return ptr_->orthographic; + } + void set_orthographic(int value) { + ptr_->orthographic = value; + } + double fovy() const { + return ptr_->fovy; + } + void set_fovy(double value) { + ptr_->fovy = value; + } + double ipd() const { + return ptr_->ipd; + } + void set_ipd(double value) { + ptr_->ipd = value; + } + emscripten::val intrinsic() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->intrinsic)); + } + emscripten::val sensor_size() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->sensor_size)); + } + emscripten::val resolution() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->resolution)); + } + emscripten::val focal_length() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->focal_length)); + } + emscripten::val focal_pixel() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->focal_pixel)); + } + emscripten::val principal_length() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->principal_length)); + } + emscripten::val principal_pixel() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->principal_pixel)); + } + mjDoubleVec &userdata() const { + return *(ptr_->userdata); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsCamera* get() const { return ptr_; } + void set(mjsCamera* ptr) { ptr_ = ptr; } + + private: + mjsCamera* ptr_; + bool owned_ = false; + + public: + MjsElement element; + MjsOrientation alt; +}; + +struct MjsFrame { + explicit MjsFrame(mjsFrame *ptr); + ~MjsFrame(); + mjString childclass() const { + return (ptr_ && ptr_->childclass) ? *(ptr_->childclass) : ""; + } + void set_childclass(const mjString& value) { + if (ptr_ && ptr_->childclass) { + *(ptr_->childclass) = value; + } + } + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val quat() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->quat)); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsFrame* get() const { return ptr_; } + void set(mjsFrame* ptr) { ptr_ = ptr; } + + private: + mjsFrame* ptr_; + bool owned_ = false; + + public: + MjsElement element; + MjsOrientation alt; +}; + +struct MjsSite { + explicit MjsSite(mjsSite *ptr); + ~MjsSite(); + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val quat() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->quat)); + } + emscripten::val fromto() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->fromto)); + } + emscripten::val size() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->size)); + } + mjtGeom type() const { + return ptr_->type; + } + void set_type(mjtGeom value) { + ptr_->type = value; + } + mjString material() const { + return (ptr_ && ptr_->material) ? *(ptr_->material) : ""; + } + void set_material(const mjString& value) { + if (ptr_ && ptr_->material) { + *(ptr_->material) = value; + } + } + int group() const { + return ptr_->group; + } + void set_group(int value) { + ptr_->group = value; + } + emscripten::val rgba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->rgba)); + } + mjDoubleVec &userdata() const { + return *(ptr_->userdata); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsSite* get() const { return ptr_; } + void set(mjsSite* ptr) { ptr_ = ptr; } + + private: + mjsSite* ptr_; + bool owned_ = false; + + public: + MjsElement element; + MjsOrientation alt; +}; + +struct MjsActuator { + explicit MjsActuator(mjsActuator *ptr); + ~MjsActuator(); + mjtGain gaintype() const { + return ptr_->gaintype; + } + void set_gaintype(mjtGain value) { + ptr_->gaintype = value; + } + emscripten::val gainprm() const { + return emscripten::val(emscripten::typed_memory_view(10, ptr_->gainprm)); + } + mjtBias biastype() const { + return ptr_->biastype; + } + void set_biastype(mjtBias value) { + ptr_->biastype = value; + } + emscripten::val biasprm() const { + return emscripten::val(emscripten::typed_memory_view(10, ptr_->biasprm)); + } + mjtDyn dyntype() const { + return ptr_->dyntype; + } + void set_dyntype(mjtDyn value) { + ptr_->dyntype = value; + } + emscripten::val dynprm() const { + return emscripten::val(emscripten::typed_memory_view(10, ptr_->dynprm)); + } + int actdim() const { + return ptr_->actdim; + } + void set_actdim(int value) { + ptr_->actdim = value; + } + mjtByte actearly() const { + return ptr_->actearly; + } + void set_actearly(mjtByte value) { + ptr_->actearly = value; + } + mjtTrn trntype() const { + return ptr_->trntype; + } + void set_trntype(mjtTrn value) { + ptr_->trntype = value; + } + emscripten::val gear() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->gear)); + } + mjString target() const { + return (ptr_ && ptr_->target) ? *(ptr_->target) : ""; + } + void set_target(const mjString& value) { + if (ptr_ && ptr_->target) { + *(ptr_->target) = value; + } + } + mjString refsite() const { + return (ptr_ && ptr_->refsite) ? *(ptr_->refsite) : ""; + } + void set_refsite(const mjString& value) { + if (ptr_ && ptr_->refsite) { + *(ptr_->refsite) = value; + } + } + mjString slidersite() const { + return (ptr_ && ptr_->slidersite) ? *(ptr_->slidersite) : ""; + } + void set_slidersite(const mjString& value) { + if (ptr_ && ptr_->slidersite) { + *(ptr_->slidersite) = value; + } + } + double cranklength() const { + return ptr_->cranklength; + } + void set_cranklength(double value) { + ptr_->cranklength = value; + } + emscripten::val lengthrange() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->lengthrange)); + } + double inheritrange() const { + return ptr_->inheritrange; + } + void set_inheritrange(double value) { + ptr_->inheritrange = value; + } + int ctrllimited() const { + return ptr_->ctrllimited; + } + void set_ctrllimited(int value) { + ptr_->ctrllimited = value; + } + emscripten::val ctrlrange() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->ctrlrange)); + } + int forcelimited() const { + return ptr_->forcelimited; + } + void set_forcelimited(int value) { + ptr_->forcelimited = value; + } + emscripten::val forcerange() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->forcerange)); + } + int actlimited() const { + return ptr_->actlimited; + } + void set_actlimited(int value) { + ptr_->actlimited = value; + } + emscripten::val actrange() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->actrange)); + } + int group() const { + return ptr_->group; + } + void set_group(int value) { + ptr_->group = value; + } + mjDoubleVec &userdata() const { + return *(ptr_->userdata); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsActuator* get() const { return ptr_; } + void set(mjsActuator* ptr) { ptr_ = ptr; } + + private: + mjsActuator* ptr_; + bool owned_ = false; + + public: + MjsElement element; + MjsPlugin plugin; +}; + +struct MjsBody { + explicit MjsBody(mjsBody *ptr); + ~MjsBody(); + mjString childclass() const { + return (ptr_ && ptr_->childclass) ? *(ptr_->childclass) : ""; + } + void set_childclass(const mjString& value) { + if (ptr_ && ptr_->childclass) { + *(ptr_->childclass) = value; + } + } + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val quat() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->quat)); + } + double mass() const { + return ptr_->mass; + } + void set_mass(double value) { + ptr_->mass = value; + } + emscripten::val ipos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->ipos)); + } + emscripten::val iquat() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->iquat)); + } + emscripten::val inertia() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->inertia)); + } + emscripten::val fullinertia() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->fullinertia)); + } + mjtByte mocap() const { + return ptr_->mocap; + } + void set_mocap(mjtByte value) { + ptr_->mocap = value; + } + double gravcomp() const { + return ptr_->gravcomp; + } + void set_gravcomp(double value) { + ptr_->gravcomp = value; + } + mjDoubleVec &userdata() const { + return *(ptr_->userdata); + } + mjtByte explicitinertial() const { + return ptr_->explicitinertial; + } + void set_explicitinertial(mjtByte value) { + ptr_->explicitinertial = value; + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsBody* get() const { return ptr_; } + void set(mjsBody* ptr) { ptr_ = ptr; } + + private: + mjsBody* ptr_; + bool owned_ = false; + + public: + MjsElement element; + MjsOrientation alt; + MjsOrientation ialt; + MjsPlugin plugin; +}; + +struct MjsGeom { + explicit MjsGeom(mjsGeom *ptr); + ~MjsGeom(); + mjtGeom type() const { + return ptr_->type; + } + void set_type(mjtGeom value) { + ptr_->type = value; + } + emscripten::val pos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->pos)); + } + emscripten::val quat() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->quat)); + } + emscripten::val fromto() const { + return emscripten::val(emscripten::typed_memory_view(6, ptr_->fromto)); + } + emscripten::val size() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->size)); + } + int contype() const { + return ptr_->contype; + } + void set_contype(int value) { + ptr_->contype = value; + } + int conaffinity() const { + return ptr_->conaffinity; + } + void set_conaffinity(int value) { + ptr_->conaffinity = value; + } + int condim() const { + return ptr_->condim; + } + void set_condim(int value) { + ptr_->condim = value; + } + int priority() const { + return ptr_->priority; + } + void set_priority(int value) { + ptr_->priority = value; + } + emscripten::val friction() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->friction)); + } + double solmix() const { + return ptr_->solmix; + } + void set_solmix(double value) { + ptr_->solmix = value; + } + emscripten::val solref() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solref)); + } + emscripten::val solimp() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->solimp)); + } + double margin() const { + return ptr_->margin; + } + void set_margin(double value) { + ptr_->margin = value; + } + double gap() const { + return ptr_->gap; + } + void set_gap(double value) { + ptr_->gap = value; + } + double mass() const { + return ptr_->mass; + } + void set_mass(double value) { + ptr_->mass = value; + } + double density() const { + return ptr_->density; + } + void set_density(double value) { + ptr_->density = value; + } + mjtGeomInertia typeinertia() const { + return ptr_->typeinertia; + } + void set_typeinertia(mjtGeomInertia value) { + ptr_->typeinertia = value; + } + mjtNum fluid_ellipsoid() const { + return ptr_->fluid_ellipsoid; + } + void set_fluid_ellipsoid(mjtNum value) { + ptr_->fluid_ellipsoid = value; + } + emscripten::val fluid_coefs() const { + return emscripten::val(emscripten::typed_memory_view(5, ptr_->fluid_coefs)); + } + mjString material() const { + return (ptr_ && ptr_->material) ? *(ptr_->material) : ""; + } + void set_material(const mjString& value) { + if (ptr_ && ptr_->material) { + *(ptr_->material) = value; + } + } + emscripten::val rgba() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->rgba)); + } + int group() const { + return ptr_->group; + } + void set_group(int value) { + ptr_->group = value; + } + mjString hfieldname() const { + return (ptr_ && ptr_->hfieldname) ? *(ptr_->hfieldname) : ""; + } + void set_hfieldname(const mjString& value) { + if (ptr_ && ptr_->hfieldname) { + *(ptr_->hfieldname) = value; + } + } + mjString meshname() const { + return (ptr_ && ptr_->meshname) ? *(ptr_->meshname) : ""; + } + void set_meshname(const mjString& value) { + if (ptr_ && ptr_->meshname) { + *(ptr_->meshname) = value; + } + } + double fitscale() const { + return ptr_->fitscale; + } + void set_fitscale(double value) { + ptr_->fitscale = value; + } + mjDoubleVec &userdata() const { + return *(ptr_->userdata); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsGeom* get() const { return ptr_; } + void set(mjsGeom* ptr) { ptr_ = ptr; } + + private: + mjsGeom* ptr_; + bool owned_ = false; + + public: + MjsElement element; + MjsOrientation alt; + MjsPlugin plugin; +}; + +struct MjsMesh { + explicit MjsMesh(mjsMesh *ptr); + ~MjsMesh(); + mjString content_type() const { + return (ptr_ && ptr_->content_type) ? *(ptr_->content_type) : ""; + } + void set_content_type(const mjString& value) { + if (ptr_ && ptr_->content_type) { + *(ptr_->content_type) = value; + } + } + mjString file() const { + return (ptr_ && ptr_->file) ? *(ptr_->file) : ""; + } + void set_file(const mjString& value) { + if (ptr_ && ptr_->file) { + *(ptr_->file) = value; + } + } + emscripten::val refpos() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->refpos)); + } + emscripten::val refquat() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->refquat)); + } + emscripten::val scale() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->scale)); + } + mjtMeshInertia inertia() const { + return ptr_->inertia; + } + void set_inertia(mjtMeshInertia value) { + ptr_->inertia = value; + } + mjtByte smoothnormal() const { + return ptr_->smoothnormal; + } + void set_smoothnormal(mjtByte value) { + ptr_->smoothnormal = value; + } + mjtByte needsdf() const { + return ptr_->needsdf; + } + void set_needsdf(mjtByte value) { + ptr_->needsdf = value; + } + int maxhullvert() const { + return ptr_->maxhullvert; + } + void set_maxhullvert(int value) { + ptr_->maxhullvert = value; + } + mjFloatVec &uservert() const { + return *(ptr_->uservert); + } + mjFloatVec &usernormal() const { + return *(ptr_->usernormal); + } + mjFloatVec &usertexcoord() const { + return *(ptr_->usertexcoord); + } + mjIntVec &userface() const { + return *(ptr_->userface); + } + mjIntVec &userfacenormal() const { + return *(ptr_->userfacenormal); + } + mjIntVec &userfacetexcoord() const { + return *(ptr_->userfacetexcoord); + } + mjString material() const { + return (ptr_ && ptr_->material) ? *(ptr_->material) : ""; + } + void set_material(const mjString& value) { + if (ptr_ && ptr_->material) { + *(ptr_->material) = value; + } + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsMesh* get() const { return ptr_; } + void set(mjsMesh* ptr) { ptr_ = ptr; } + + private: + mjsMesh* ptr_; + bool owned_ = false; + + public: + MjsElement element; + MjsPlugin plugin; +}; + +struct MjsSensor { + explicit MjsSensor(mjsSensor *ptr); + ~MjsSensor(); + mjtSensor type() const { + return ptr_->type; + } + void set_type(mjtSensor value) { + ptr_->type = value; + } + mjtObj objtype() const { + return ptr_->objtype; + } + void set_objtype(mjtObj value) { + ptr_->objtype = value; + } + mjString objname() const { + return (ptr_ && ptr_->objname) ? *(ptr_->objname) : ""; + } + void set_objname(const mjString& value) { + if (ptr_ && ptr_->objname) { + *(ptr_->objname) = value; + } + } + mjtObj reftype() const { + return ptr_->reftype; + } + void set_reftype(mjtObj value) { + ptr_->reftype = value; + } + mjString refname() const { + return (ptr_ && ptr_->refname) ? *(ptr_->refname) : ""; + } + void set_refname(const mjString& value) { + if (ptr_ && ptr_->refname) { + *(ptr_->refname) = value; + } + } + emscripten::val intprm() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->intprm)); + } + mjtDataType datatype() const { + return ptr_->datatype; + } + void set_datatype(mjtDataType value) { + ptr_->datatype = value; + } + mjtStage needstage() const { + return ptr_->needstage; + } + void set_needstage(mjtStage value) { + ptr_->needstage = value; + } + int dim() const { + return ptr_->dim; + } + void set_dim(int value) { + ptr_->dim = value; + } + double cutoff() const { + return ptr_->cutoff; + } + void set_cutoff(double value) { + ptr_->cutoff = value; + } + double noise() const { + return ptr_->noise; + } + void set_noise(double value) { + ptr_->noise = value; + } + mjDoubleVec &userdata() const { + return *(ptr_->userdata); + } + mjString info() const { + return (ptr_ && ptr_->info) ? *(ptr_->info) : ""; + } + void set_info(const mjString& value) { + if (ptr_ && ptr_->info) { + *(ptr_->info) = value; + } + } + mjsSensor* get() const { return ptr_; } + void set(mjsSensor* ptr) { ptr_ = ptr; } + + private: + mjsSensor* ptr_; + bool owned_ = false; + + public: + MjsElement element; + MjsPlugin plugin; +}; + +struct MjsDefault { + explicit MjsDefault(mjsDefault *ptr); + ~MjsDefault(); + mjsDefault* get() const { return ptr_; } + void set(mjsDefault* ptr) { ptr_ = ptr; } + + private: + mjsDefault* ptr_; + bool owned_ = false; + + public: + MjsElement element; + MjsJoint joint; + MjsGeom geom; + MjsSite site; + MjsCamera camera; + MjsLight light; + MjsFlex flex; + MjsMesh mesh; + MjsMaterial material; + MjsPair pair; + MjsEquality equality; + MjsTendon tendon; + MjsActuator actuator; +}; + +struct MjVisualGlobal { + MjVisualGlobal(); + explicit MjVisualGlobal(mjVisualGlobal *ptr); + MjVisualGlobal(const MjVisualGlobal &); + MjVisualGlobal &operator=(const MjVisualGlobal &); + ~MjVisualGlobal(); + std::unique_ptr copy(); + int cameraid() const { + return ptr_->cameraid; + } + void set_cameraid(int value) { + ptr_->cameraid = value; + } + int orthographic() const { + return ptr_->orthographic; + } + void set_orthographic(int value) { + ptr_->orthographic = value; + } + float fovy() const { + return ptr_->fovy; + } + void set_fovy(float value) { + ptr_->fovy = value; + } + float ipd() const { + return ptr_->ipd; + } + void set_ipd(float value) { + ptr_->ipd = value; + } + float azimuth() const { + return ptr_->azimuth; + } + void set_azimuth(float value) { + ptr_->azimuth = value; + } + float elevation() const { + return ptr_->elevation; + } + void set_elevation(float value) { + ptr_->elevation = value; + } + float linewidth() const { + return ptr_->linewidth; + } + void set_linewidth(float value) { + ptr_->linewidth = value; + } + float glow() const { + return ptr_->glow; + } + void set_glow(float value) { + ptr_->glow = value; + } + float realtime() const { + return ptr_->realtime; + } + void set_realtime(float value) { + ptr_->realtime = value; + } + int offwidth() const { + return ptr_->offwidth; + } + void set_offwidth(int value) { + ptr_->offwidth = value; + } + int offheight() const { + return ptr_->offheight; + } + void set_offheight(int value) { + ptr_->offheight = value; + } + int ellipsoidinertia() const { + return ptr_->ellipsoidinertia; + } + void set_ellipsoidinertia(int value) { + ptr_->ellipsoidinertia = value; + } + int bvactive() const { + return ptr_->bvactive; + } + void set_bvactive(int value) { + ptr_->bvactive = value; + } + mjVisualGlobal* get() const { return ptr_; } + void set(mjVisualGlobal* ptr) { ptr_ = ptr; } + + private: + mjVisualGlobal* ptr_; + bool owned_ = false; +}; + +struct MjVisualQuality { + MjVisualQuality(); + explicit MjVisualQuality(mjVisualQuality *ptr); + MjVisualQuality(const MjVisualQuality &); + MjVisualQuality &operator=(const MjVisualQuality &); + ~MjVisualQuality(); + std::unique_ptr copy(); + int shadowsize() const { + return ptr_->shadowsize; + } + void set_shadowsize(int value) { + ptr_->shadowsize = value; + } + int offsamples() const { + return ptr_->offsamples; + } + void set_offsamples(int value) { + ptr_->offsamples = value; + } + int numslices() const { + return ptr_->numslices; + } + void set_numslices(int value) { + ptr_->numslices = value; + } + int numstacks() const { + return ptr_->numstacks; + } + void set_numstacks(int value) { + ptr_->numstacks = value; + } + int numquads() const { + return ptr_->numquads; + } + void set_numquads(int value) { + ptr_->numquads = value; + } + mjVisualQuality* get() const { return ptr_; } + void set(mjVisualQuality* ptr) { ptr_ = ptr; } + + private: + mjVisualQuality* ptr_; + bool owned_ = false; +}; + +struct MjVisualHeadlight { + MjVisualHeadlight(); + explicit MjVisualHeadlight(mjVisualHeadlight *ptr); + MjVisualHeadlight(const MjVisualHeadlight &); + MjVisualHeadlight &operator=(const MjVisualHeadlight &); + ~MjVisualHeadlight(); + std::unique_ptr copy(); + emscripten::val ambient() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->ambient)); + } + emscripten::val diffuse() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->diffuse)); + } + emscripten::val specular() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->specular)); + } + int active() const { + return ptr_->active; + } + void set_active(int value) { + ptr_->active = value; + } + mjVisualHeadlight* get() const { return ptr_; } + void set(mjVisualHeadlight* ptr) { ptr_ = ptr; } + + private: + mjVisualHeadlight* ptr_; + bool owned_ = false; +}; + +struct MjVisualMap { + MjVisualMap(); + explicit MjVisualMap(mjVisualMap *ptr); + MjVisualMap(const MjVisualMap &); + MjVisualMap &operator=(const MjVisualMap &); + ~MjVisualMap(); + std::unique_ptr copy(); + float stiffness() const { + return ptr_->stiffness; + } + void set_stiffness(float value) { + ptr_->stiffness = value; + } + float stiffnessrot() const { + return ptr_->stiffnessrot; + } + void set_stiffnessrot(float value) { + ptr_->stiffnessrot = value; + } + float force() const { + return ptr_->force; + } + void set_force(float value) { + ptr_->force = value; + } + float torque() const { + return ptr_->torque; + } + void set_torque(float value) { + ptr_->torque = value; + } + float alpha() const { + return ptr_->alpha; + } + void set_alpha(float value) { + ptr_->alpha = value; + } + float fogstart() const { + return ptr_->fogstart; + } + void set_fogstart(float value) { + ptr_->fogstart = value; + } + float fogend() const { + return ptr_->fogend; + } + void set_fogend(float value) { + ptr_->fogend = value; + } + float znear() const { + return ptr_->znear; + } + void set_znear(float value) { + ptr_->znear = value; + } + float zfar() const { + return ptr_->zfar; + } + void set_zfar(float value) { + ptr_->zfar = value; + } + float haze() const { + return ptr_->haze; + } + void set_haze(float value) { + ptr_->haze = value; + } + float shadowclip() const { + return ptr_->shadowclip; + } + void set_shadowclip(float value) { + ptr_->shadowclip = value; + } + float shadowscale() const { + return ptr_->shadowscale; + } + void set_shadowscale(float value) { + ptr_->shadowscale = value; + } + float actuatortendon() const { + return ptr_->actuatortendon; + } + void set_actuatortendon(float value) { + ptr_->actuatortendon = value; + } + mjVisualMap* get() const { return ptr_; } + void set(mjVisualMap* ptr) { ptr_ = ptr; } + + private: + mjVisualMap* ptr_; + bool owned_ = false; +}; + +struct MjVisualScale { + MjVisualScale(); + explicit MjVisualScale(mjVisualScale *ptr); + MjVisualScale(const MjVisualScale &); + MjVisualScale &operator=(const MjVisualScale &); + ~MjVisualScale(); + std::unique_ptr copy(); + float forcewidth() const { + return ptr_->forcewidth; + } + void set_forcewidth(float value) { + ptr_->forcewidth = value; + } + float contactwidth() const { + return ptr_->contactwidth; + } + void set_contactwidth(float value) { + ptr_->contactwidth = value; + } + float contactheight() const { + return ptr_->contactheight; + } + void set_contactheight(float value) { + ptr_->contactheight = value; + } + float connect() const { + return ptr_->connect; + } + void set_connect(float value) { + ptr_->connect = value; + } + float com() const { + return ptr_->com; + } + void set_com(float value) { + ptr_->com = value; + } + float camera() const { + return ptr_->camera; + } + void set_camera(float value) { + ptr_->camera = value; + } + float light() const { + return ptr_->light; + } + void set_light(float value) { + ptr_->light = value; + } + float selectpoint() const { + return ptr_->selectpoint; + } + void set_selectpoint(float value) { + ptr_->selectpoint = value; + } + float jointlength() const { + return ptr_->jointlength; + } + void set_jointlength(float value) { + ptr_->jointlength = value; + } + float jointwidth() const { + return ptr_->jointwidth; + } + void set_jointwidth(float value) { + ptr_->jointwidth = value; + } + float actuatorlength() const { + return ptr_->actuatorlength; + } + void set_actuatorlength(float value) { + ptr_->actuatorlength = value; + } + float actuatorwidth() const { + return ptr_->actuatorwidth; + } + void set_actuatorwidth(float value) { + ptr_->actuatorwidth = value; + } + float framelength() const { + return ptr_->framelength; + } + void set_framelength(float value) { + ptr_->framelength = value; + } + float framewidth() const { + return ptr_->framewidth; + } + void set_framewidth(float value) { + ptr_->framewidth = value; + } + float constraint() const { + return ptr_->constraint; + } + void set_constraint(float value) { + ptr_->constraint = value; + } + float slidercrank() const { + return ptr_->slidercrank; + } + void set_slidercrank(float value) { + ptr_->slidercrank = value; + } + float frustum() const { + return ptr_->frustum; + } + void set_frustum(float value) { + ptr_->frustum = value; + } + mjVisualScale* get() const { return ptr_; } + void set(mjVisualScale* ptr) { ptr_ = ptr; } + + private: + mjVisualScale* ptr_; + bool owned_ = false; +}; + +struct MjVisualRgba { + MjVisualRgba(); + explicit MjVisualRgba(mjVisualRgba *ptr); + MjVisualRgba(const MjVisualRgba &); + MjVisualRgba &operator=(const MjVisualRgba &); + ~MjVisualRgba(); + std::unique_ptr copy(); + emscripten::val fog() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->fog)); + } + emscripten::val haze() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->haze)); + } + emscripten::val force() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->force)); + } + emscripten::val inertia() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->inertia)); + } + emscripten::val joint() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->joint)); + } + emscripten::val actuator() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->actuator)); + } + emscripten::val actuatornegative() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->actuatornegative)); + } + emscripten::val actuatorpositive() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->actuatorpositive)); + } + emscripten::val com() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->com)); + } + emscripten::val camera() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->camera)); + } + emscripten::val light() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->light)); + } + emscripten::val selectpoint() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->selectpoint)); + } + emscripten::val connect() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->connect)); + } + emscripten::val contactpoint() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->contactpoint)); + } + emscripten::val contactforce() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->contactforce)); + } + emscripten::val contactfriction() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->contactfriction)); + } + emscripten::val contacttorque() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->contacttorque)); + } + emscripten::val contactgap() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->contactgap)); + } + emscripten::val rangefinder() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->rangefinder)); + } + emscripten::val constraint() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->constraint)); + } + emscripten::val slidercrank() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->slidercrank)); + } + emscripten::val crankbroken() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->crankbroken)); + } + emscripten::val frustum() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->frustum)); + } + emscripten::val bv() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->bv)); + } + emscripten::val bvactive() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->bvactive)); + } + mjVisualRgba* get() const { return ptr_; } + void set(mjVisualRgba* ptr) { ptr_ = ptr; } + + private: + mjVisualRgba* ptr_; + bool owned_ = false; +}; + +struct MjVisual { + MjVisual(); + explicit MjVisual(mjVisual *ptr_); + MjVisual(const MjVisual &); + MjVisual &operator=(const MjVisual &); + ~MjVisual(); + std::unique_ptr copy(); + mjVisual* get() const { return ptr_; } + void set(mjVisual* ptr) { ptr_ = ptr; } + + private: + mjVisual* ptr_; + bool owned_ = false; + + public: + MjVisualGlobal global; + MjVisualQuality quality; + MjVisualHeadlight headlight; + MjVisualMap map; + MjVisualScale scale; + MjVisualRgba rgba; +}; + +struct MjModel { + explicit MjModel(mjModel *m); + explicit MjModel(const MjModel &other); + ~MjModel(); + std::unique_ptr copy(); + int nq() const { + return ptr_->nq; + } + void set_nq(int value) { + ptr_->nq = value; + } + int nv() const { + return ptr_->nv; + } + void set_nv(int value) { + ptr_->nv = value; + } + int nu() const { + return ptr_->nu; + } + void set_nu(int value) { + ptr_->nu = value; + } + int na() const { + return ptr_->na; + } + void set_na(int value) { + ptr_->na = value; + } + int nbody() const { + return ptr_->nbody; + } + void set_nbody(int value) { + ptr_->nbody = value; + } + int nbvh() const { + return ptr_->nbvh; + } + void set_nbvh(int value) { + ptr_->nbvh = value; + } + int nbvhstatic() const { + return ptr_->nbvhstatic; + } + void set_nbvhstatic(int value) { + ptr_->nbvhstatic = value; + } + int nbvhdynamic() const { + return ptr_->nbvhdynamic; + } + void set_nbvhdynamic(int value) { + ptr_->nbvhdynamic = value; + } + int noct() const { + return ptr_->noct; + } + void set_noct(int value) { + ptr_->noct = value; + } + int njnt() const { + return ptr_->njnt; + } + void set_njnt(int value) { + ptr_->njnt = value; + } + int ntree() const { + return ptr_->ntree; + } + void set_ntree(int value) { + ptr_->ntree = value; + } + int nM() const { + return ptr_->nM; + } + void set_nM(int value) { + ptr_->nM = value; + } + int nB() const { + return ptr_->nB; + } + void set_nB(int value) { + ptr_->nB = value; + } + int nC() const { + return ptr_->nC; + } + void set_nC(int value) { + ptr_->nC = value; + } + int nD() const { + return ptr_->nD; + } + void set_nD(int value) { + ptr_->nD = value; + } + int ngeom() const { + return ptr_->ngeom; + } + void set_ngeom(int value) { + ptr_->ngeom = value; + } + int nsite() const { + return ptr_->nsite; + } + void set_nsite(int value) { + ptr_->nsite = value; + } + int ncam() const { + return ptr_->ncam; + } + void set_ncam(int value) { + ptr_->ncam = value; + } + int nlight() const { + return ptr_->nlight; + } + void set_nlight(int value) { + ptr_->nlight = value; + } + int nflex() const { + return ptr_->nflex; + } + void set_nflex(int value) { + ptr_->nflex = value; + } + int nflexnode() const { + return ptr_->nflexnode; + } + void set_nflexnode(int value) { + ptr_->nflexnode = value; + } + int nflexvert() const { + return ptr_->nflexvert; + } + void set_nflexvert(int value) { + ptr_->nflexvert = value; + } + int nflexedge() const { + return ptr_->nflexedge; + } + void set_nflexedge(int value) { + ptr_->nflexedge = value; + } + int nflexelem() const { + return ptr_->nflexelem; + } + void set_nflexelem(int value) { + ptr_->nflexelem = value; + } + int nflexelemdata() const { + return ptr_->nflexelemdata; + } + void set_nflexelemdata(int value) { + ptr_->nflexelemdata = value; + } + int nflexelemedge() const { + return ptr_->nflexelemedge; + } + void set_nflexelemedge(int value) { + ptr_->nflexelemedge = value; + } + int nflexshelldata() const { + return ptr_->nflexshelldata; + } + void set_nflexshelldata(int value) { + ptr_->nflexshelldata = value; + } + int nflexevpair() const { + return ptr_->nflexevpair; + } + void set_nflexevpair(int value) { + ptr_->nflexevpair = value; + } + int nflextexcoord() const { + return ptr_->nflextexcoord; + } + void set_nflextexcoord(int value) { + ptr_->nflextexcoord = value; + } + int nmesh() const { + return ptr_->nmesh; + } + void set_nmesh(int value) { + ptr_->nmesh = value; + } + int nmeshvert() const { + return ptr_->nmeshvert; + } + void set_nmeshvert(int value) { + ptr_->nmeshvert = value; + } + int nmeshnormal() const { + return ptr_->nmeshnormal; + } + void set_nmeshnormal(int value) { + ptr_->nmeshnormal = value; + } + int nmeshtexcoord() const { + return ptr_->nmeshtexcoord; + } + void set_nmeshtexcoord(int value) { + ptr_->nmeshtexcoord = value; + } + int nmeshface() const { + return ptr_->nmeshface; + } + void set_nmeshface(int value) { + ptr_->nmeshface = value; + } + int nmeshgraph() const { + return ptr_->nmeshgraph; + } + void set_nmeshgraph(int value) { + ptr_->nmeshgraph = value; + } + int nmeshpoly() const { + return ptr_->nmeshpoly; + } + void set_nmeshpoly(int value) { + ptr_->nmeshpoly = value; + } + int nmeshpolyvert() const { + return ptr_->nmeshpolyvert; + } + void set_nmeshpolyvert(int value) { + ptr_->nmeshpolyvert = value; + } + int nmeshpolymap() const { + return ptr_->nmeshpolymap; + } + void set_nmeshpolymap(int value) { + ptr_->nmeshpolymap = value; + } + int nskin() const { + return ptr_->nskin; + } + void set_nskin(int value) { + ptr_->nskin = value; + } + int nskinvert() const { + return ptr_->nskinvert; + } + void set_nskinvert(int value) { + ptr_->nskinvert = value; + } + int nskintexvert() const { + return ptr_->nskintexvert; + } + void set_nskintexvert(int value) { + ptr_->nskintexvert = value; + } + int nskinface() const { + return ptr_->nskinface; + } + void set_nskinface(int value) { + ptr_->nskinface = value; + } + int nskinbone() const { + return ptr_->nskinbone; + } + void set_nskinbone(int value) { + ptr_->nskinbone = value; + } + int nskinbonevert() const { + return ptr_->nskinbonevert; + } + void set_nskinbonevert(int value) { + ptr_->nskinbonevert = value; + } + int nhfield() const { + return ptr_->nhfield; + } + void set_nhfield(int value) { + ptr_->nhfield = value; + } + int nhfielddata() const { + return ptr_->nhfielddata; + } + void set_nhfielddata(int value) { + ptr_->nhfielddata = value; + } + int ntex() const { + return ptr_->ntex; + } + void set_ntex(int value) { + ptr_->ntex = value; + } + int ntexdata() const { + return ptr_->ntexdata; + } + void set_ntexdata(int value) { + ptr_->ntexdata = value; + } + int nmat() const { + return ptr_->nmat; + } + void set_nmat(int value) { + ptr_->nmat = value; + } + int npair() const { + return ptr_->npair; + } + void set_npair(int value) { + ptr_->npair = value; + } + int nexclude() const { + return ptr_->nexclude; + } + void set_nexclude(int value) { + ptr_->nexclude = value; + } + int neq() const { + return ptr_->neq; + } + void set_neq(int value) { + ptr_->neq = value; + } + int ntendon() const { + return ptr_->ntendon; + } + void set_ntendon(int value) { + ptr_->ntendon = value; + } + int nwrap() const { + return ptr_->nwrap; + } + void set_nwrap(int value) { + ptr_->nwrap = value; + } + int nsensor() const { + return ptr_->nsensor; + } + void set_nsensor(int value) { + ptr_->nsensor = value; + } + int nnumeric() const { + return ptr_->nnumeric; + } + void set_nnumeric(int value) { + ptr_->nnumeric = value; + } + int nnumericdata() const { + return ptr_->nnumericdata; + } + void set_nnumericdata(int value) { + ptr_->nnumericdata = value; + } + int ntext() const { + return ptr_->ntext; + } + void set_ntext(int value) { + ptr_->ntext = value; + } + int ntextdata() const { + return ptr_->ntextdata; + } + void set_ntextdata(int value) { + ptr_->ntextdata = value; + } + int ntuple() const { + return ptr_->ntuple; + } + void set_ntuple(int value) { + ptr_->ntuple = value; + } + int ntupledata() const { + return ptr_->ntupledata; + } + void set_ntupledata(int value) { + ptr_->ntupledata = value; + } + int nkey() const { + return ptr_->nkey; + } + void set_nkey(int value) { + ptr_->nkey = value; + } + int nmocap() const { + return ptr_->nmocap; + } + void set_nmocap(int value) { + ptr_->nmocap = value; + } + int nplugin() const { + return ptr_->nplugin; + } + void set_nplugin(int value) { + ptr_->nplugin = value; + } + int npluginattr() const { + return ptr_->npluginattr; + } + void set_npluginattr(int value) { + ptr_->npluginattr = value; + } + int nuser_body() const { + return ptr_->nuser_body; + } + void set_nuser_body(int value) { + ptr_->nuser_body = value; + } + int nuser_jnt() const { + return ptr_->nuser_jnt; + } + void set_nuser_jnt(int value) { + ptr_->nuser_jnt = value; + } + int nuser_geom() const { + return ptr_->nuser_geom; + } + void set_nuser_geom(int value) { + ptr_->nuser_geom = value; + } + int nuser_site() const { + return ptr_->nuser_site; + } + void set_nuser_site(int value) { + ptr_->nuser_site = value; + } + int nuser_cam() const { + return ptr_->nuser_cam; + } + void set_nuser_cam(int value) { + ptr_->nuser_cam = value; + } + int nuser_tendon() const { + return ptr_->nuser_tendon; + } + void set_nuser_tendon(int value) { + ptr_->nuser_tendon = value; + } + int nuser_actuator() const { + return ptr_->nuser_actuator; + } + void set_nuser_actuator(int value) { + ptr_->nuser_actuator = value; + } + int nuser_sensor() const { + return ptr_->nuser_sensor; + } + void set_nuser_sensor(int value) { + ptr_->nuser_sensor = value; + } + int nnames() const { + return ptr_->nnames; + } + void set_nnames(int value) { + ptr_->nnames = value; + } + int npaths() const { + return ptr_->npaths; + } + void set_npaths(int value) { + ptr_->npaths = value; + } + int nnames_map() const { + return ptr_->nnames_map; + } + void set_nnames_map(int value) { + ptr_->nnames_map = value; + } + int nJmom() const { + return ptr_->nJmom; + } + void set_nJmom(int value) { + ptr_->nJmom = value; + } + int ngravcomp() const { + return ptr_->ngravcomp; + } + void set_ngravcomp(int value) { + ptr_->ngravcomp = value; + } + int nemax() const { + return ptr_->nemax; + } + void set_nemax(int value) { + ptr_->nemax = value; + } + int njmax() const { + return ptr_->njmax; + } + void set_njmax(int value) { + ptr_->njmax = value; + } + int nconmax() const { + return ptr_->nconmax; + } + void set_nconmax(int value) { + ptr_->nconmax = value; + } + int nuserdata() const { + return ptr_->nuserdata; + } + void set_nuserdata(int value) { + ptr_->nuserdata = value; + } + int nsensordata() const { + return ptr_->nsensordata; + } + void set_nsensordata(int value) { + ptr_->nsensordata = value; + } + int npluginstate() const { + return ptr_->npluginstate; + } + void set_npluginstate(int value) { + ptr_->npluginstate = value; + } + mjtSize narena() const { + return ptr_->narena; + } + void set_narena(mjtSize value) { + ptr_->narena = value; + } + mjtSize nbuffer() const { + return ptr_->nbuffer; + } + void set_nbuffer(mjtSize value) { + ptr_->nbuffer = value; + } + emscripten::val buffer() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbuffer, static_cast(ptr_->buffer))); + } + emscripten::val qpos0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nq, ptr_->qpos0)); + } + emscripten::val qpos_spring() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nq, ptr_->qpos_spring)); + } + emscripten::val body_parentid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_parentid)); + } + emscripten::val body_rootid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_rootid)); + } + emscripten::val body_weldid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_weldid)); + } + emscripten::val body_mocapid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_mocapid)); + } + emscripten::val body_jntnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_jntnum)); + } + emscripten::val body_jntadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_jntadr)); + } + emscripten::val body_dofnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_dofnum)); + } + emscripten::val body_dofadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_dofadr)); + } + emscripten::val body_treeid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_treeid)); + } + emscripten::val body_geomnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_geomnum)); + } + emscripten::val body_geomadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_geomadr)); + } + emscripten::val body_simple() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_simple)); + } + emscripten::val body_sameframe() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_sameframe)); + } + emscripten::val body_pos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody * 3, ptr_->body_pos)); + } + emscripten::val body_quat() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody * 4, ptr_->body_quat)); + } + emscripten::val body_ipos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody * 3, ptr_->body_ipos)); + } + emscripten::val body_iquat() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody * 4, ptr_->body_iquat)); + } + emscripten::val body_mass() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_mass)); + } + emscripten::val body_subtreemass() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_subtreemass)); + } + emscripten::val body_inertia() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody * 3, ptr_->body_inertia)); + } + emscripten::val body_invweight0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody * 2, ptr_->body_invweight0)); + } + emscripten::val body_gravcomp() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_gravcomp)); + } + emscripten::val body_margin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_margin)); + } + emscripten::val body_user() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody * ptr_->nuser_body, ptr_->body_user)); + } + emscripten::val body_plugin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_plugin)); + } + emscripten::val body_contype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_contype)); + } + emscripten::val body_conaffinity() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_conaffinity)); + } + emscripten::val body_bvhadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_bvhadr)); + } + emscripten::val body_bvhnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->body_bvhnum)); + } + emscripten::val bvh_depth() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbvh, ptr_->bvh_depth)); + } + emscripten::val bvh_child() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbvh * 2, ptr_->bvh_child)); + } + emscripten::val bvh_nodeid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbvh, ptr_->bvh_nodeid)); + } + emscripten::val bvh_aabb() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbvhstatic * 6, ptr_->bvh_aabb)); + } + emscripten::val oct_depth() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->noct, ptr_->oct_depth)); + } + emscripten::val oct_child() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->noct * 8, ptr_->oct_child)); + } + emscripten::val oct_aabb() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->noct * 6, ptr_->oct_aabb)); + } + emscripten::val oct_coeff() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->noct * 8, ptr_->oct_coeff)); + } + emscripten::val jnt_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_type)); + } + emscripten::val jnt_qposadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_qposadr)); + } + emscripten::val jnt_dofadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_dofadr)); + } + emscripten::val jnt_bodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_bodyid)); + } + emscripten::val jnt_group() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_group)); + } + emscripten::val jnt_limited() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_limited)); + } + emscripten::val jnt_actfrclimited() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_actfrclimited)); + } + emscripten::val jnt_actgravcomp() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_actgravcomp)); + } + emscripten::val jnt_solref() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt * mjNREF, ptr_->jnt_solref)); + } + emscripten::val jnt_solimp() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt * mjNIMP, ptr_->jnt_solimp)); + } + emscripten::val jnt_pos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt * 3, ptr_->jnt_pos)); + } + emscripten::val jnt_axis() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt * 3, ptr_->jnt_axis)); + } + emscripten::val jnt_stiffness() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_stiffness)); + } + emscripten::val jnt_range() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt * 2, ptr_->jnt_range)); + } + emscripten::val jnt_actfrcrange() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt * 2, ptr_->jnt_actfrcrange)); + } + emscripten::val jnt_margin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->jnt_margin)); + } + emscripten::val jnt_user() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt * ptr_->nuser_jnt, ptr_->jnt_user)); + } + emscripten::val dof_bodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_bodyid)); + } + emscripten::val dof_jntid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_jntid)); + } + emscripten::val dof_parentid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_parentid)); + } + emscripten::val dof_treeid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_treeid)); + } + emscripten::val dof_Madr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_Madr)); + } + emscripten::val dof_simplenum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_simplenum)); + } + emscripten::val dof_solref() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv * mjNREF, ptr_->dof_solref)); + } + emscripten::val dof_solimp() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv * mjNIMP, ptr_->dof_solimp)); + } + emscripten::val dof_frictionloss() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_frictionloss)); + } + emscripten::val dof_armature() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_armature)); + } + emscripten::val dof_damping() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_damping)); + } + emscripten::val dof_invweight0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_invweight0)); + } + emscripten::val dof_M0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->dof_M0)); + } + emscripten::val geom_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_type)); + } + emscripten::val geom_contype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_contype)); + } + emscripten::val geom_conaffinity() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_conaffinity)); + } + emscripten::val geom_condim() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_condim)); + } + emscripten::val geom_bodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_bodyid)); + } + emscripten::val geom_dataid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_dataid)); + } + emscripten::val geom_matid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_matid)); + } + emscripten::val geom_group() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_group)); + } + emscripten::val geom_priority() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_priority)); + } + emscripten::val geom_plugin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_plugin)); + } + emscripten::val geom_sameframe() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_sameframe)); + } + emscripten::val geom_solmix() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_solmix)); + } + emscripten::val geom_solref() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * mjNREF, ptr_->geom_solref)); + } + emscripten::val geom_solimp() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * mjNIMP, ptr_->geom_solimp)); + } + emscripten::val geom_size() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 3, ptr_->geom_size)); + } + emscripten::val geom_aabb() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 6, ptr_->geom_aabb)); + } + emscripten::val geom_rbound() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_rbound)); + } + emscripten::val geom_pos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 3, ptr_->geom_pos)); + } + emscripten::val geom_quat() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 4, ptr_->geom_quat)); + } + emscripten::val geom_friction() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 3, ptr_->geom_friction)); + } + emscripten::val geom_margin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_margin)); + } + emscripten::val geom_gap() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->geom_gap)); + } + emscripten::val geom_fluid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * mjNFLUID, ptr_->geom_fluid)); + } + emscripten::val geom_user() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * ptr_->nuser_geom, ptr_->geom_user)); + } + emscripten::val geom_rgba() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 4, ptr_->geom_rgba)); + } + emscripten::val site_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite, ptr_->site_type)); + } + emscripten::val site_bodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite, ptr_->site_bodyid)); + } + emscripten::val site_matid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite, ptr_->site_matid)); + } + emscripten::val site_group() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite, ptr_->site_group)); + } + emscripten::val site_sameframe() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite, ptr_->site_sameframe)); + } + emscripten::val site_size() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite * 3, ptr_->site_size)); + } + emscripten::val site_pos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite * 3, ptr_->site_pos)); + } + emscripten::val site_quat() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite * 4, ptr_->site_quat)); + } + emscripten::val site_user() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite * ptr_->nuser_site, ptr_->site_user)); + } + emscripten::val site_rgba() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite * 4, ptr_->site_rgba)); + } + emscripten::val cam_mode() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_mode)); + } + emscripten::val cam_bodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_bodyid)); + } + emscripten::val cam_targetbodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_targetbodyid)); + } + emscripten::val cam_pos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 3, ptr_->cam_pos)); + } + emscripten::val cam_quat() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 4, ptr_->cam_quat)); + } + emscripten::val cam_poscom0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 3, ptr_->cam_poscom0)); + } + emscripten::val cam_pos0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 3, ptr_->cam_pos0)); + } + emscripten::val cam_mat0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 9, ptr_->cam_mat0)); + } + emscripten::val cam_orthographic() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_orthographic)); + } + emscripten::val cam_fovy() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_fovy)); + } + emscripten::val cam_ipd() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_ipd)); + } + emscripten::val cam_resolution() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 2, ptr_->cam_resolution)); + } + emscripten::val cam_sensorsize() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 2, ptr_->cam_sensorsize)); + } + emscripten::val cam_intrinsic() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 4, ptr_->cam_intrinsic)); + } + emscripten::val cam_user() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * ptr_->nuser_cam, ptr_->cam_user)); + } + emscripten::val light_mode() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_mode)); + } + emscripten::val light_bodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_bodyid)); + } + emscripten::val light_targetbodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_targetbodyid)); + } + emscripten::val light_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_type)); + } + emscripten::val light_texid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_texid)); + } + emscripten::val light_castshadow() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_castshadow)); + } + emscripten::val light_bulbradius() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_bulbradius)); + } + emscripten::val light_intensity() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_intensity)); + } + emscripten::val light_range() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_range)); + } + emscripten::val light_active() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_active)); + } + emscripten::val light_pos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight * 3, ptr_->light_pos)); + } + emscripten::val light_dir() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight * 3, ptr_->light_dir)); + } + emscripten::val light_poscom0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight * 3, ptr_->light_poscom0)); + } + emscripten::val light_pos0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight * 3, ptr_->light_pos0)); + } + emscripten::val light_dir0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight * 3, ptr_->light_dir0)); + } + emscripten::val light_attenuation() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight * 3, ptr_->light_attenuation)); + } + emscripten::val light_cutoff() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_cutoff)); + } + emscripten::val light_exponent() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_exponent)); + } + emscripten::val light_ambient() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight * 3, ptr_->light_ambient)); + } + emscripten::val light_diffuse() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight * 3, ptr_->light_diffuse)); + } + emscripten::val light_specular() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight * 3, ptr_->light_specular)); + } + emscripten::val flex_contype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_contype)); + } + emscripten::val flex_conaffinity() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_conaffinity)); + } + emscripten::val flex_condim() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_condim)); + } + emscripten::val flex_priority() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_priority)); + } + emscripten::val flex_solmix() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_solmix)); + } + emscripten::val flex_solref() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex * mjNREF, ptr_->flex_solref)); + } + emscripten::val flex_solimp() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex * mjNIMP, ptr_->flex_solimp)); + } + emscripten::val flex_friction() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex * 3, ptr_->flex_friction)); + } + emscripten::val flex_margin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_margin)); + } + emscripten::val flex_gap() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_gap)); + } + emscripten::val flex_internal() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_internal)); + } + emscripten::val flex_selfcollide() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_selfcollide)); + } + emscripten::val flex_activelayers() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_activelayers)); + } + emscripten::val flex_passive() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_passive)); + } + emscripten::val flex_dim() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_dim)); + } + emscripten::val flex_matid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_matid)); + } + emscripten::val flex_group() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_group)); + } + emscripten::val flex_interp() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_interp)); + } + emscripten::val flex_nodeadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_nodeadr)); + } + emscripten::val flex_nodenum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_nodenum)); + } + emscripten::val flex_vertadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_vertadr)); + } + emscripten::val flex_vertnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_vertnum)); + } + emscripten::val flex_edgeadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_edgeadr)); + } + emscripten::val flex_edgenum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_edgenum)); + } + emscripten::val flex_elemadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_elemadr)); + } + emscripten::val flex_elemnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_elemnum)); + } + emscripten::val flex_elemdataadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_elemdataadr)); + } + emscripten::val flex_elemedgeadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_elemedgeadr)); + } + emscripten::val flex_shellnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_shellnum)); + } + emscripten::val flex_shelldataadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_shelldataadr)); + } + emscripten::val flex_evpairadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_evpairadr)); + } + emscripten::val flex_evpairnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_evpairnum)); + } + emscripten::val flex_texcoordadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_texcoordadr)); + } + emscripten::val flex_nodebodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexnode, ptr_->flex_nodebodyid)); + } + emscripten::val flex_vertbodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexvert, ptr_->flex_vertbodyid)); + } + emscripten::val flex_edge() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexedge * 2, ptr_->flex_edge)); + } + emscripten::val flex_edgeflap() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexedge * 2, ptr_->flex_edgeflap)); + } + emscripten::val flex_elem() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexelemdata, ptr_->flex_elem)); + } + emscripten::val flex_elemtexcoord() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexelemdata, ptr_->flex_elemtexcoord)); + } + emscripten::val flex_elemedge() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexelemedge, ptr_->flex_elemedge)); + } + emscripten::val flex_elemlayer() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexelem, ptr_->flex_elemlayer)); + } + emscripten::val flex_shell() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexshelldata, ptr_->flex_shell)); + } + emscripten::val flex_evpair() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexevpair * 2, ptr_->flex_evpair)); + } + emscripten::val flex_vert() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexvert * 3, ptr_->flex_vert)); + } + emscripten::val flex_vert0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexvert * 3, ptr_->flex_vert0)); + } + emscripten::val flex_node() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexnode * 3, ptr_->flex_node)); + } + emscripten::val flex_node0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexnode * 3, ptr_->flex_node0)); + } + emscripten::val flexedge_length0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexedge, ptr_->flexedge_length0)); + } + emscripten::val flexedge_invweight0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexedge, ptr_->flexedge_invweight0)); + } + emscripten::val flex_radius() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_radius)); + } + emscripten::val flex_stiffness() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexelem * 21, ptr_->flex_stiffness)); + } + emscripten::val flex_bending() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexedge * 17, ptr_->flex_bending)); + } + emscripten::val flex_damping() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_damping)); + } + emscripten::val flex_edgestiffness() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_edgestiffness)); + } + emscripten::val flex_edgedamping() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_edgedamping)); + } + emscripten::val flex_edgeequality() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_edgeequality)); + } + emscripten::val flex_rigid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_rigid)); + } + emscripten::val flexedge_rigid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflexedge, ptr_->flexedge_rigid)); + } + emscripten::val flex_centered() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_centered)); + } + emscripten::val flex_flatskin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_flatskin)); + } + emscripten::val flex_bvhadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_bvhadr)); + } + emscripten::val flex_bvhnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_bvhnum)); + } + emscripten::val flex_rgba() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex * 4, ptr_->flex_rgba)); + } + emscripten::val flex_texcoord() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflextexcoord * 2, ptr_->flex_texcoord)); + } + emscripten::val mesh_vertadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_vertadr)); + } + emscripten::val mesh_vertnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_vertnum)); + } + emscripten::val mesh_faceadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_faceadr)); + } + emscripten::val mesh_facenum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_facenum)); + } + emscripten::val mesh_bvhadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_bvhadr)); + } + emscripten::val mesh_bvhnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_bvhnum)); + } + emscripten::val mesh_octadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_octadr)); + } + emscripten::val mesh_octnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_octnum)); + } + emscripten::val mesh_normaladr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_normaladr)); + } + emscripten::val mesh_normalnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_normalnum)); + } + emscripten::val mesh_texcoordadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_texcoordadr)); + } + emscripten::val mesh_texcoordnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_texcoordnum)); + } + emscripten::val mesh_graphadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_graphadr)); + } + emscripten::val mesh_vert() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshvert * 3, ptr_->mesh_vert)); + } + emscripten::val mesh_normal() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshnormal * 3, ptr_->mesh_normal)); + } + emscripten::val mesh_texcoord() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshtexcoord * 2, ptr_->mesh_texcoord)); + } + emscripten::val mesh_face() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshface * 3, ptr_->mesh_face)); + } + emscripten::val mesh_facenormal() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshface * 3, ptr_->mesh_facenormal)); + } + emscripten::val mesh_facetexcoord() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshface * 3, ptr_->mesh_facetexcoord)); + } + emscripten::val mesh_graph() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshgraph, ptr_->mesh_graph)); + } + emscripten::val mesh_scale() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh * 3, ptr_->mesh_scale)); + } + emscripten::val mesh_pos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh * 3, ptr_->mesh_pos)); + } + emscripten::val mesh_quat() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh * 4, ptr_->mesh_quat)); + } + emscripten::val mesh_pathadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_pathadr)); + } + emscripten::val mesh_polynum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_polynum)); + } + emscripten::val mesh_polyadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->mesh_polyadr)); + } + emscripten::val mesh_polynormal() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshpoly * 3, ptr_->mesh_polynormal)); + } + emscripten::val mesh_polyvertadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshpoly, ptr_->mesh_polyvertadr)); + } + emscripten::val mesh_polyvertnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshpoly, ptr_->mesh_polyvertnum)); + } + emscripten::val mesh_polyvert() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshpolyvert, ptr_->mesh_polyvert)); + } + emscripten::val mesh_polymapadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshvert, ptr_->mesh_polymapadr)); + } + emscripten::val mesh_polymapnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshvert, ptr_->mesh_polymapnum)); + } + emscripten::val mesh_polymap() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmeshpolymap, ptr_->mesh_polymap)); + } + emscripten::val skin_matid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_matid)); + } + emscripten::val skin_group() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_group)); + } + emscripten::val skin_rgba() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin * 4, ptr_->skin_rgba)); + } + emscripten::val skin_inflate() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_inflate)); + } + emscripten::val skin_vertadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_vertadr)); + } + emscripten::val skin_vertnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_vertnum)); + } + emscripten::val skin_texcoordadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_texcoordadr)); + } + emscripten::val skin_faceadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_faceadr)); + } + emscripten::val skin_facenum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_facenum)); + } + emscripten::val skin_boneadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_boneadr)); + } + emscripten::val skin_bonenum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_bonenum)); + } + emscripten::val skin_vert() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskinvert * 3, ptr_->skin_vert)); + } + emscripten::val skin_texcoord() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskintexvert * 2, ptr_->skin_texcoord)); + } + emscripten::val skin_face() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskinface * 3, ptr_->skin_face)); + } + emscripten::val skin_bonevertadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskinbone, ptr_->skin_bonevertadr)); + } + emscripten::val skin_bonevertnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskinbone, ptr_->skin_bonevertnum)); + } + emscripten::val skin_bonebindpos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskinbone * 3, ptr_->skin_bonebindpos)); + } + emscripten::val skin_bonebindquat() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskinbone * 4, ptr_->skin_bonebindquat)); + } + emscripten::val skin_bonebodyid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskinbone, ptr_->skin_bonebodyid)); + } + emscripten::val skin_bonevertid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskinbonevert, ptr_->skin_bonevertid)); + } + emscripten::val skin_bonevertweight() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskinbonevert, ptr_->skin_bonevertweight)); + } + emscripten::val skin_pathadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->skin_pathadr)); + } + emscripten::val hfield_size() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nhfield * 4, ptr_->hfield_size)); + } + emscripten::val hfield_nrow() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nhfield, ptr_->hfield_nrow)); + } + emscripten::val hfield_ncol() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nhfield, ptr_->hfield_ncol)); + } + emscripten::val hfield_adr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nhfield, ptr_->hfield_adr)); + } + emscripten::val hfield_data() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nhfielddata, ptr_->hfield_data)); + } + emscripten::val hfield_pathadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nhfield, ptr_->hfield_pathadr)); + } + emscripten::val tex_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntex, ptr_->tex_type)); + } + emscripten::val tex_colorspace() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntex, ptr_->tex_colorspace)); + } + emscripten::val tex_height() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntex, ptr_->tex_height)); + } + emscripten::val tex_width() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntex, ptr_->tex_width)); + } + emscripten::val tex_nchannel() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntex, ptr_->tex_nchannel)); + } + emscripten::val tex_adr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntex, ptr_->tex_adr)); + } + emscripten::val tex_data() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntexdata, ptr_->tex_data)); + } + emscripten::val tex_pathadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntex, ptr_->tex_pathadr)); + } + emscripten::val mat_texid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat * mjNTEXROLE, ptr_->mat_texid)); + } + emscripten::val mat_texuniform() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat, ptr_->mat_texuniform)); + } + emscripten::val mat_texrepeat() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat * 2, ptr_->mat_texrepeat)); + } + emscripten::val mat_emission() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat, ptr_->mat_emission)); + } + emscripten::val mat_specular() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat, ptr_->mat_specular)); + } + emscripten::val mat_shininess() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat, ptr_->mat_shininess)); + } + emscripten::val mat_reflectance() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat, ptr_->mat_reflectance)); + } + emscripten::val mat_metallic() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat, ptr_->mat_metallic)); + } + emscripten::val mat_roughness() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat, ptr_->mat_roughness)); + } + emscripten::val mat_rgba() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat * 4, ptr_->mat_rgba)); + } + emscripten::val pair_dim() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair, ptr_->pair_dim)); + } + emscripten::val pair_geom1() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair, ptr_->pair_geom1)); + } + emscripten::val pair_geom2() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair, ptr_->pair_geom2)); + } + emscripten::val pair_signature() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair, ptr_->pair_signature)); + } + emscripten::val pair_solref() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair * mjNREF, ptr_->pair_solref)); + } + emscripten::val pair_solreffriction() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair * mjNREF, ptr_->pair_solreffriction)); + } + emscripten::val pair_solimp() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair * mjNIMP, ptr_->pair_solimp)); + } + emscripten::val pair_margin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair, ptr_->pair_margin)); + } + emscripten::val pair_gap() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair, ptr_->pair_gap)); + } + emscripten::val pair_friction() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair * 5, ptr_->pair_friction)); + } + emscripten::val exclude_signature() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nexclude, ptr_->exclude_signature)); + } + emscripten::val eq_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->neq, ptr_->eq_type)); + } + emscripten::val eq_obj1id() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->neq, ptr_->eq_obj1id)); + } + emscripten::val eq_obj2id() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->neq, ptr_->eq_obj2id)); + } + emscripten::val eq_objtype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->neq, ptr_->eq_objtype)); + } + emscripten::val eq_active0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->neq, ptr_->eq_active0)); + } + emscripten::val eq_solref() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->neq * mjNREF, ptr_->eq_solref)); + } + emscripten::val eq_solimp() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->neq * mjNIMP, ptr_->eq_solimp)); + } + emscripten::val eq_data() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->neq * mjNEQDATA, ptr_->eq_data)); + } + emscripten::val tendon_adr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_adr)); + } + emscripten::val tendon_num() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_num)); + } + emscripten::val tendon_matid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_matid)); + } + emscripten::val tendon_group() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_group)); + } + emscripten::val tendon_limited() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_limited)); + } + emscripten::val tendon_actfrclimited() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_actfrclimited)); + } + emscripten::val tendon_width() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_width)); + } + emscripten::val tendon_solref_lim() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon * mjNREF, ptr_->tendon_solref_lim)); + } + emscripten::val tendon_solimp_lim() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon * mjNIMP, ptr_->tendon_solimp_lim)); + } + emscripten::val tendon_solref_fri() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon * mjNREF, ptr_->tendon_solref_fri)); + } + emscripten::val tendon_solimp_fri() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon * mjNIMP, ptr_->tendon_solimp_fri)); + } + emscripten::val tendon_range() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon * 2, ptr_->tendon_range)); + } + emscripten::val tendon_actfrcrange() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon * 2, ptr_->tendon_actfrcrange)); + } + emscripten::val tendon_margin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_margin)); + } + emscripten::val tendon_stiffness() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_stiffness)); + } + emscripten::val tendon_damping() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_damping)); + } + emscripten::val tendon_armature() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_armature)); + } + emscripten::val tendon_frictionloss() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_frictionloss)); + } + emscripten::val tendon_lengthspring() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon * 2, ptr_->tendon_lengthspring)); + } + emscripten::val tendon_length0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_length0)); + } + emscripten::val tendon_invweight0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->tendon_invweight0)); + } + emscripten::val tendon_user() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon * ptr_->nuser_tendon, ptr_->tendon_user)); + } + emscripten::val tendon_rgba() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon * 4, ptr_->tendon_rgba)); + } + emscripten::val wrap_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nwrap, ptr_->wrap_type)); + } + emscripten::val wrap_objid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nwrap, ptr_->wrap_objid)); + } + emscripten::val wrap_prm() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nwrap, ptr_->wrap_prm)); + } + emscripten::val actuator_trntype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_trntype)); + } + emscripten::val actuator_dyntype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_dyntype)); + } + emscripten::val actuator_gaintype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_gaintype)); + } + emscripten::val actuator_biastype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_biastype)); + } + emscripten::val actuator_trnid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * 2, ptr_->actuator_trnid)); + } + emscripten::val actuator_actadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_actadr)); + } + emscripten::val actuator_actnum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_actnum)); + } + emscripten::val actuator_group() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_group)); + } + emscripten::val actuator_ctrllimited() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_ctrllimited)); + } + emscripten::val actuator_forcelimited() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_forcelimited)); + } + emscripten::val actuator_actlimited() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_actlimited)); + } + emscripten::val actuator_dynprm() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * mjNDYN, ptr_->actuator_dynprm)); + } + emscripten::val actuator_gainprm() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * mjNGAIN, ptr_->actuator_gainprm)); + } + emscripten::val actuator_biasprm() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * mjNBIAS, ptr_->actuator_biasprm)); + } + emscripten::val actuator_actearly() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_actearly)); + } + emscripten::val actuator_ctrlrange() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * 2, ptr_->actuator_ctrlrange)); + } + emscripten::val actuator_forcerange() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * 2, ptr_->actuator_forcerange)); + } + emscripten::val actuator_actrange() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * 2, ptr_->actuator_actrange)); + } + emscripten::val actuator_gear() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * 6, ptr_->actuator_gear)); + } + emscripten::val actuator_cranklength() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_cranklength)); + } + emscripten::val actuator_acc0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_acc0)); + } + emscripten::val actuator_length0() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_length0)); + } + emscripten::val actuator_lengthrange() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * 2, ptr_->actuator_lengthrange)); + } + emscripten::val actuator_user() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu * ptr_->nuser_actuator, ptr_->actuator_user)); + } + emscripten::val actuator_plugin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->actuator_plugin)); + } + emscripten::val sensor_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_type)); + } + emscripten::val sensor_datatype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_datatype)); + } + emscripten::val sensor_needstage() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_needstage)); + } + emscripten::val sensor_objtype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_objtype)); + } + emscripten::val sensor_objid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_objid)); + } + emscripten::val sensor_reftype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_reftype)); + } + emscripten::val sensor_refid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_refid)); + } + emscripten::val sensor_intprm() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor * mjNSENS, ptr_->sensor_intprm)); + } + emscripten::val sensor_dim() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_dim)); + } + emscripten::val sensor_adr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_adr)); + } + emscripten::val sensor_cutoff() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_cutoff)); + } + emscripten::val sensor_noise() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_noise)); + } + emscripten::val sensor_user() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor * ptr_->nuser_sensor, ptr_->sensor_user)); + } + emscripten::val sensor_plugin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->sensor_plugin)); + } + emscripten::val plugin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nplugin, ptr_->plugin)); + } + emscripten::val plugin_stateadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nplugin, ptr_->plugin_stateadr)); + } + emscripten::val plugin_statenum() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nplugin, ptr_->plugin_statenum)); + } + emscripten::val plugin_attr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npluginattr, ptr_->plugin_attr)); + } + emscripten::val plugin_attradr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nplugin, ptr_->plugin_attradr)); + } + emscripten::val numeric_adr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nnumeric, ptr_->numeric_adr)); + } + emscripten::val numeric_size() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nnumeric, ptr_->numeric_size)); + } + emscripten::val numeric_data() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nnumericdata, ptr_->numeric_data)); + } + emscripten::val text_adr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntext, ptr_->text_adr)); + } + emscripten::val text_size() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntext, ptr_->text_size)); + } + emscripten::val text_data() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntextdata, ptr_->text_data)); + } + emscripten::val tuple_adr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntuple, ptr_->tuple_adr)); + } + emscripten::val tuple_size() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntuple, ptr_->tuple_size)); + } + emscripten::val tuple_objtype() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntupledata, ptr_->tuple_objtype)); + } + emscripten::val tuple_objid() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntupledata, ptr_->tuple_objid)); + } + emscripten::val tuple_objprm() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntupledata, ptr_->tuple_objprm)); + } + emscripten::val key_time() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nkey, ptr_->key_time)); + } + emscripten::val key_qpos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nkey * ptr_->nq, ptr_->key_qpos)); + } + emscripten::val key_qvel() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nkey * ptr_->nv, ptr_->key_qvel)); + } + emscripten::val key_act() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nkey * ptr_->na, ptr_->key_act)); + } + emscripten::val key_mpos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nkey * ptr_->nmocap*3, ptr_->key_mpos)); + } + emscripten::val key_mquat() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nkey * ptr_->nmocap*4, ptr_->key_mquat)); + } + emscripten::val key_ctrl() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nkey * ptr_->nu, ptr_->key_ctrl)); + } + emscripten::val name_bodyadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->name_bodyadr)); + } + emscripten::val name_jntadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->njnt, ptr_->name_jntadr)); + } + emscripten::val name_geomadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom, ptr_->name_geomadr)); + } + emscripten::val name_siteadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsite, ptr_->name_siteadr)); + } + emscripten::val name_camadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->name_camadr)); + } + emscripten::val name_lightadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->name_lightadr)); + } + emscripten::val name_flexadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->name_flexadr)); + } + emscripten::val name_meshadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmesh, ptr_->name_meshadr)); + } + emscripten::val name_skinadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nskin, ptr_->name_skinadr)); + } + emscripten::val name_hfieldadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nhfield, ptr_->name_hfieldadr)); + } + emscripten::val name_texadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntex, ptr_->name_texadr)); + } + emscripten::val name_matadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nmat, ptr_->name_matadr)); + } + emscripten::val name_pairadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npair, ptr_->name_pairadr)); + } + emscripten::val name_excludeadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nexclude, ptr_->name_excludeadr)); + } + emscripten::val name_eqadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->neq, ptr_->name_eqadr)); + } + emscripten::val name_tendonadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntendon, ptr_->name_tendonadr)); + } + emscripten::val name_actuatoradr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nu, ptr_->name_actuatoradr)); + } + emscripten::val name_sensoradr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nsensor, ptr_->name_sensoradr)); + } + emscripten::val name_numericadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nnumeric, ptr_->name_numericadr)); + } + emscripten::val name_textadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntext, ptr_->name_textadr)); + } + emscripten::val name_tupleadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ntuple, ptr_->name_tupleadr)); + } + emscripten::val name_keyadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nkey, ptr_->name_keyadr)); + } + emscripten::val name_pluginadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nplugin, ptr_->name_pluginadr)); + } + emscripten::val names() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nnames, ptr_->names)); + } + emscripten::val names_map() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nnames_map, ptr_->names_map)); + } + emscripten::val paths() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->npaths, ptr_->paths)); + } + emscripten::val B_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->B_rownnz)); + } + emscripten::val B_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nbody, ptr_->B_rowadr)); + } + emscripten::val B_colind() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nB, ptr_->B_colind)); + } + emscripten::val M_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->M_rownnz)); + } + emscripten::val M_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->M_rowadr)); + } + emscripten::val M_colind() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nC, ptr_->M_colind)); + } + emscripten::val mapM2M() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nC, ptr_->mapM2M)); + } + emscripten::val D_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->D_rownnz)); + } + emscripten::val D_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->D_rowadr)); + } + emscripten::val D_diag() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nv, ptr_->D_diag)); + } + emscripten::val D_colind() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nD, ptr_->D_colind)); + } + emscripten::val mapM2D() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nD, ptr_->mapM2D)); + } + emscripten::val mapD2M() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nC, ptr_->mapD2M)); + } + uint64_t signature() const { + return ptr_->signature; + } + void set_signature(uint64_t value) { + ptr_->signature = value; + } + mjModel* get() const { return ptr_; } + void set(mjModel* ptr) { ptr_ = ptr; } + + private: + mjModel* ptr_; + + public: + MjOption opt; + MjStatistic stat; + MjVisual vis; +}; + +struct MjData { + MjData(MjModel *m); + explicit MjData(const MjModel &, const MjData &); + ~MjData(); + std::vector InitSolverArray(); + std::vector InitTimerArray(); + std::vector InitWarningArray(); + std::vector contact() const; + std::unique_ptr copy(); + mjtSize narena() const { + return ptr_->narena; + } + void set_narena(mjtSize value) { + ptr_->narena = value; + } + mjtSize nbuffer() const { + return ptr_->nbuffer; + } + void set_nbuffer(mjtSize value) { + ptr_->nbuffer = value; + } + int nplugin() const { + return ptr_->nplugin; + } + void set_nplugin(int value) { + ptr_->nplugin = value; + } + size_t pstack() const { + return ptr_->pstack; + } + void set_pstack(size_t value) { + ptr_->pstack = value; + } + size_t pbase() const { + return ptr_->pbase; + } + void set_pbase(size_t value) { + ptr_->pbase = value; + } + size_t parena() const { + return ptr_->parena; + } + void set_parena(size_t value) { + ptr_->parena = value; + } + mjtSize maxuse_stack() const { + return ptr_->maxuse_stack; + } + void set_maxuse_stack(mjtSize value) { + ptr_->maxuse_stack = value; + } + emscripten::val maxuse_threadstack() const { + return emscripten::val(emscripten::typed_memory_view(128, ptr_->maxuse_threadstack)); + } + mjtSize maxuse_arena() const { + return ptr_->maxuse_arena; + } + void set_maxuse_arena(mjtSize value) { + ptr_->maxuse_arena = value; + } + int maxuse_con() const { + return ptr_->maxuse_con; + } + void set_maxuse_con(int value) { + ptr_->maxuse_con = value; + } + int maxuse_efc() const { + return ptr_->maxuse_efc; + } + void set_maxuse_efc(int value) { + ptr_->maxuse_efc = value; + } + // array field is defined manually. solver + emscripten::val solver_niter() const { + return emscripten::val(emscripten::typed_memory_view(20, ptr_->solver_niter)); + } + emscripten::val solver_nnz() const { + return emscripten::val(emscripten::typed_memory_view(20, ptr_->solver_nnz)); + } + emscripten::val solver_fwdinv() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->solver_fwdinv)); + } + // array field is defined manually. warning + // array field is defined manually. timer + int ncon() const { + return ptr_->ncon; + } + void set_ncon(int value) { + ptr_->ncon = value; + } + int ne() const { + return ptr_->ne; + } + void set_ne(int value) { + ptr_->ne = value; + } + int nf() const { + return ptr_->nf; + } + void set_nf(int value) { + ptr_->nf = value; + } + int nl() const { + return ptr_->nl; + } + void set_nl(int value) { + ptr_->nl = value; + } + int nefc() const { + return ptr_->nefc; + } + void set_nefc(int value) { + ptr_->nefc = value; + } + int nJ() const { + return ptr_->nJ; + } + void set_nJ(int value) { + ptr_->nJ = value; + } + int nA() const { + return ptr_->nA; + } + void set_nA(int value) { + ptr_->nA = value; + } + int nisland() const { + return ptr_->nisland; + } + void set_nisland(int value) { + ptr_->nisland = value; + } + int nidof() const { + return ptr_->nidof; + } + void set_nidof(int value) { + ptr_->nidof = value; + } + mjtNum time() const { + return ptr_->time; + } + void set_time(mjtNum value) { + ptr_->time = value; + } + emscripten::val energy() const { + return emscripten::val(emscripten::typed_memory_view(2, ptr_->energy)); + } + emscripten::val buffer() const { + return emscripten::val(emscripten::typed_memory_view(model->nbuffer, static_cast(ptr_->buffer))); + } + emscripten::val arena() const { + return emscripten::val(emscripten::typed_memory_view(model->narena, static_cast(ptr_->arena))); + } + emscripten::val qpos() const { + return emscripten::val(emscripten::typed_memory_view(model->nq, ptr_->qpos)); + } + emscripten::val qvel() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qvel)); + } + emscripten::val act() const { + return emscripten::val(emscripten::typed_memory_view(model->na, ptr_->act)); + } + emscripten::val qacc_warmstart() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qacc_warmstart)); + } + emscripten::val plugin_state() const { + return emscripten::val(emscripten::typed_memory_view(model->npluginstate, ptr_->plugin_state)); + } + emscripten::val ctrl() const { + return emscripten::val(emscripten::typed_memory_view(model->nu, ptr_->ctrl)); + } + emscripten::val qfrc_applied() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_applied)); + } + emscripten::val xfrc_applied() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 6, ptr_->xfrc_applied)); + } + emscripten::val eq_active() const { + return emscripten::val(emscripten::typed_memory_view(model->neq, ptr_->eq_active)); + } + emscripten::val mocap_pos() const { + return emscripten::val(emscripten::typed_memory_view(model->nmocap * 3, ptr_->mocap_pos)); + } + emscripten::val mocap_quat() const { + return emscripten::val(emscripten::typed_memory_view(model->nmocap * 4, ptr_->mocap_quat)); + } + emscripten::val qacc() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qacc)); + } + emscripten::val act_dot() const { + return emscripten::val(emscripten::typed_memory_view(model->na, ptr_->act_dot)); + } + emscripten::val userdata() const { + return emscripten::val(emscripten::typed_memory_view(model->nuserdata, ptr_->userdata)); + } + emscripten::val sensordata() const { + return emscripten::val(emscripten::typed_memory_view(model->nsensordata, ptr_->sensordata)); + } + emscripten::val plugin() const { + return emscripten::val(emscripten::typed_memory_view(model->nplugin, ptr_->plugin)); + } + emscripten::val plugin_data() const { + return emscripten::val(emscripten::typed_memory_view(model->nplugin, ptr_->plugin_data)); + } + emscripten::val xpos() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 3, ptr_->xpos)); + } + emscripten::val xquat() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 4, ptr_->xquat)); + } + emscripten::val xmat() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 9, ptr_->xmat)); + } + emscripten::val xipos() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 3, ptr_->xipos)); + } + emscripten::val ximat() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 9, ptr_->ximat)); + } + emscripten::val xanchor() const { + return emscripten::val(emscripten::typed_memory_view(model->njnt * 3, ptr_->xanchor)); + } + emscripten::val xaxis() const { + return emscripten::val(emscripten::typed_memory_view(model->njnt * 3, ptr_->xaxis)); + } + emscripten::val geom_xpos() const { + return emscripten::val(emscripten::typed_memory_view(model->ngeom * 3, ptr_->geom_xpos)); + } + emscripten::val geom_xmat() const { + return emscripten::val(emscripten::typed_memory_view(model->ngeom * 9, ptr_->geom_xmat)); + } + emscripten::val site_xpos() const { + return emscripten::val(emscripten::typed_memory_view(model->nsite * 3, ptr_->site_xpos)); + } + emscripten::val site_xmat() const { + return emscripten::val(emscripten::typed_memory_view(model->nsite * 9, ptr_->site_xmat)); + } + emscripten::val cam_xpos() const { + return emscripten::val(emscripten::typed_memory_view(model->ncam * 3, ptr_->cam_xpos)); + } + emscripten::val cam_xmat() const { + return emscripten::val(emscripten::typed_memory_view(model->ncam * 9, ptr_->cam_xmat)); + } + emscripten::val light_xpos() const { + return emscripten::val(emscripten::typed_memory_view(model->nlight * 3, ptr_->light_xpos)); + } + emscripten::val light_xdir() const { + return emscripten::val(emscripten::typed_memory_view(model->nlight * 3, ptr_->light_xdir)); + } + emscripten::val subtree_com() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 3, ptr_->subtree_com)); + } + emscripten::val cdof() const { + return emscripten::val(emscripten::typed_memory_view(model->nv * 6, ptr_->cdof)); + } + emscripten::val cinert() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 10, ptr_->cinert)); + } + emscripten::val flexvert_xpos() const { + return emscripten::val(emscripten::typed_memory_view(model->nflexvert * 3, ptr_->flexvert_xpos)); + } + emscripten::val flexelem_aabb() const { + return emscripten::val(emscripten::typed_memory_view(model->nflexelem * 6, ptr_->flexelem_aabb)); + } + emscripten::val flexedge_J_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(model->nflexedge, ptr_->flexedge_J_rownnz)); + } + emscripten::val flexedge_J_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(model->nflexedge, ptr_->flexedge_J_rowadr)); + } + emscripten::val flexedge_J_colind() const { + return emscripten::val(emscripten::typed_memory_view(model->nflexedge * model->nv, ptr_->flexedge_J_colind)); + } + emscripten::val flexedge_J() const { + return emscripten::val(emscripten::typed_memory_view(model->nflexedge * model->nv, ptr_->flexedge_J)); + } + emscripten::val flexedge_length() const { + return emscripten::val(emscripten::typed_memory_view(model->nflexedge, ptr_->flexedge_length)); + } + emscripten::val bvh_aabb_dyn() const { + return emscripten::val(emscripten::typed_memory_view(model->nbvhdynamic * 6, ptr_->bvh_aabb_dyn)); + } + emscripten::val ten_wrapadr() const { + return emscripten::val(emscripten::typed_memory_view(model->ntendon, ptr_->ten_wrapadr)); + } + emscripten::val ten_wrapnum() const { + return emscripten::val(emscripten::typed_memory_view(model->ntendon, ptr_->ten_wrapnum)); + } + emscripten::val ten_J_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(model->ntendon, ptr_->ten_J_rownnz)); + } + emscripten::val ten_J_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(model->ntendon, ptr_->ten_J_rowadr)); + } + emscripten::val ten_J_colind() const { + return emscripten::val(emscripten::typed_memory_view(model->ntendon * model->nv, ptr_->ten_J_colind)); + } + emscripten::val ten_J() const { + return emscripten::val(emscripten::typed_memory_view(model->ntendon * model->nv, ptr_->ten_J)); + } + emscripten::val ten_length() const { + return emscripten::val(emscripten::typed_memory_view(model->ntendon, ptr_->ten_length)); + } + emscripten::val wrap_obj() const { + return emscripten::val(emscripten::typed_memory_view(model->nwrap * 2, ptr_->wrap_obj)); + } + emscripten::val wrap_xpos() const { + return emscripten::val(emscripten::typed_memory_view(model->nwrap * 6, ptr_->wrap_xpos)); + } + emscripten::val actuator_length() const { + return emscripten::val(emscripten::typed_memory_view(model->nu, ptr_->actuator_length)); + } + emscripten::val moment_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(model->nu, ptr_->moment_rownnz)); + } + emscripten::val moment_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(model->nu, ptr_->moment_rowadr)); + } + emscripten::val moment_colind() const { + return emscripten::val(emscripten::typed_memory_view(model->nJmom, ptr_->moment_colind)); + } + emscripten::val actuator_moment() const { + return emscripten::val(emscripten::typed_memory_view(model->nJmom, ptr_->actuator_moment)); + } + emscripten::val crb() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 10, ptr_->crb)); + } + emscripten::val qM() const { + return emscripten::val(emscripten::typed_memory_view(model->nM, ptr_->qM)); + } + emscripten::val M() const { + return emscripten::val(emscripten::typed_memory_view(model->nC, ptr_->M)); + } + emscripten::val qLD() const { + return emscripten::val(emscripten::typed_memory_view(model->nC, ptr_->qLD)); + } + emscripten::val qLDiagInv() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qLDiagInv)); + } + emscripten::val bvh_active() const { + return emscripten::val(emscripten::typed_memory_view(model->nbvh, ptr_->bvh_active)); + } + emscripten::val flexedge_velocity() const { + return emscripten::val(emscripten::typed_memory_view(model->nflexedge, ptr_->flexedge_velocity)); + } + emscripten::val ten_velocity() const { + return emscripten::val(emscripten::typed_memory_view(model->ntendon, ptr_->ten_velocity)); + } + emscripten::val actuator_velocity() const { + return emscripten::val(emscripten::typed_memory_view(model->nu, ptr_->actuator_velocity)); + } + emscripten::val cvel() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 6, ptr_->cvel)); + } + emscripten::val cdof_dot() const { + return emscripten::val(emscripten::typed_memory_view(model->nv * 6, ptr_->cdof_dot)); + } + emscripten::val qfrc_bias() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_bias)); + } + emscripten::val qfrc_spring() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_spring)); + } + emscripten::val qfrc_damper() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_damper)); + } + emscripten::val qfrc_gravcomp() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_gravcomp)); + } + emscripten::val qfrc_fluid() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_fluid)); + } + emscripten::val qfrc_passive() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_passive)); + } + emscripten::val subtree_linvel() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 3, ptr_->subtree_linvel)); + } + emscripten::val subtree_angmom() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 3, ptr_->subtree_angmom)); + } + emscripten::val qH() const { + return emscripten::val(emscripten::typed_memory_view(model->nC, ptr_->qH)); + } + emscripten::val qHDiagInv() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qHDiagInv)); + } + emscripten::val qDeriv() const { + return emscripten::val(emscripten::typed_memory_view(model->nD, ptr_->qDeriv)); + } + emscripten::val qLU() const { + return emscripten::val(emscripten::typed_memory_view(model->nD, ptr_->qLU)); + } + emscripten::val actuator_force() const { + return emscripten::val(emscripten::typed_memory_view(model->nu, ptr_->actuator_force)); + } + emscripten::val qfrc_actuator() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_actuator)); + } + emscripten::val qfrc_smooth() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_smooth)); + } + emscripten::val qacc_smooth() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qacc_smooth)); + } + emscripten::val qfrc_constraint() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_constraint)); + } + emscripten::val qfrc_inverse() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->qfrc_inverse)); + } + emscripten::val cacc() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 6, ptr_->cacc)); + } + emscripten::val cfrc_int() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 6, ptr_->cfrc_int)); + } + emscripten::val cfrc_ext() const { + return emscripten::val(emscripten::typed_memory_view(model->nbody * 6, ptr_->cfrc_ext)); + } + // complex pointer field is defined manually. contact + emscripten::val efc_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_type)); + } + emscripten::val efc_id() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_id)); + } + emscripten::val efc_J_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_J_rownnz)); + } + emscripten::val efc_J_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_J_rowadr)); + } + emscripten::val efc_J_rowsuper() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_J_rowsuper)); + } + emscripten::val efc_J_colind() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nJ, ptr_->efc_J_colind)); + } + emscripten::val efc_J() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nJ, ptr_->efc_J)); + } + emscripten::val efc_pos() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_pos)); + } + emscripten::val efc_margin() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_margin)); + } + emscripten::val efc_frictionloss() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_frictionloss)); + } + emscripten::val efc_diagApprox() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_diagApprox)); + } + emscripten::val efc_KBIP() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc * 4, ptr_->efc_KBIP)); + } + emscripten::val efc_D() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_D)); + } + emscripten::val efc_R() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_R)); + } + emscripten::val tendon_efcadr() const { + return emscripten::val(emscripten::typed_memory_view(model->ntendon, ptr_->tendon_efcadr)); + } + emscripten::val dof_island() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->dof_island)); + } + emscripten::val island_nv() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nisland, ptr_->island_nv)); + } + emscripten::val island_idofadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nisland, ptr_->island_idofadr)); + } + emscripten::val island_dofadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nisland, ptr_->island_dofadr)); + } + emscripten::val map_dof2idof() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->map_dof2idof)); + } + emscripten::val map_idof2dof() const { + return emscripten::val(emscripten::typed_memory_view(model->nv, ptr_->map_idof2dof)); + } + emscripten::val ifrc_smooth() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nidof, ptr_->ifrc_smooth)); + } + emscripten::val iacc_smooth() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nidof, ptr_->iacc_smooth)); + } + emscripten::val iM_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nidof, ptr_->iM_rownnz)); + } + emscripten::val iM_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nidof, ptr_->iM_rowadr)); + } + emscripten::val iM_colind() const { + return emscripten::val(emscripten::typed_memory_view(model->nC, ptr_->iM_colind)); + } + emscripten::val iM() const { + return emscripten::val(emscripten::typed_memory_view(model->nC, ptr_->iM)); + } + emscripten::val iLD() const { + return emscripten::val(emscripten::typed_memory_view(model->nC, ptr_->iLD)); + } + emscripten::val iLDiagInv() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nidof, ptr_->iLDiagInv)); + } + emscripten::val iacc() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nidof, ptr_->iacc)); + } + emscripten::val efc_island() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_island)); + } + emscripten::val island_ne() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nisland, ptr_->island_ne)); + } + emscripten::val island_nf() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nisland, ptr_->island_nf)); + } + emscripten::val island_nefc() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nisland, ptr_->island_nefc)); + } + emscripten::val island_iefcadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nisland, ptr_->island_iefcadr)); + } + emscripten::val map_efc2iefc() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->map_efc2iefc)); + } + emscripten::val map_iefc2efc() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->map_iefc2efc)); + } + emscripten::val iefc_type() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_type)); + } + emscripten::val iefc_id() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_id)); + } + emscripten::val iefc_J_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_J_rownnz)); + } + emscripten::val iefc_J_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_J_rowadr)); + } + emscripten::val iefc_J_rowsuper() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_J_rowsuper)); + } + emscripten::val iefc_J_colind() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nJ, ptr_->iefc_J_colind)); + } + emscripten::val iefc_J() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nJ, ptr_->iefc_J)); + } + emscripten::val iefc_frictionloss() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_frictionloss)); + } + emscripten::val iefc_D() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_D)); + } + emscripten::val iefc_R() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_R)); + } + emscripten::val efc_AR_rownnz() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_AR_rownnz)); + } + emscripten::val efc_AR_rowadr() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_AR_rowadr)); + } + emscripten::val efc_AR_colind() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nA, ptr_->efc_AR_colind)); + } + emscripten::val efc_AR() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nA, ptr_->efc_AR)); + } + emscripten::val efc_vel() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_vel)); + } + emscripten::val efc_aref() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_aref)); + } + emscripten::val efc_b() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_b)); + } + emscripten::val iefc_aref() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_aref)); + } + emscripten::val iefc_state() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_state)); + } + emscripten::val iefc_force() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->iefc_force)); + } + emscripten::val efc_state() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_state)); + } + emscripten::val efc_force() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_force)); + } + emscripten::val ifrc_constraint() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->nidof, ptr_->ifrc_constraint)); + } + uintptr_t threadpool() const { + return ptr_->threadpool; + } + void set_threadpool(uintptr_t value) { + ptr_->threadpool = value; + } + uint64_t signature() const { + return ptr_->signature; + } + void set_signature(uint64_t value) { + ptr_->signature = value; + } + mjData* get() const { return ptr_; } + void set(mjData* ptr) { ptr_ = ptr; } + + private: + mjData* ptr_; + + public: + mjModel *model; + std::vector solver; + std::vector timer; + std::vector warning; +}; + +struct MjvScene { + MjvScene(); + MjvScene(MjModel *m, int maxgeom); + // MjvScene(const MjvScene &); + ~MjvScene(); + std::unique_ptr copy(); + int GetSumFlexFaces() const; + std::vector InitLightsArray(); + std::vector InitCameraArray(); + + std::vector geoms() const; + + emscripten::val geomorder() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->ngeom, ptr_->geomorder)); + } + emscripten::val flexedgeadr() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexedgeadr)); + } + emscripten::val flexedgenum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexedgenum)); + } + emscripten::val flexvertadr() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexvertadr)); + } + emscripten::val flexvertnum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexvertnum)); + } + emscripten::val flexfaceadr() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexfaceadr)); + } + emscripten::val flexfacenum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexfacenum)); + } + emscripten::val flexfaceused() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexfaceused)); + } + emscripten::val flexedge() const { + return emscripten::val( + emscripten::typed_memory_view(2 * model->nflexedge, ptr_->flexedge)); + } + emscripten::val flexvert() const { + return emscripten::val( + emscripten::typed_memory_view(3 * model->nflexvert, ptr_->flexvert)); + } + emscripten::val skinfacenum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nskin, ptr_->skinfacenum)); + } + emscripten::val skinvertadr() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nskin, ptr_->skinvertadr)); + } + emscripten::val skinvertnum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nskin, ptr_->skinvertnum)); + } + emscripten::val skinvert() const { + return emscripten::val( + emscripten::typed_memory_view(3 * model->nskinvert, ptr_->skinvert)); + } + emscripten::val skinnormal() const { + return emscripten::val( + emscripten::typed_memory_view(3 * model->nskinvert, ptr_->skinnormal)); + } + emscripten::val flexface() const { + return emscripten::val(emscripten::typed_memory_view( + 9 * MjvScene::GetSumFlexFaces(), ptr_->flexface)); + } + emscripten::val flexnormal() const { + return emscripten::val(emscripten::typed_memory_view( + 9 * MjvScene::GetSumFlexFaces(), ptr_->flexnormal)); + } + emscripten::val flextexcoord() const { + return emscripten::val(emscripten::typed_memory_view( + 6 * MjvScene::GetSumFlexFaces(), ptr_->flextexcoord)); + } + int maxgeom() const { + return ptr_->maxgeom; + } + void set_maxgeom(int value) { + ptr_->maxgeom = value; + } + int ngeom() const { + return ptr_->ngeom; + } + void set_ngeom(int value) { + ptr_->ngeom = value; + } + // complex pointer field is defined manually. geoms + // primitive pointer field with complex extents is defined manually. geomorder + int nflex() const { + return ptr_->nflex; + } + void set_nflex(int value) { + ptr_->nflex = value; + } + // primitive pointer field with complex extents is defined manually. flexedgeadr + // primitive pointer field with complex extents is defined manually. flexedgenum + // primitive pointer field with complex extents is defined manually. flexvertadr + // primitive pointer field with complex extents is defined manually. flexvertnum + // primitive pointer field with complex extents is defined manually. flexfaceadr + // primitive pointer field with complex extents is defined manually. flexfacenum + // primitive pointer field with complex extents is defined manually. flexfaceused + // primitive pointer field with complex extents is defined manually. flexedge + // primitive pointer field with complex extents is defined manually. flexvert + // primitive pointer field with complex extents is defined manually. flexface + // primitive pointer field with complex extents is defined manually. flexnormal + // primitive pointer field with complex extents is defined manually. flextexcoord + mjtByte flexvertopt() const { + return ptr_->flexvertopt; + } + void set_flexvertopt(mjtByte value) { + ptr_->flexvertopt = value; + } + mjtByte flexedgeopt() const { + return ptr_->flexedgeopt; + } + void set_flexedgeopt(mjtByte value) { + ptr_->flexedgeopt = value; + } + mjtByte flexfaceopt() const { + return ptr_->flexfaceopt; + } + void set_flexfaceopt(mjtByte value) { + ptr_->flexfaceopt = value; + } + mjtByte flexskinopt() const { + return ptr_->flexskinopt; + } + void set_flexskinopt(mjtByte value) { + ptr_->flexskinopt = value; + } + int nskin() const { + return ptr_->nskin; + } + void set_nskin(int value) { + ptr_->nskin = value; + } + // primitive pointer field with complex extents is defined manually. skinfacenum + // primitive pointer field with complex extents is defined manually. skinvertadr + // primitive pointer field with complex extents is defined manually. skinvertnum + // primitive pointer field with complex extents is defined manually. skinvert + // primitive pointer field with complex extents is defined manually. skinnormal + int nlight() const { + return ptr_->nlight; + } + void set_nlight(int value) { + ptr_->nlight = value; + } + // array field is defined manually. lights + // array field is defined manually. camera + mjtByte enabletransform() const { + return ptr_->enabletransform; + } + void set_enabletransform(mjtByte value) { + ptr_->enabletransform = value; + } + emscripten::val translate() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->translate)); + } + emscripten::val rotate() const { + return emscripten::val(emscripten::typed_memory_view(4, ptr_->rotate)); + } + float scale() const { + return ptr_->scale; + } + void set_scale(float value) { + ptr_->scale = value; + } + int stereo() const { + return ptr_->stereo; + } + void set_stereo(int value) { + ptr_->stereo = value; + } + emscripten::val flags() const { + return emscripten::val(emscripten::typed_memory_view(10, ptr_->flags)); + } + int framewidth() const { + return ptr_->framewidth; + } + void set_framewidth(int value) { + ptr_->framewidth = value; + } + emscripten::val framergb() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->framergb)); + } + int status() const { + return ptr_->status; + } + void set_status(int value) { + ptr_->status = value; + } + mjvScene* get() const { return ptr_; } + void set(mjvScene* ptr) { ptr_ = ptr; } + + private: + mjvScene* ptr_; + bool owned_ = false; + + public: + mjModel *model; + std::vector lights; + std::vector camera; +}; + +struct MjSpec { + MjSpec(); + explicit MjSpec(mjSpec *ptr); + MjSpec(const MjSpec &); + MjSpec &operator=(const MjSpec &); + ~MjSpec(); + std::unique_ptr copy(); + // complex pointer field is defined manually. element + mjString modelname() const { + return (ptr_ && ptr_->modelname) ? *(ptr_->modelname) : ""; + } + void set_modelname(const mjString& value) { + if (ptr_ && ptr_->modelname) { + *(ptr_->modelname) = value; + } + } + // struct field is defined manually. compiler + mjtByte strippath() const { + return ptr_->strippath; + } + void set_strippath(mjtByte value) { + ptr_->strippath = value; + } + // struct field is defined manually. option + // struct field is defined manually. visual + // struct field is defined manually. stat + mjtSize memory() const { + return ptr_->memory; + } + void set_memory(mjtSize value) { + ptr_->memory = value; + } + int nemax() const { + return ptr_->nemax; + } + void set_nemax(int value) { + ptr_->nemax = value; + } + int nuserdata() const { + return ptr_->nuserdata; + } + void set_nuserdata(int value) { + ptr_->nuserdata = value; + } + int nuser_body() const { + return ptr_->nuser_body; + } + void set_nuser_body(int value) { + ptr_->nuser_body = value; + } + int nuser_jnt() const { + return ptr_->nuser_jnt; + } + void set_nuser_jnt(int value) { + ptr_->nuser_jnt = value; + } + int nuser_geom() const { + return ptr_->nuser_geom; + } + void set_nuser_geom(int value) { + ptr_->nuser_geom = value; + } + int nuser_site() const { + return ptr_->nuser_site; + } + void set_nuser_site(int value) { + ptr_->nuser_site = value; + } + int nuser_cam() const { + return ptr_->nuser_cam; + } + void set_nuser_cam(int value) { + ptr_->nuser_cam = value; + } + int nuser_tendon() const { + return ptr_->nuser_tendon; + } + void set_nuser_tendon(int value) { + ptr_->nuser_tendon = value; + } + int nuser_actuator() const { + return ptr_->nuser_actuator; + } + void set_nuser_actuator(int value) { + ptr_->nuser_actuator = value; + } + int nuser_sensor() const { + return ptr_->nuser_sensor; + } + void set_nuser_sensor(int value) { + ptr_->nuser_sensor = value; + } + int nkey() const { + return ptr_->nkey; + } + void set_nkey(int value) { + ptr_->nkey = value; + } + int njmax() const { + return ptr_->njmax; + } + void set_njmax(int value) { + ptr_->njmax = value; + } + int nconmax() const { + return ptr_->nconmax; + } + void set_nconmax(int value) { + ptr_->nconmax = value; + } + mjtSize nstack() const { + return ptr_->nstack; + } + void set_nstack(mjtSize value) { + ptr_->nstack = value; + } + mjString comment() const { + return (ptr_ && ptr_->comment) ? *(ptr_->comment) : ""; + } + void set_comment(const mjString& value) { + if (ptr_ && ptr_->comment) { + *(ptr_->comment) = value; + } + } + mjString modelfiledir() const { + return (ptr_ && ptr_->modelfiledir) ? *(ptr_->modelfiledir) : ""; + } + void set_modelfiledir(const mjString& value) { + if (ptr_ && ptr_->modelfiledir) { + *(ptr_->modelfiledir) = value; + } + } + mjtByte hasImplicitPluginElem() const { + return ptr_->hasImplicitPluginElem; + } + void set_hasImplicitPluginElem(mjtByte value) { + ptr_->hasImplicitPluginElem = value; + } + mjSpec* get() const { return ptr_; } + void set(mjSpec* ptr) { ptr_ = ptr; } + + private: + mjSpec* ptr_; + bool owned_ = false; + + public: + MjOption option; + MjVisual visual; + MjStatistic stat; + MjsCompiler compiler; + MjsElement element; +}; + +// TODO: Refactor, Structs Manually added so functions.cc compile -- // +struct MjpResourceProvider { + MjpResourceProvider(mjpResourceProvider *ptr_) { ptr = ptr_; }; + ~MjpResourceProvider() {} + mjpResourceProvider *get() const { return ptr; } + mjpResourceProvider *ptr; +}; + +struct MjpPlugin { + MjpPlugin(mjpPlugin *ptr_) { ptr = ptr_; }; + ~MjpPlugin() {} + mjpPlugin *get() const { return ptr; } + mjpPlugin *ptr; +}; + +// TODO: Factory and debug helper functions, some should be removed when +// functions are generated -- // +std::unique_ptr loadFromXML(std::string filename); +void step(MjModel *model, MjData *data); +void error(const std::string &msg); +void kinematics(MjModel *m, MjData *d); +std::unique_ptr parseXMLString(const std::string &xml); +std::unique_ptr findBody(MjSpec *spec, const std::string &name); +std::unique_ptr findGeom(MjSpec *spec, const std::string &name); + +} // namespace mujoco::wasm + +#endif // MUJOCO_WASM_CODEGEN_GENERATED_BINDINGS_H_ +// NOLINTEND(whitespace/line_length) diff --git a/wasm/codegen/generators/constants.py b/wasm/codegen/generators/constants.py new file mode 100644 index 00000000..a7550ca2 --- /dev/null +++ b/wasm/codegen/generators/constants.py @@ -0,0 +1,34 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generator for the constants.""" + +from wasm.codegen.helpers import common + + +# TODO(manevi): Delete this file and use the genrule to handle the file copying +class Generator: + """Generator for the constants.""" + + def run(self): + """Runs the generator.""" + template_cc_file, output_cc_file = common.get_file_path( + "templates", "generated", "constants.cc" + ) + + with open(template_cc_file, "r") as f_template: + template_content = f_template.read() + + with open(output_cc_file, "w") as f_output: + f_output.write(template_content) diff --git a/wasm/codegen/generators/enums.py b/wasm/codegen/generators/enums.py new file mode 100644 index 00000000..ecbb66d0 --- /dev/null +++ b/wasm/codegen/generators/enums.py @@ -0,0 +1,47 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generates Embind bindings for MuJoCo enums.""" + +from typing import Mapping + +from introspect import ast_nodes + +from wasm.codegen.helpers import code_builder + + +class Generator: + """Generates Embind code for MuJoCo enums.""" + + def __init__(self, enums: Mapping[str, ast_nodes.EnumDecl]): + self.enums = enums + + def _generate_enum_binding(self, enum: ast_nodes.EnumDecl) -> str: + """Generates the Embind code for a single enum.""" + + code = f'{code_builder.INDENT}enum_<{enum.name}>("{enum.name}")' + + for value_name in enum.values: + code += f'\n{2*code_builder.INDENT}.value("{value_name}", {value_name})' + + code += ";" + return code + + def generate(self) -> str: + """Generates all Embind code for the provided enums.""" + + code = [] + for enum in self.enums.values(): + code.append(self._generate_enum_binding(enum)) + return "\n\n".join(code) + "\n" diff --git a/wasm/codegen/generators/enums_test.py b/wasm/codegen/generators/enums_test.py new file mode 100644 index 00000000..1d09de6f --- /dev/null +++ b/wasm/codegen/generators/enums_test.py @@ -0,0 +1,62 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from absl.testing import absltest + +from introspect import ast_nodes + +from wasm.codegen.generators import enums + + +class EnumsGeneratorTest(absltest.TestCase): + + def test_generate_enum_bindings(self): + + generator = enums.Generator({ + "TestEnum": ast_nodes.EnumDecl( + name="TestEnum", + declname="enum TestEnum_", + values={"FIRST_VAL": 0, "SECOND_VAL": 1, "THIRD_VAL": 2}, + ), + "AnotherEnum": ast_nodes.EnumDecl( + name="AnotherEnum", + declname="enum AnotherEnum_", + values={"ALPHA": 100, "BETA": 200}, + ), + "EmptyEnum": ast_nodes.EnumDecl( + name="EmptyEnum", + declname="enum EmptyEnum_", + values={}, + ), + }) + + expected_code = """ enum_("TestEnum") + .value("FIRST_VAL", FIRST_VAL) + .value("SECOND_VAL", SECOND_VAL) + .value("THIRD_VAL", THIRD_VAL); + + enum_("AnotherEnum") + .value("ALPHA", ALPHA) + .value("BETA", BETA); + + enum_("EmptyEnum"); +""" + + actual_code = generator.generate() + + self.assertEqual(actual_code, expected_code) + + +if __name__ == "__main__": + absltest.main() diff --git a/wasm/codegen/generators/functions.py b/wasm/codegen/generators/functions.py new file mode 100644 index 00000000..0569ea8b --- /dev/null +++ b/wasm/codegen/generators/functions.py @@ -0,0 +1,93 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generates Embind bindings for MuJoCo functions.""" + +import pathlib +from typing import List, Mapping, TypeAlias + +from introspect import ast_nodes + +from wasm.codegen.helpers import code_builder +from wasm.codegen.helpers import function_utils + +FunctionDecl: TypeAlias = ast_nodes.FunctionDecl +FunctionParameterDecl: TypeAlias = ast_nodes.FunctionParameterDecl +PointerType: TypeAlias = ast_nodes.PointerType +ValueType: TypeAlias = ast_nodes.ValueType +Path: TypeAlias = pathlib.Path + + +class Generator: + """Generates Embind bindings for MuJoCo functions.""" + + def __init__(self, functions: Mapping[str, FunctionDecl]): + self.direct_bind_functions: List[FunctionDecl] = [] + self.wrapper_bind_functions: List[FunctionDecl] = [] + + for func in functions.values(): + if function_utils.should_be_wrapped(func): + self.wrapper_bind_functions.append(func) + else: + self.direct_bind_functions.append(func) + + def _generate_wrappers(self) -> str: + """Generates Embind bindings for all functions that need wrappers.""" + + code = [] + for func in self.wrapper_bind_functions: + wrapper_code = function_utils.generate_function_wrapper(func) + code.append(wrapper_code) + + return "\n\n".join(code) + + def _generate_direct_bindable_functions(self) -> str: + """Generates Embind bindings for all directly bindable functions.""" + + result = "" + for func in self.direct_bind_functions: + result += code_builder.INDENT + result += self._generate_function_binding(func) + + return result + + def _generate_function_binding( + self, func: FunctionDecl, is_wrapper=False + ) -> str: + """Generates the Embind code for a single function.""" + + js_name, cpp_func = func.name, func.name + if is_wrapper: + cpp_func += "_wrapper" + + return f'function("{js_name}", &{cpp_func});\n' + + def _generate_wrapper_bindable_functions(self) -> str: + """Generates Embind bindings for all functions that need wrappers.""" + + result = "" + for func in self.wrapper_bind_functions: + result += code_builder.INDENT + result += self._generate_function_binding(func, True) + + return result + + def generate(self) -> tuple[str, str]: + """Generates the bindings file for all functions.""" + + wrapper_functions = self._generate_wrappers() + function_bindings = self._generate_direct_bindable_functions() + function_bindings += self._generate_wrapper_bindable_functions() + + return wrapper_functions, function_bindings diff --git a/wasm/codegen/generators/functions_test.py b/wasm/codegen/generators/functions_test.py new file mode 100644 index 00000000..e1ac87a3 --- /dev/null +++ b/wasm/codegen/generators/functions_test.py @@ -0,0 +1,67 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from absl.testing import absltest + +from introspect import ast_nodes + +from wasm.codegen.generators import functions + + +class FunctionsGeneratorTest(absltest.TestCase): + + def setUp(self): + super().setUp() + self.generator = functions.Generator({}) + self.int_type = ast_nodes.ValueType(name="int") + + def test_generate_function_binding_simple_case(self): + func_simple_void = ast_nodes.FunctionDecl( + name="do_nothing", + return_type=ast_nodes.ValueType(name="void"), + parameters=tuple(), + doc="doc", + ) + self.assertEqual( + self.generator._generate_function_binding(func_simple_void), + 'function("do_nothing", &do_nothing);\n', + ) + + def test_generate_direct_bindable_functions_simple_filter(self): + direct_bind = ast_nodes.FunctionDecl( + name="direct_bind", + return_type=self.int_type, + parameters=( + ast_nodes.FunctionParameterDecl(name="val", type=self.int_type), + ), + doc="doc", + ) + needs_wrap = ast_nodes.FunctionDecl( + name="needs_wrap", + return_type=ast_nodes.PointerType(inner_type=self.int_type), + parameters=tuple(), + doc="doc", + ) + self.generator = functions.Generator({ + "direct1": direct_bind, + "wrapped1": needs_wrap, + }) + + generated_code = self.generator._generate_direct_bindable_functions() + self.assertIn('function("direct_bind", &direct_bind);\n', generated_code) + self.assertNotIn('function("needs_wrap", &needs_wrap);\n', generated_code) + + +if __name__ == "__main__": + absltest.main() diff --git a/wasm/codegen/generators/structs.py b/wasm/codegen/generators/structs.py new file mode 100644 index 00000000..062f13ed --- /dev/null +++ b/wasm/codegen/generators/structs.py @@ -0,0 +1,88 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generates Embind bindings for MuJoCo structs.""" + +from typing import Optional + +from wasm.codegen.helpers import constants +from wasm.codegen.helpers import structs_parser +from wasm.codegen.helpers import structs_wrappers_data + + +class Generator: + """Generates C++ code for binding and wrapping MuJoCo structs.""" + + def __init__(self): + # Set up the correct input dict based on the structs we want to bind + # and already have a wrapper manually created in the template/bindings.cc + wrapped_structs = structs_wrappers_data.create_wrapped_structs_set_up_data( + constants.STRUCTS_TO_BIND + ) + + # Traverse the introspect dictionary to get the field + # wrapper/bindings statements set up for each struct + self.structs_to_bind_data = structs_parser.generate_wasm_bindings( + wrapped_structs + ) + + def generate_header( + self + ) -> list[tuple[str, list[Optional[str]]]]: + """Generates C++ header file for binding and wrapping MuJoCo structs.""" + autogenned_struct_definitions = [] + markers_and_content = [] + + # Sort by struct name by dependency to ensure deterministic output order + sorted_struct_names = structs_parser.sort_structs_by_dependency( + constants.STRUCTS_TO_BIND + ) + + for struct_name in sorted_struct_names: + struct_data = self.structs_to_bind_data[struct_name] + if struct_data.wrapped_header: + autogenned_struct_definitions.append( + struct_data.wrapped_header + "\n" + ) + else: + markers_and_content.append(( + f"// INSERT-GENERATED-{struct_data.wrap_name}-DEFINITIONS", + [ + l.definition if l.definition else "" + for l in struct_data.wrapped_fields + ], + )) + markers_and_content.append(( + "// {{ AUTOGENNED_STRUCT_DEFINITIONS }}", + autogenned_struct_definitions, + )) + return markers_and_content + + def generate_source(self) -> list[tuple[str, list[str]]]: + """Generates C++ source file for binding and wrapping MuJoCo structs.""" + constructors = [ + ( + f"// INSERT-GENERATED-{struct_data.wrap_name}-CONSTRUCTOR", + [struct_data.wrapped_source], + ) + for _, struct_data in self.structs_to_bind_data.items() + ] + properties = [ + ( + f"// INSERT-GENERATED-{struct_data.wrap_name}-BINDINGS", + [l.binding for l in struct_data.wrapped_fields], + ) + for _, struct_data in self.structs_to_bind_data.items() + ] + return constructors + properties diff --git a/wasm/codegen/helpers/code_builder.py b/wasm/codegen/helpers/code_builder.py new file mode 100644 index 00000000..93779bef --- /dev/null +++ b/wasm/codegen/helpers/code_builder.py @@ -0,0 +1,73 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Helper class to build code string line by line with indentation.""" + +INDENT = " " + + +class CodeBuilder: + """Helper class to build code string line by line with indentation.""" + + def __init__(self, indent_str: str = INDENT): + self._lines = [] + self._indent_level = 0 + self._indent_str = indent_str + + def line(self, line_content: str) -> None: + """Adds a line with indentation, special-casing "private:" and "public:".""" + indent = self._indent_str * self._indent_level + content = line_content.strip() + if content == "private:" or content == "public:": + self._lines.append(indent[:-1] + line_content) + elif content: + self._lines.append(indent + line_content) + else: + self._lines.append("") + + def to_string(self) -> str: + """Returns the complete code string.""" + return "\n".join(self._lines) + + class IndentBlock: + """Helper class to manage indentation within a `with` statement.""" + + def __init__(self, builder: "CodeBuilder", header_line=""): + self._builder = builder + self._header_line = header_line + + def __enter__(self): + line = self._header_line + line += " {" if line else "{" + self._builder.line(line) + self._builder._indent_level += 1 + return self._builder + + def __exit__(self, exc_type, exc_val, exc_tb): + if self._builder._indent_level > 0: + self._builder._indent_level -= 1 + self._builder.line("}") + + def block(self, header_line="") -> IndentBlock: + """Creates a block including braces and an optional header before the opening brace. + + Use via a `with` statement. + + Args: + header_line: Optional header line to add before the opening brace. + + Returns: + An IndentBlock instance that manages the indentation. + """ + return self.IndentBlock(self, header_line) diff --git a/wasm/codegen/helpers/code_builder_test.py b/wasm/codegen/helpers/code_builder_test.py new file mode 100644 index 00000000..a1b0783a --- /dev/null +++ b/wasm/codegen/helpers/code_builder_test.py @@ -0,0 +1,51 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for the code_builder module.""" + +from absl.testing import absltest +from wasm.codegen.helpers import code_builder + + +class CodeBuilderTest(absltest.TestCase): + + def test_code_builder_functionality(self): + """Test nested indentation blocks.""" + builder = code_builder.CodeBuilder(indent_str=" ") + builder.line("let a = 1") + with builder.block("function myFunc()"): + builder.line("let flag = true") + with builder.block("while (flag)"): + builder.line("a++") + builder.line("flag = a < 10") + builder.line("return a") + builder.line("print('Done')") + + expected_lines = [ + "let a = 1", + "function myFunc() {", + " let flag = true", + " while (flag) {", + " a++", + " flag = a < 10", + " }", + " return a", + "}", + "print('Done')", + ] + self.assertEqual(builder.to_string(), "\n".join(expected_lines)) + + +if __name__ == "__main__": + absltest.main() diff --git a/wasm/codegen/helpers/common.py b/wasm/codegen/helpers/common.py new file mode 100644 index 00000000..1aa0598a --- /dev/null +++ b/wasm/codegen/helpers/common.py @@ -0,0 +1,128 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utility functions for code generation.""" + +import os +import pathlib + +from wasm.codegen.helpers import constants + +Path = pathlib.Path + + +def get_default_output_dir() -> str: + """Gets the default output directory (sibling of 'generated' folder).""" + # Get the directory of the current file (generator/base.py) + current_dir = Path(__file__).parent + # Go up one level to the project root and then down to 'generated' + default_output_dir = str(current_dir.parent / "generated") + return default_output_dir + + +def get_file_path( + template_dir: str, output_dir: str, filename: str +) -> tuple[str, str]: + """Constructs the template and output file paths. + + Args: + template_dir: The directory containing the template files. + output_dir: The directory where the generated files will be saved. + filename: The name of the file. + + Returns: + A tuple containing the template file path and the output file path. + """ + template_file = f"wasm/codegen/{template_dir}/{filename}" + output_file = f"wasm/codegen/{output_dir}/{filename}" + return template_file, output_file + + +def write_to_file(filepath: str, content: str) -> None: + """Writes content to a file.""" + output_dir = os.path.dirname(filepath) + + try: + if output_dir: + os.makedirs(output_dir, exist_ok=True) + with open(filepath, "w") as f: + chars = f.write(content) + print(f"wrote {chars} characters to file '{filepath}'") + except IOError as e: + print(f"Error writing to output file: {filepath} - {e}") + + +def uppercase_first_letter(input_string: str) -> str: + """Uppercases the first letter of a string.""" + if input_string: + return input_string[0].upper() + input_string[1:] + return input_string + + +def try_cast_to_scalar_type(value: str) -> int | float | str: + """Tries to cast a string to an integer, then a float, otherwise returns the original string.""" + for type_ in [int, float]: + try: + return type_(value) + except ValueError: + continue + return value + + +def debug_print(msg: str): + """Prints a message to the console if STRUCT_DEBUG_MODE is enabled.""" + if constants.STRUCT_DEBUG_MODE: + print(msg) + + +def replace_lines_containing_marker( + lines: list[str], + marker_to_replace: str, + replacement_content: str | list[str], +) -> list[str]: + """Replaces lines containing a specific marker with new content.""" + + new_lines = [] + replaced = False + for line in lines: + if not replaced and marker_to_replace in line: + indentation = _get_indentation(line) + if isinstance(replacement_content, str): + new_lines.append(indentation + replacement_content) + elif isinstance(replacement_content, list): + for content_line in replacement_content: + if not content_line.strip(): + continue + indented_line = ( + indentation + + content_line.replace("\n", "\n" + indentation) + + "\n" + ) + new_lines.append(indented_line) + replaced = True + else: + new_lines.append(line) + return new_lines + + +def _get_indentation(line: str) -> str: + """Returns the indentation of the given line as a string of spaces.""" + + indentation = "" + for char in line: + if char == " ": + indentation += " " + else: + break + return indentation diff --git a/wasm/codegen/helpers/common_test.py b/wasm/codegen/helpers/common_test.py new file mode 100644 index 00000000..cdb3d4f4 --- /dev/null +++ b/wasm/codegen/helpers/common_test.py @@ -0,0 +1,37 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from absl.testing import absltest +from wasm.codegen.helpers import common + + +class CommonUtilsTest(absltest.TestCase): + + def test_uppercase_first_letter(self): + self.assertEqual(common.uppercase_first_letter(""), "") + self.assertEqual(common.uppercase_first_letter("hello"), "Hello") + self.assertEqual(common.uppercase_first_letter("1st place"), "1st place") + self.assertEqual(common.uppercase_first_letter("!wow"), "!wow") + self.assertEqual( + common.uppercase_first_letter(" leading space"), " leading space" + ) + + def test_try_cast_to_scalar_type(self): + self.assertEqual(common.try_cast_to_scalar_type("123"), 123) + self.assertEqual(common.try_cast_to_scalar_type("123.456"), 123.456) + self.assertEqual(common.try_cast_to_scalar_type("abc"), "abc") + + +if __name__ == "__main__": + absltest.main() diff --git a/wasm/codegen/helpers/constants.py b/wasm/codegen/helpers/constants.py new file mode 100644 index 00000000..32b1fdfe --- /dev/null +++ b/wasm/codegen/helpers/constants.py @@ -0,0 +1,474 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Constants used in the code generation process.""" + +from introspect import structs as introspect_structs + +PRIMITIVE_TYPES = { + # go/keep-sorted start + "char", + "double", + "float", + "int", + "mjtByte", + "mjtMeshBuiltin", + "mjtNum", + "mjtObj", # Adding this to the primitives because it is used as int, + "mjtSize", + "size_t", + "uint64_t", + "uintptr_t", + "unsigned char", + "unsigned int", + "void", + # go/keep-sorted end +} + +_PLUGIN_FUNCTIONS = [ + # go/keep-sorted start + "mj_getPluginConfig", + "mj_loadAllPluginLibraries", + "mj_loadPluginLibrary", + "mjc_distance", + "mjc_getSDF", + "mjc_gradient", + "mjp_defaultPlugin", + "mjp_defaultResourceProvider", + "mjp_getPlugin", + "mjp_getPluginAtSlot", + "mjp_getResourceProvider", + "mjp_getResourceProviderAtSlot", + "mjp_pluginCount", + "mjp_registerPlugin", + "mjp_registerResourceProvider", + "mjp_resourceProviderCount", + # go/keep-sorted end +] + +# Functions that are bound as class methods +_CLASS_METHODS = [ + # go/keep-sorted start + "mj_compile", + "mj_copyData", + "mj_copyModel", + "mj_copySpec", + "mj_deleteData", + "mj_deleteModel", + "mj_deleteSpec", + "mj_loadXML", + "mj_makeData", + "mj_makeSpec", + "mj_parseXML", # TODO(manevi): Bind this function. + "mj_parseXMLString", + "mj_recompile", # TODO(manevi): Bind this function. + "mj_saveXML", # TODO(manevi): Bind this function. + "mj_saveXMLString", # TODO(manevi): Bind this function. + # go/keep-sorted end +] + +# Omitted because not very useful +_WRITABLE_ERROR = [ + "mj_printSchema", +] + +# Omitted thread management functions +_THREAD_FUNCTIONS = [ + # go/keep-sorted start + "mju_bindThreadPool", + "mju_defaultTask", + "mju_taskJoin", + "mju_threadPoolCreate", + "mju_threadPoolDestroy", + "mju_threadPoolEnqueue", + # go/keep-sorted end +] + +# Omitted asset cache functions +_ASSET_CACHE_FUNCTIONS = [ + # go/keep-sorted start + "mj_clearCache", + "mj_getCache", + "mj_getCacheCapacity", + "mj_getCacheSize", + "mj_setCacheCapacity", + # go/keep-sorted end +] + +# Omitted Virtual Filesystem (VFS) functions +_VFS_FUNCTIONS = [ + # go/keep-sorted start + "mj_addBufferVFS", + "mj_addFileVFS", + "mj_defaultVFS", + "mj_deleteFileVFS", + "mj_deleteVFS", + # go/keep-sorted end +] + +# Omitted irrelevant visual functions +_VISUAL_FUNCTIONS = [ + # go/keep-sorted start + "mjv_averageCamera", + "mjv_copyData", + "mjv_copyModel", + "mjv_defaultScene", + "mjv_freeScene", + "mjv_makeScene", + # go/keep-sorted end +] + +_MEMORY_FUNCTIONS = [ + # go/keep-sorted start + "mj_freeLastXML", + "mj_freeStack", + "mj_loadModel", + "mj_markStack", + "mj_saveModel", + "mj_stackAllocByte", + "mj_stackAllocInt", + "mj_stackAllocNum", + "mj_warning", + "mjs_bodyToFrame", + "mju_boxQPmalloc", + "mju_clearHandlers", + "mju_error", + "mju_error_i", + "mju_error_s", + "mju_free", + "mju_malloc", + "mju_strncpy", + "mju_warning", + "mju_warning_i", + "mju_warning_s", + # go/keep-sorted end +] + +_GETTERS_AND_SETTERS = [ + # go/keep-sorted start + "mjs_appendFloatVec", + "mjs_appendIntVec", + "mjs_appendString", + "mjs_getDouble", + "mjs_getPluginAttributes", + "mjs_getString", + "mjs_getUserValue", + "mjs_setBuffer", + "mjs_setDouble", + "mjs_setFloat", + "mjs_setInStringVec", + "mjs_setInt", + "mjs_setPluginAttributes", + "mjs_setString", + "mjs_setStringVec", + "mjs_setUserValue", + # go/keep-sorted end +] + +_UTILITY_FUNCTIONS = [ + # go/keep-sorted start + "mju_getXMLDependencies", + # go/keep-sorted end +] + +# List of functions that should be skipped during the code generation process. +SKIPPED_FUNCTIONS = ( + _CLASS_METHODS + + _THREAD_FUNCTIONS + + _MEMORY_FUNCTIONS + + _PLUGIN_FUNCTIONS + + _GETTERS_AND_SETTERS + + _VISUAL_FUNCTIONS + + _ASSET_CACHE_FUNCTIONS + + _VFS_FUNCTIONS + + _WRITABLE_ERROR + + _UTILITY_FUNCTIONS +) + +# Functions that require special wrappers to infer sizes and make additional +# validation checks. These functions are not bound automatically but are +# written by hand instead. +BOUNDCHECK_FUNCS = [ + # go/keep-sorted start + "mj_addM", + "mj_angmomMat", + "mj_applyFT", + "mj_constraintUpdate", + "mj_differentiatePos", + "mj_fullM", + "mj_geomDistance", + "mj_getState", + "mj_integratePos", + "mj_jac", + "mj_jacBody", + "mj_jacBodyCom", + "mj_jacDot", + "mj_jacGeom", + "mj_jacPointAxis", + "mj_jacSite", + "mj_jacSubtreeCom", + "mj_mulJacTVec", + "mj_mulJacVec", + "mj_mulM", + "mj_mulM2", + "mj_multiRay", + "mj_normalizeQuat", + "mj_rne", + "mj_saveLastXML", + "mj_setLengthRange", + "mj_setState", + "mj_solveM", + "mj_solveM2", + "mjd_inverseFD", + "mjd_subQuat", + "mjd_transitionFD", + "mju_L1", + "mju_add", + "mju_addScl", + "mju_addTo", + "mju_addToScl", + "mju_band2Dense", + "mju_bandMulMatVec", + "mju_boxQP", + "mju_cholFactor", + "mju_cholFactorBand", + "mju_cholSolve", + "mju_cholSolveBand", + "mju_cholUpdate", + "mju_copy", + "mju_d2n", + "mju_decodePyramid", + "mju_dense2Band", + "mju_dense2sparse", + "mju_dot", + "mju_encodePyramid", + "mju_eye", + "mju_f2n", + "mju_fill", + "mju_insertionSort", + "mju_insertionSortInt", + "mju_isZero", + "mju_mulMatMat", + "mju_mulMatMatT", + "mju_mulMatTMat", + "mju_mulMatTVec", + "mju_mulMatVec", + "mju_mulVecMatVec", + "mju_n2d", + "mju_n2f", + "mju_norm", + "mju_normalize", + "mju_printMatSparse", + "mju_scl", + "mju_sparse2dense", + "mju_sqrMatTD", + "mju_sub", + "mju_subFrom", + "mju_sum", + "mju_symmetrize", + "mju_transpose", + "mju_zero", + # go/keep-sorted end +] + +# List of structs that should be skipped during the code generation process. +SKIPPED_STRUCTS = [ + # go/keep-sorted start + "mjCache", + "mjSDF", + "mjTask", + "mjThreadPool", + "mjUI", + "mjrContext", + "mjrRect", + "mjuiDef", + "mjuiItem", + "mjuiSection", + "mjuiState", + "mjuiThemeColor", + "mjuiThemeSpacing", + # go/keep-sorted end +] + +# Dictionary that maps anonymous structs to their parent struct and field name. +# Anonymous structs are not defined as independent structs in the MuJoCo +# codebase, but they are part of other structs. This dictionary is used to +# handle them as if they were independent structs. +ANONYMOUS_STRUCTS = { + # go/keep-sorted start + "mjVisualGlobal": {"parent": "mjVisual", "field_name": "global"}, + "mjVisualHeadlight": {"parent": "mjVisual", "field_name": "headlight"}, + "mjVisualMap": {"parent": "mjVisual", "field_name": "map"}, + "mjVisualQuality": {"parent": "mjVisual", "field_name": "quality"}, + "mjVisualRgba": {"parent": "mjVisual", "field_name": "rgba"}, + "mjVisualScale": {"parent": "mjVisual", "field_name": "scale"}, + # go/keep-sorted end +} + +# This list is created by subtracting the skipped structs from the list of all +# structs and adding the anonymous structs. +STRUCTS_TO_BIND = list( + (set(introspect_structs.STRUCTS.keys()) - set(SKIPPED_STRUCTS)).union( + ANONYMOUS_STRUCTS.keys() + ) +) + +# List of structs that do not have a default constructor. +NO_DEFAULT_CONSTRUCTORS = [ + # go/keep-sorted start + "mjContact", + "mjSolverStat", + "mjStatistic", + "mjTimerStat", + "mjWarningStat", + "mjsCompiler", + "mjsDefault", + "mjsElement", + "mjsExclude", + "mjsWrap", + "mjvGLCamera", + "mjvLight", + # go/keep-sorted end +] + +# List of `mjData` fields where the array size should be obtained from other +# `mjData` members, instead of from `mjModel` members. This is typically the +# case for fields that are dynamically allocated during the simulation. +MJDATA_SIZES = [ + # go/keep-sorted start + "contact", + "efc_AR", + "efc_AR_colind", + "efc_AR_rowadr", + "efc_AR_rownnz", + "efc_D", + "efc_J", + "efc_JT", + "efc_JT_colind", + "efc_J_colind", + "efc_J_rowadr", + "efc_J_rownnz", + "efc_J_rowsuper", + "efc_KBIP", + "efc_R", + "efc_aref", + "efc_b", + "efc_diagApprox", + "efc_force", + "efc_frictionloss", + "efc_id", + "efc_island", + "efc_margin", + "efc_pos", + "efc_state", + "efc_type", + "efc_vel", + "iLDiagInv", + "iM_rowadr", + "iM_rownnz", + "iacc", + "iacc_smooth", + "iefc_D", + "iefc_J", + "iefc_JT", + "iefc_JT_colind", + "iefc_JT_rowadr", + "iefc_JT_rownnz", + "iefc_JT_rowsuper", + "iefc_J_colind", + "iefc_J_rowadr", + "iefc_J_rownnz", + "iefc_J_rowsuper", + "iefc_R", + "iefc_aref", + "iefc_force", + "iefc_frictionloss", + "iefc_id", + "iefc_state", + "iefc_type", + "ifrc_constraint", + "ifrc_smooth", + "island_dofadr", + "island_dofnum", + "island_efcadr", + "island_efcind", + "island_efcnum", + "island_idofadr", + "island_iefcadr", + "island_ne", + "island_nefc", + "island_nf", + "island_nv", + "map_efc2iefc", + "map_iefc2efc", + # go/keep-sorted end +] + +# Dictionary where keys are the struct names and the values are lists of the +# fields that are manually specified in the structs.h template file. +MANUALLY_ADDED_FIELDS_FROM_TEMPLATE = { + # go/keep-sorted start + "MjData": ["solver", "timer", "warning", "contact"], + "MjSpec": ["option", "visual", "stat", "element", "compiler"], + "MjvScene": [ + "model", + "lights", + "camera", + "geoms", + "geomorder", + "flexedgeadr", + "flexedgenum", + "flexvertadr", + "flexvertnum", + "flexfaceadr", + "flexfacenum", + "flexfaceused", + "flexedge", + "flexvert", + "skinfacenum", + "skinvertadr", + "skinvertnum", + "skinvert", + "skinnormal", + "flexface", + "flexnormal", + "flextexcoord", + # go/keep-sorted end + ], +} + +# Dictionary that maps byte array fields to their corresponding size members. +# When generating the code for these fields, a specific cast to `uint8_t*` is +# required for embind. This dictionary is used to register those fields and +# their sizes. +BYTE_FIELDS = { + "buffer": {"size": "nbuffer"}, + "arena": {"size": "narena"}, +} + +# Boolean flag to enable debug prints during the struct wrapper and binding +# generation process. When set to `True`, it will print additional information +# about the steps being executed. +STRUCT_DEBUG_MODE = False + +# These structs require specific function calls for creation and/or deletion, +# or some of their fields need to be handled manually for now; +# making their wrapper constructors/destructors non-trivial. +HARDCODED_WRAPPER_STRUCTS = [ + "MjData", + "MjModel", + "MjvScene", + "MjSpec", + "MjVisual", +] diff --git a/wasm/codegen/helpers/function_utils.py b/wasm/codegen/helpers/function_utils.py new file mode 100644 index 00000000..a4003fe4 --- /dev/null +++ b/wasm/codegen/helpers/function_utils.py @@ -0,0 +1,350 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Helper functions for processing and generating bindings for MuJoCo functions.""" + +from typing import List, Set, Tuple, cast + +from introspect import ast_nodes + +from wasm.codegen.helpers import code_builder +from wasm.codegen.helpers import common +from wasm.codegen.helpers import constants + + +PRIMITIVE_TYPES = constants.PRIMITIVE_TYPES +uppercase_first_letter = common.uppercase_first_letter + + +def param_is_primitive_value(param: ast_nodes.FunctionParameterDecl) -> bool: + """Checks if param is a primitive value type.""" + if isinstance(param.type, ast_nodes.ValueType): + return param.type.name in PRIMITIVE_TYPES + return False + + +def param_is_pointer_to_primitive_value( + param: ast_nodes.FunctionParameterDecl, +) -> bool: + """Checks if param is a pointer to a primitive value.""" + return ( + isinstance(param.type, ast_nodes.PointerType) + or isinstance(param.type, ast_nodes.ArrayType) + ) and ( + isinstance(param.type.inner_type, ast_nodes.ValueType) + and param.type.inner_type.name in PRIMITIVE_TYPES + ) + + +def param_is_pointer_to_struct(param: ast_nodes.FunctionParameterDecl) -> bool: + """Checks if param is a pointer to a struct.""" + return ( + isinstance(param.type, ast_nodes.PointerType) + or isinstance(param.type, ast_nodes.ArrayType) + ) and ( + isinstance(param.type.inner_type, ast_nodes.ValueType) + and param.type.inner_type.name not in PRIMITIVE_TYPES + ) + + +def return_is_value_of_type( + func: ast_nodes.FunctionDecl, allowed_types: Set[str] +) -> bool: + """Checks if func returns an allowed value type.""" + return ( + isinstance(func.return_type, ast_nodes.ValueType) + and func.return_type.name in allowed_types + ) + + +def return_is_pointer_to_struct(func: ast_nodes.FunctionDecl) -> bool: + """Checks if func returns a pointer to a struct.""" + return ( + isinstance(func.return_type, ast_nodes.PointerType) + and isinstance(func.return_type.inner_type, ast_nodes.ValueType) + and func.return_type.inner_type.name not in PRIMITIVE_TYPES + ) + + +def return_is_pointer_to_primitive(func: ast_nodes.FunctionDecl) -> bool: + """Checks if func returns a pointer to a primitive value.""" + return ( + isinstance(func.return_type, ast_nodes.PointerType) + and isinstance(func.return_type.inner_type, ast_nodes.ValueType) + and func.return_type.inner_type.name in PRIMITIVE_TYPES + ) + + +def get_const_qualifier(func: ast_nodes.FunctionDecl) -> str: + """Returns the const qualifier of func's return type.""" + if ( + isinstance(func.return_type, ast_nodes.PointerType) + and isinstance(func.return_type.inner_type, ast_nodes.ValueType) + and func.return_type.inner_type.is_const + ): + return "const " + return "" + + +def should_be_wrapped(func: ast_nodes.FunctionDecl) -> bool: + """Checks if a MuJoCo function needs a wrapper function.""" + return ( + return_is_pointer_to_primitive(func) + or return_is_pointer_to_struct(func) + or any( + param_is_pointer_to_primitive_value(param) + or isinstance(param.type, ast_nodes.ArrayType) + or param_is_pointer_to_struct(param) + for param in func.parameters + ) + ) + + +def generate_function_wrapper(func: ast_nodes.FunctionDecl) -> str: + """Generates C++ code for a wrapper function.""" + + builder = code_builder.CodeBuilder() + # Build function header + params_unpack_statements = get_params_unpack_statements(func.parameters) + wrapper_params_list = get_params_string(func.parameters) + not_nullable_params = get_params_notnullable(func.parameters) + wrapper_params_str = ", ".join(wrapper_params_list) + ret_type = get_compatible_return_type(func) + builder.line(f"{ret_type} {func.name}_wrapper({wrapper_params_str})") + + # Build function body + with builder.block(): + invoker_params_list = get_params_string_maybe_with_conversion( + func.parameters + ) + invoker_params_str = ", ".join(invoker_params_list) + invoker_call = f"{func.name}({invoker_params_str})" + invoker_statement = get_compatible_return_call(func, invoker_call) + for p in not_nullable_params: + builder.line(f"CHECK_VAL({p});") + for unpack_statement in params_unpack_statements: + builder.line(unpack_statement) + builder.line(f"{invoker_statement};") + return builder.to_string() + + +def get_params_notnullable( + ast_params: Tuple[ast_nodes.FunctionParameterDecl, ...], +) -> List[str]: + """Generates list of param names for checking if they aren't null/undefined.""" + + not_nullable_params = [] + for p in ast_params: + if ( + isinstance(p.type, (ast_nodes.PointerType, ast_nodes.ArrayType)) + and isinstance(p.type.inner_type, ast_nodes.ValueType) + # We only check for char because others are checked in the unpacker + # and we don't want to check twice. + and p.type.inner_type.name == "char" + and not p.nullable + ): + not_nullable_params.append(p.name) + return not_nullable_params + + +def get_params_unpack_statements( + ast_params: Tuple[ast_nodes.FunctionParameterDecl, ...], +) -> List[str]: + """Generates C++ statements to unpack JS values for pointer/array parameters.""" + + params_unpack_statements = [] + for p in ast_params: + if ( + isinstance(p.type, (ast_nodes.PointerType, ast_nodes.ArrayType)) + and isinstance(p.type.inner_type, ast_nodes.ValueType) + and p.type.inner_type.name in PRIMITIVE_TYPES + ): + if p.type.inner_type.name == "char": + # param is Javascript string + continue + + if p.type.inner_type.is_const: + # param is Javascript number[] + params_unpack_statements.append( + f"UNPACK_ARRAY({p.type.inner_type.name}, {p.name});" + ) + else: + # param is TypedArray or a WasmBuffer + params_unpack_statements.append( + f"UNPACK_VALUE({p.type.inner_type.name}, {p.name});" + ) + return params_unpack_statements + + +def get_params_string( + parameters: Tuple[ast_nodes.FunctionParameterDecl, ...] +) -> List[str]: + """Generates a list of C++ parameter declarations as strings.""" + + result = [] + for p in parameters: + if ( + isinstance(p.type, ast_nodes.PointerType) + and isinstance(p.type.inner_type, ast_nodes.ValueType) + and p.type.inner_type.name not in PRIMITIVE_TYPES + ): + # Pointer to struct parameters + const_qualifier = "const " if p.type.inner_type.is_const else "" + result.append( + f"{const_qualifier}{uppercase_first_letter(p.type.inner_type.name)}&" + f" {p.name}" + ) + elif ( + isinstance(p.type, ast_nodes.ValueType) + and p.type.name in PRIMITIVE_TYPES + ): + # Primitive value parameters + const_qualifier = "const " if p.type.is_const else "" + result.append(f"{const_qualifier}{p.type} {p.name}") + elif ( + isinstance(p.type, (ast_nodes.PointerType, ast_nodes.ArrayType)) + and isinstance(p.type.inner_type, ast_nodes.ValueType) + and p.type.inner_type.name in PRIMITIVE_TYPES + ): + # Pointer to primitive value parameters or arrays + if p.type.inner_type.name == "char": + if p.nullable: + result.append(f"const NullableString& {p.name}") + else: + result.append(f"const String& {p.name}") + elif ( + p.type.inner_type.name + in ["int", "float", "double", "mjtNum", "mjtByte"] + and p.type.inner_type.is_const + ): + result.append(f"const NumberArray& {p.name}") + else: + result.append(f"const val& {p.name}") + else: + # This case should ideally not be reached if AST is well-formed + # and types are categorized by the helper booleans correctly. + raise TypeError( + "Unable to generate param string. Unhandled parameter type:" + f" {p.type} for param '{p.name}'" + ) + return result + + +def get_params_string_maybe_with_conversion( + ast_params: Tuple[ast_nodes.FunctionParameterDecl, ...], +) -> List[str]: + """Generates C++ expressions for passing compatible params from JS to MuJoCo C-API functions.""" + + native_params = [] + for p in ast_params: + if param_is_pointer_to_struct(p): + native_params.append(f"{p.name}.get()") + elif param_is_primitive_value(p): + native_params.append(p.name) + elif ( + isinstance(p.type, (ast_nodes.PointerType, ast_nodes.ArrayType)) + and isinstance(p.type.inner_type, ast_nodes.ValueType) + and p.type.inner_type.name in PRIMITIVE_TYPES + and p.type.inner_type.name != "char" + ): + native_params.append(f"{p.name}_.data()") + elif ( + isinstance(p.type, (ast_nodes.PointerType, ast_nodes.ArrayType)) + and isinstance(p.type.inner_type, ast_nodes.ValueType) + and p.type.inner_type.name == "char" + ): + const_qualifier = "const " if p.type.inner_type.is_const else "" + native_params.append( + f"{p.name}.as<{const_qualifier}std::string>().data()" + ) + else: + raise TypeError( + f"Unhandled parameter type for conversion: {p.type} for param" + f" '{p.name}'" + ) + return native_params + + +def get_compatible_return_call( + func: ast_nodes.FunctionDecl, invoker: str +) -> str: + """Generates embind compatible return value conversion.""" + + if return_is_value_of_type(func, {"void"}): + return invoker + if isinstance(func.return_type, ast_nodes.PointerType) and isinstance( + func.return_type.inner_type, ast_nodes.ValueType + ): + if func.return_type.inner_type.name == "char": + return f"return std::string({invoker})" + elif func.return_type.inner_type.name == "mjString": + return f"return *{invoker}" + if return_is_pointer_to_struct(func): + return get_converted_struct_to_class(func, invoker) + if return_is_value_of_type(func, PRIMITIVE_TYPES): + return f"return {invoker}" + raise RuntimeError( + "Failed to calculate return value conversion for function" + f" {func.name} that returns '{func.return_type}'" + ) + + +def get_compatible_return_type(func: ast_nodes.FunctionDecl) -> str: + """Creates embind compatible return type.""" + + if ( + isinstance(func.return_type, ast_nodes.PointerType) + and isinstance(func.return_type.inner_type, ast_nodes.ValueType) + and func.return_type.inner_type.name in ["char", "mjString"] + ): + return "std::string" + if ( + isinstance(func.return_type, ast_nodes.PointerType) + and isinstance(func.return_type.inner_type, ast_nodes.ValueType) + and func.return_type.inner_type.name not in PRIMITIVE_TYPES + ): + const_qualifier = get_const_qualifier(func) + return f"""{const_qualifier}std::optional<{uppercase_first_letter(func.return_type.inner_type.name)}>""" + if ( + isinstance(func.return_type, ast_nodes.ValueType) + and func.return_type.name in PRIMITIVE_TYPES + ): + return f"{func.return_type.name}" + return "val" + + +def get_converted_struct_to_class( + func: ast_nodes.FunctionDecl, invoker: str +) -> str: + """Generates a C++ function invocation for a struct return-type function.""" + + const_qualifier = get_const_qualifier(func) + return_type = cast(ast_nodes.PointerType, func.return_type) + struct_name = cast(ast_nodes.ValueType, return_type.inner_type).name + class_constructor = uppercase_first_letter(struct_name) + return_str = f"{class_constructor}(result)" + return f"""{const_qualifier}{struct_name}* result = {invoker}; + if (result == nullptr) {{ + return std::nullopt; + }} + return {return_str}""" + + +def is_excluded_function_name(func_name: str) -> bool: + """Checks if a function name should be excluded from direct binding.""" + return ( + func_name.startswith("mjr_") + or func_name.startswith("mjui_") + or func_name in constants.SKIPPED_FUNCTIONS + ) diff --git a/wasm/codegen/helpers/function_utils_test.py b/wasm/codegen/helpers/function_utils_test.py new file mode 100644 index 00000000..30e6ee80 --- /dev/null +++ b/wasm/codegen/helpers/function_utils_test.py @@ -0,0 +1,236 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TypeAlias + +from absl.testing import absltest +from introspect import ast_nodes + +from wasm.codegen.helpers import constants +from wasm.codegen.helpers import function_utils + + +PrimitiveTypes: TypeAlias = constants.PRIMITIVE_TYPES +ValueType: TypeAlias = ast_nodes.ValueType +PointerType: TypeAlias = ast_nodes.PointerType +ArrayType: TypeAlias = ast_nodes.ArrayType +FunctionParameterDecl: TypeAlias = ast_nodes.FunctionParameterDecl +FunctionDecl: TypeAlias = ast_nodes.FunctionDecl + + +class FunctionUtilsTest(absltest.TestCase): + + def setUp(self): + super().setUp() + self.struct_type = ValueType("MyStruct") + self.ptr_to_int = PointerType(ValueType("int")) + self.func_ret_ptr_int = FunctionDecl( + "func_pi", PointerType(ValueType("int")), [], "doc" + ) + self.func_ret_ptr_struct = FunctionDecl( + "func_ps", PointerType(ValueType("MyStruct")), [], "doc" + ) + + def test_return_is_value_of_type(self): + self.assertTrue( + function_utils.return_is_value_of_type( + FunctionDecl("func_i", ValueType("int"), [], "doc"), PrimitiveTypes + ) + ) + self.assertFalse( + function_utils.return_is_value_of_type( + FunctionDecl("func_s", ValueType("MyStruct"), [], "doc"), + PrimitiveTypes, + ) + ) + + def test_return_is_pointer_to_struct(self): + self.assertTrue( + function_utils.return_is_pointer_to_struct(self.func_ret_ptr_struct) + ) + self.assertFalse( + function_utils.return_is_pointer_to_struct(self.func_ret_ptr_int) + ) + + def test_return_is_pointer_to_primitive(self): + self.assertTrue( + function_utils.return_is_pointer_to_primitive(self.func_ret_ptr_int) + ) + self.assertFalse( + function_utils.return_is_pointer_to_primitive(self.func_ret_ptr_struct) + ) + + def test_param_is_primitive_value(self): + param_prim_val = FunctionParameterDecl("prim_v", ValueType("int")) + param_arr = FunctionParameterDecl( + "arr_v", ArrayType(ValueType("int"), extents=(10,)) + ) + + self.assertTrue(function_utils.param_is_primitive_value(param_prim_val)) + self.assertFalse(function_utils.param_is_primitive_value(param_arr)) + + def test_param_is_pointer_to_primitive_value(self): + param_ptr_to_prim = FunctionParameterDecl("p_prim", self.ptr_to_int) + param_arr_of_prim = FunctionParameterDecl( + "a_prim", ArrayType(ValueType("int"), extents=(10,)) + ) + param_ptr_to_struct = FunctionParameterDecl( + name="p_struct", type=PointerType(inner_type=self.struct_type) + ) + self.assertTrue( + function_utils.param_is_pointer_to_primitive_value(param_ptr_to_prim) + ) + self.assertTrue( + function_utils.param_is_pointer_to_primitive_value(param_arr_of_prim) + ) + self.assertFalse( + function_utils.param_is_pointer_to_primitive_value(param_ptr_to_struct) + ) + + def test_param_is_pointer_to_struct(self): + param_arr_of_struct = FunctionParameterDecl( + "a_struct", ArrayType(self.struct_type, extents=(5,)) + ) + param_ptr_to_struct = FunctionParameterDecl( + "p_struct", PointerType(self.struct_type) + ) + param_ptr_to_ptr = FunctionParameterDecl( + "p_ptr", PointerType(self.ptr_to_int) + ) + self.assertTrue( + function_utils.param_is_pointer_to_struct(param_arr_of_struct) + ) + self.assertTrue( + function_utils.param_is_pointer_to_struct(param_ptr_to_struct) + ) + self.assertFalse( + function_utils.param_is_pointer_to_struct(param_ptr_to_ptr) + ) + + def test_should_be_wrapped_with_primitive_ptr_return(self): + func = FunctionDecl( + name="get_data", + return_type=PointerType(ValueType("int")), + parameters=tuple(), + doc="Returns int pointer", + ) + self.assertTrue(function_utils.should_be_wrapped(func)) + + def test_generate_function_wrapper_for_simple_func(self): + func = FunctionDecl( + name="get_id", + return_type=ValueType("int"), + parameters=tuple(), + doc="Returns an integer ID", + ) + result = function_utils.generate_function_wrapper(func) + self.assertEqual(result, """int get_id_wrapper() +{ + return get_id(); +}""") + + def test_generate_function_wrapper_checking_param(self): + parameters = ( + FunctionParameterDecl( + name="mat", + type=PointerType( + inner_type=ValueType(name="mjtNum", is_const=True), + ), + ), + FunctionParameterDecl( + name="nr", + type=ValueType(name="int"), + ), + ) + func = FunctionDecl( + name="get_id", + return_type=ValueType("int"), + parameters=parameters, + doc="Returns an integer ID", + ) + result = function_utils.generate_function_wrapper(func) + self.assertEqual( + result, + """int get_id_wrapper(const NumberArray& mat, int nr) +{ + UNPACK_ARRAY(mjtNum, mat); + return get_id(mat_.data(), nr); +}""", + ) + + def test_get_params_string_with_struct_ptr(self): + param = FunctionParameterDecl( + name="my_struct", + type=PointerType(ValueType("mystruct")), + ) + result = function_utils.get_params_string((param,)) + self.assertEqual(result, ["Mystruct& my_struct"]) + + def test_get_params_string_maybe_with_conversion_struct_ptr(self): + param = FunctionParameterDecl( + name="s", + type=PointerType(ValueType("customstruct")), + ) + result = function_utils.get_params_string_maybe_with_conversion((param,)) + self.assertEqual(result, ["s.get()"]) + + def test_get_compatible_return_call(self): + func = FunctionDecl( + name="noop", + return_type=ValueType("void"), + parameters=tuple(), + doc="does nothing", + ) + result = function_utils.get_compatible_return_call(func, "noop()") + self.assertEqual(result, "noop()") + + def test_get_compatible_return_type(self): + func = FunctionDecl( + name="get_name", + return_type=PointerType(ValueType("char")), + parameters=tuple(), + doc="returns name", + ) + result = function_utils.get_compatible_return_type(func) + self.assertEqual(result.strip(), "std::string") + + def test_get_converted_struct_to_class(self): + func = FunctionDecl( + name="get_struct", + return_type=PointerType(ValueType("mystruct")), + parameters=tuple(), + doc="returns struct", + ) + result = function_utils.get_converted_struct_to_class(func, "get_struct()") + self.assertIn("mystruct* result = get_struct();", result) + self.assertIn("return Mystruct(result)", result) + + def test_is_excluded_function_name(self): + self.assertTrue(function_utils.is_excluded_function_name("mjr_function")) + self.assertTrue(function_utils.is_excluded_function_name("mjui_function")) + self.assertTrue(function_utils.is_excluded_function_name("mju_malloc")) + self.assertTrue(function_utils.is_excluded_function_name("mj_makeData")) + self.assertFalse( + function_utils.is_excluded_function_name("mjv_updateScene") + ) + self.assertFalse( + function_utils.is_excluded_function_name("mj_normalFunction") + ) + self.assertFalse( + function_utils.is_excluded_function_name("mju_someOtherFunction") + ) + + +if __name__ == "__main__": + absltest.main() diff --git a/wasm/codegen/helpers/struct_constructor_code_builder.py b/wasm/codegen/helpers/struct_constructor_code_builder.py new file mode 100644 index 00000000..74194b96 --- /dev/null +++ b/wasm/codegen/helpers/struct_constructor_code_builder.py @@ -0,0 +1,195 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Code builder for struct constructor code.""" + +from typing import List, cast + +from introspect import ast_nodes +from introspect import structs as introspect_structs + +from wasm.codegen.helpers import code_builder +from wasm.codegen.helpers import common +from wasm.codegen.helpers import constants +from wasm.codegen.helpers import structs_wrappers_data + + +def _has_nested_wrapper_members( + struct_info: ast_nodes.StructDecl +) -> bool: + """Checks if the struct contains other wrapped structs as direct members.""" + for field in struct_info.fields: + struct_field = cast(ast_nodes.StructFieldDecl, field) + if isinstance(struct_field.type, ast_nodes.ValueType): + if struct_field.type.name in constants.STRUCTS_TO_BIND: + return True + if isinstance(struct_field.type, ast_nodes.ArrayType): + if isinstance(struct_field.type.inner_type, ast_nodes.ValueType): + if struct_field.type.inner_type.name in constants.STRUCTS_TO_BIND: + return True + if isinstance(struct_field.type, ast_nodes.PointerType): + if isinstance(struct_field.type.inner_type, ast_nodes.ValueType): + if struct_field.type.inner_type.name in constants.STRUCTS_TO_BIND: + return True + return False + + +def _build_struct_header_internal( + struct_name: str, + wrapped_fields: List[structs_wrappers_data.WrappedFieldData], + fields_with_init: List[structs_wrappers_data.WrappedFieldData], + use_shallow_copy: bool = False, + is_mjs: bool = False, +): + """Builds the C++ header file code for a struct.""" + wrapper_name = common.uppercase_first_letter(struct_name) + builder = code_builder.CodeBuilder() + with builder.block(f"struct {wrapper_name}"): + if not is_mjs: + builder.line(f"{wrapper_name}();") + builder.line(f"{wrapper_name}(const {wrapper_name} &);") + builder.line(f"{wrapper_name} &operator=(const {wrapper_name} &);") + + builder.line(f"explicit {wrapper_name}({struct_name} *ptr);") + builder.line(f"~{wrapper_name}();") + + if use_shallow_copy: + builder.line(f"std::unique_ptr<{wrapper_name}> copy();") + + for field in wrapped_fields: + if field.definition and field not in fields_with_init: + for line in field.definition.splitlines(): + builder.line(line) + + builder.line(f"{struct_name}* get() const {{ return ptr_; }}") + builder.line(f"void set({struct_name}* ptr) {{ ptr_ = ptr; }}") + builder.line("") + builder.line("private:") + builder.line(f"{struct_name}* ptr_;") + builder.line("bool owned_ = false;") + + if is_mjs and fields_with_init: + builder.line("") + builder.line("public:") + for field in fields_with_init: + if field.definition: + builder.line(f"{field.definition}") + return builder.to_string()+";" + + +def build_struct_header( + struct_name: str, + use_shallow_copy: bool = False, + fields_with_init: List[structs_wrappers_data.WrappedFieldData] = [], + wrapped_fields: List[structs_wrappers_data.WrappedFieldData] = [], +): + """Builds the C++ header file code for a struct.""" + struct_info = introspect_structs.STRUCTS.get(struct_name) + + if struct_name.startswith("mjs"): + return _build_struct_header_internal( + struct_name, + wrapped_fields, + fields_with_init, + use_shallow_copy, + is_mjs=True, + ) + + if ( + ( + common.uppercase_first_letter(struct_name) + not in constants.HARDCODED_WRAPPER_STRUCTS + ) + and struct_info + and not _has_nested_wrapper_members(struct_info) + ): + return _build_struct_header_internal( + struct_name, wrapped_fields, [], use_shallow_copy, is_mjs=False + ) + return "" + + +def build_struct_source( + struct_name: str, + mj_default_func: str | None = None, + fields_with_init: List[structs_wrappers_data.WrappedFieldData] = [], + use_shallow_copy: bool = False, +): + """Builds the C++ .cc file code for a struct.""" + wrapper_name = common.uppercase_first_letter(struct_name) + is_mjs_struct = "Mjs" in wrapper_name + builder = code_builder.CodeBuilder() + + fields_init = "" + if fields_with_init: + fields_init = "".join( + field_with_init.initialization + for field_with_init in fields_with_init + ) + # constructor passing native ptr + builder.line( + f"{wrapper_name}::{wrapper_name}({struct_name} *ptr) :" + f" ptr_(ptr){fields_init} {{}}" + ) + # constructor with default values + if not is_mjs_struct: + with builder.block( + f"{wrapper_name}::{wrapper_name}() : ptr_(new" + f" {struct_name}){fields_init}" + ): + builder.line("owned_ = true;") + if mj_default_func: + builder.line(f"{mj_default_func}(ptr_);") + # copy constructor + if use_shallow_copy and not is_mjs_struct: + with builder.block( + f"{wrapper_name}::{wrapper_name}(const" + f" {wrapper_name} &other)" + + (f" : {wrapper_name}()" if not is_mjs_struct else "") + ): + builder.line("*ptr_ = *other.get();") + if fields_with_init: + for field_with_init in fields_with_init: + if field_with_init.ptr_copy_reset is not None: + builder.line(field_with_init.ptr_copy_reset) + # assignment operator + with builder.block( + f"{wrapper_name}&" + f" {wrapper_name}::operator=(const" + f" {wrapper_name} &other)" + ): + with builder.block("if (this == &other)"): + builder.line("return *this;") + builder.line("*ptr_ = *other.get();") + if fields_with_init: + for field_with_init in fields_with_init: + if field_with_init.ptr_copy_reset is not None: + builder.line(field_with_init.ptr_copy_reset) + builder.line("return *this;") + # destructor + if is_mjs_struct: + builder.line(f"{wrapper_name}::~{wrapper_name}() {{}}") + else: + with builder.block(f"{wrapper_name}::~{wrapper_name}()"): + builder.line("if (owned_ && ptr_) delete ptr_;") + # copy function + if use_shallow_copy: + with builder.block( + f"std::unique_ptr<{wrapper_name}>" + f" {wrapper_name}::copy()" + ): + builder.line( + f"return std::make_unique<{wrapper_name}>(*this);" + ) + return builder.to_string() diff --git a/wasm/codegen/helpers/struct_constructor_code_builder_test.py b/wasm/codegen/helpers/struct_constructor_code_builder_test.py new file mode 100644 index 00000000..72c906d3 --- /dev/null +++ b/wasm/codegen/helpers/struct_constructor_code_builder_test.py @@ -0,0 +1,140 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from absl.testing import absltest +from introspect import ast_nodes +from wasm.codegen.helpers import struct_constructor_code_builder +from wasm.codegen.helpers import struct_field_handler + +StructFieldDecl = ast_nodes.StructFieldDecl +ValueType = ast_nodes.ValueType +PointerType = ast_nodes.PointerType +ArrayType = ast_nodes.ArrayType +StructDecl = ast_nodes.StructDecl + + +class StructConstructorCodeBuilderTest(absltest.TestCase): + + def test_constructor_code_with_default_function(self): + self.assertEqual( + struct_constructor_code_builder.build_struct_source( + "mjLROpt", "mj_defaultLROpt" + ), + """ +MjLROpt::MjLROpt(mjLROpt *ptr) : ptr_(ptr) {} +MjLROpt::MjLROpt() : ptr_(new mjLROpt) { + owned_ = true; + mj_defaultLROpt(ptr_); +} +MjLROpt::~MjLROpt() { + if (owned_ && ptr_) delete ptr_; +} +""".strip(), + ) + + def test_constructor_code_without_default_function(self): + self.assertEqual( + struct_constructor_code_builder.build_struct_source("mjLROpt"), + """ +MjLROpt::MjLROpt(mjLROpt *ptr) : ptr_(ptr) {} +MjLROpt::MjLROpt() : ptr_(new mjLROpt) { + owned_ = true; +} +MjLROpt::~MjLROpt() { + if (owned_ && ptr_) delete ptr_; +} +""".strip(), + ) + + def test_constructor_code_with_fields_with_init(self): + field_with_init = StructFieldDecl( + name="element", + type=PointerType( + inner_type=ValueType(name="mjsElement"), + ), + doc="", + ) + wrapped_field_data = struct_field_handler.StructFieldHandler( + field_with_init, "MjsTexture" + ).generate() + self.assertEqual( + struct_constructor_code_builder.build_struct_source( + "mjsTexture", + "mjs_defaultTexture", + [wrapped_field_data], + ), + """ +MjsTexture::MjsTexture(mjsTexture *ptr) : ptr_(ptr), element(ptr_->element) {} +MjsTexture::~MjsTexture() {} +""".strip(), + ) + + def test_constructor_code_with_shallow_copy(self): + self.assertEqual( + struct_constructor_code_builder.build_struct_source( + "mjvLight", use_shallow_copy=True + ), + """MjvLight::MjvLight(mjvLight *ptr) : ptr_(ptr) {} +MjvLight::MjvLight() : ptr_(new mjvLight) { + owned_ = true; +} +MjvLight::MjvLight(const MjvLight &other) : MjvLight() { + *ptr_ = *other.get(); +} +MjvLight& MjvLight::operator=(const MjvLight &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjvLight::~MjvLight() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjvLight::copy() { + return std::make_unique(*this); +}""".strip(), + ) + + +def test_build_struct_header_with_nested_wrappers(self): + self.assertEqual( + struct_constructor_code_builder.build_struct_header("mjData"), + "", + ) + + +def test_build_struct_header_basic_struct(self): + self.assertEqual( + struct_constructor_code_builder.build_struct_header("mjLROpt"), + """ +struct MjLROpt { + MjLROpt(); + MjLROpt(const MjLROpt &); + MjLROpt &operator=(const MjLROpt &); + explicit MjLROpt(mjLROpt *ptr); + ~MjLROpt(); + mjLROpt* get() const { return ptr_; } + void set(mjLROpt* ptr) { ptr_ = ptr; } + + private: + mjLROpt* ptr_; + bool owned_ = false; +}; +""".strip(), + ) + + +if __name__ == "__main__": + absltest.main() diff --git a/wasm/codegen/helpers/struct_field_code_builder.py b/wasm/codegen/helpers/struct_field_code_builder.py new file mode 100644 index 00000000..b8919348 --- /dev/null +++ b/wasm/codegen/helpers/struct_field_code_builder.py @@ -0,0 +1,100 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Class to build the C++ code for a struct field wrapper.""" + +from introspect import ast_nodes +from wasm.codegen.helpers import code_builder + +StructFieldDecl = ast_nodes.StructFieldDecl +ValueType = ast_nodes.ValueType + + +def build_primitive_type_definition(field: StructFieldDecl) -> str: + """Builds the C++ code for a primitive type field wrapper.""" + if not isinstance(field.type, ValueType): + raise ValueError(f"{field.type} must be ValueType.") + builder = code_builder.CodeBuilder() + # build getter for primitive type field + with builder.block(f"{field.type.name} {field.name}() const"): + builder.line(f"return ptr_->{field.name};") + # build setter for primitive type field + with builder.block(f"void set_{field.name}({field.type.name} value)"): + builder.line(f"ptr_->{field.name} = value;") + return builder.to_string() + + +def build_memory_view_definition( + field: StructFieldDecl, array_size_str: str, ptr_expr: str +) -> str: + """Builds the C++ code for a pointer type field wrapper.""" + builder = code_builder.CodeBuilder() + with builder.block(f"emscripten::val {field.name}() const"): + builder.line( + "return" + f" emscripten::val(emscripten::typed_memory_view({array_size_str}," + f" {ptr_expr}));" + ) + return builder.to_string() + + +def build_string_field_definition(field: StructFieldDecl) -> str: + """Builds the C++ code for a string type field wrapper.""" + builder = code_builder.CodeBuilder() + with builder.block(f"mjString {field.name}() const"): + builder.line( + f'return (ptr_ && ptr_->{field.name}) ? *(ptr_->{field.name}) : "";' + ) + with builder.block(f"void set_{field.name}(const mjString& value)"): + with builder.block(f"if (ptr_ && ptr_->{field.name})"): + builder.line(f"*(ptr_->{field.name}) = value;") + return builder.to_string() + + +def build_mjvec_pointer_definition( + field: StructFieldDecl, vector_type: str +) -> str: + """Builds the C++ code for a mjVec type field wrapper.""" + ptr_field_expr = f"*(ptr_->{field.name})" + if vector_type == "mjByteVec": + vector_type = "std::vector" + ptr_field_expr = ( + f"*(reinterpret_cast*>(ptr_->{field.name}))" + ) + builder = code_builder.CodeBuilder() + with builder.block(f"{vector_type} &{field.name}() const"): + builder.line(f"return {ptr_field_expr};") + return builder.to_string() + + +def build_simple_property_binding( + field: StructFieldDecl, + struct_wrapper_name: str, + add_setter: bool = False, + add_return_value_policy_as_ref: bool = False, +) -> str: + """Builds the C++ code for a simple property binding.""" + builder = code_builder.CodeBuilder() + setter_txt = "" + if add_setter: + setter_txt = f", &{struct_wrapper_name}::set_{field.name}" + if add_return_value_policy_as_ref: + as_reference_txt = ", reference()" + else: + as_reference_txt = "" + builder.line( + f'.property("{field.name}",' + f" &{struct_wrapper_name}::{field.name}{setter_txt}{as_reference_txt})" + ) + return builder.to_string() diff --git a/wasm/codegen/helpers/struct_field_code_builder_test.py b/wasm/codegen/helpers/struct_field_code_builder_test.py new file mode 100644 index 00000000..3a6477c9 --- /dev/null +++ b/wasm/codegen/helpers/struct_field_code_builder_test.py @@ -0,0 +1,171 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from absl.testing import absltest +from introspect import ast_nodes + +from wasm.codegen.helpers import struct_field_code_builder + +StructFieldDecl = ast_nodes.StructFieldDecl +ValueType = ast_nodes.ValueType +PointerType = ast_nodes.PointerType +ArrayType = ast_nodes.ArrayType + + +class StructFieldCodeBuilderTest(absltest.TestCase): + + def test_primitive_type_definition(self): + field = StructFieldDecl( + name="ngeom", + type=ValueType(name="int"), + doc="number of geoms", + ) + self.assertEqual( + struct_field_code_builder.build_primitive_type_definition(field), + """ +int ngeom() const { + return ptr_->ngeom; +} +void set_ngeom(int value) { + ptr_->ngeom = value; +} +""".strip(), + ) + + def test_memory_view_definition(self): + field = StructFieldDecl( + name="geom_rgba", + type=PointerType( + inner_type=ValueType(name="float"), + ), + doc="rgba when material is omitted", + array_extent=("ngeom", 4), + ) + self.assertEqual( + struct_field_code_builder.build_memory_view_definition( + field, "ptr_->ngeom * 4", "ptr_->geom_rgba" + ), + """ +emscripten::val geom_rgba() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 4, ptr_->geom_rgba)); +} +""".strip(), + ) + + def test_string_field_definition(self): + field = StructFieldDecl( + name="string_field", + type=PointerType( + inner_type=ValueType(name="mjString"), + ), + doc="rgba when material is omitted", + ) + self.assertEqual( + struct_field_code_builder.build_string_field_definition(field), + """ +mjString string_field() const { + return (ptr_ && ptr_->string_field) ? *(ptr_->string_field) : ""; +} +void set_string_field(const mjString& value) { + if (ptr_ && ptr_->string_field) { + *(ptr_->string_field) = value; + } +} +""".strip(), + ) + + def test_mjvec_pointer_definition(self): + field = StructFieldDecl( + name="vector_field", + type=PointerType( + inner_type=ValueType(name="mjDoubleVec"), + ), + doc="", + ) + self.assertEqual( + struct_field_code_builder.build_mjvec_pointer_definition( + field, "mjDoubleVec" + ), + """ +mjDoubleVec &vector_field() const { + return *(ptr_->vector_field); +}""".strip(), + ) + + def test_mjbyte_vec_pointer_definition(self): + field = StructFieldDecl( + name="vector_field", + type=PointerType( + inner_type=ValueType(name="mjByteVec"), + ), + doc="", + ) + self.assertEqual( + struct_field_code_builder.build_mjvec_pointer_definition( + field, "mjByteVec" + ), + """ +std::vector &vector_field() const { + return *(reinterpret_cast*>(ptr_->vector_field)); +}""".strip(), + ) + + def test_simple_property_binding(self): + field = StructFieldDecl( + name="ngeom", + type=ValueType(name="int"), + doc="number of geoms", + ) + self.assertEqual( + struct_field_code_builder.build_simple_property_binding( + field, "MjModel" + ), + '.property("ngeom", &MjModel::ngeom)', + ) + + def test_simple_property_binding_with_setter(self): + field = StructFieldDecl( + name="ngeom", + type=ValueType(name="int"), + doc="", + ) + self.assertEqual( + struct_field_code_builder.build_simple_property_binding( + field, "MjModel", True + ), + '.property("ngeom", &MjModel::ngeom,' + " &MjModel::set_ngeom)", + ) + + def test_simple_property_binding_with_return_value_policy_as_ref(self): + field = StructFieldDecl( + name="ngeom", + type=ValueType(name="int"), + doc="", + ) + self.assertEqual( + struct_field_code_builder.build_simple_property_binding( + field, + "MjModel", + add_setter=True, + add_return_value_policy_as_ref=True, + ), + '.property("ngeom", &MjModel::ngeom,' + " &MjModel::set_ngeom," + " reference())", + ) + + +if __name__ == "__main__": + absltest.main() diff --git a/wasm/codegen/helpers/struct_field_handler.py b/wasm/codegen/helpers/struct_field_handler.py new file mode 100644 index 00000000..013a1cc0 --- /dev/null +++ b/wasm/codegen/helpers/struct_field_handler.py @@ -0,0 +1,358 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Class to handle the different struct field types, and provide the c++ code for the wrappers and bindings.""" + +import math +from typing import Tuple, Union, cast +from introspect import ast_nodes +from wasm.codegen.helpers import common +from wasm.codegen.helpers import constants +from wasm.codegen.helpers import struct_field_code_builder +from wasm.codegen.helpers import structs_wrappers_data + +AnonymousStructDecl = ast_nodes.AnonymousStructDecl +ArrayType = ast_nodes.ArrayType +PointerType = ast_nodes.PointerType +StructFieldDecl = ast_nodes.StructFieldDecl +ValueType = ast_nodes.ValueType +WrappedFieldData = structs_wrappers_data.WrappedFieldData + +debug_print = common.debug_print + + +class StructFieldHandler: + """Class to handle the different struct field types, and provide the c++ code for the definitions and bindings.""" + + def __init__( + self, + field: StructFieldDecl, + struct_wrapper_name: str, + ): + self.field = field + self.struct_wrapper_name = struct_wrapper_name + self.simple_property_binding = ( + struct_field_code_builder.build_simple_property_binding( + self.field, self.struct_wrapper_name + ) + ) + self.manually_added_fields = ( + constants.MANUALLY_ADDED_FIELDS_FROM_TEMPLATE.get( + self.struct_wrapper_name, {} + ) + ) + + def generate(self) -> WrappedFieldData: + """Generates the C++ definition and binding code for the struct field.""" + field_type = self.field.type + if isinstance(field_type, ValueType) and ( + field_type.name in constants.PRIMITIVE_TYPES + or field_type.name.startswith("mjt") + ): + return self._handle_primitive() + elif isinstance(field_type, PointerType): + return self._handle_pointer() + elif isinstance(field_type, ArrayType): + return self._handle_array() + elif isinstance(field_type, ValueType) and field_type.name.startswith("mj"): + return self._handle_mj_struct() + elif isinstance(field_type, AnonymousStructDecl): + return self._handle_anonymous_struct() + return self._undefined() + + def _handle_primitive(self) -> WrappedFieldData: + """Handles the generation of C++ definition and binding code for primitive fields.""" + return WrappedFieldData( + definition=( + struct_field_code_builder.build_primitive_type_definition( + self.field + ) + ), + binding=struct_field_code_builder.build_simple_property_binding( + self.field, + self.struct_wrapper_name, + add_setter=True, + add_return_value_policy_as_ref=True, + ), + is_primitive_or_fixed_size=True, + ) + + def _handle_pointer(self) -> WrappedFieldData: + """Handles the generation of C++ definition and binding code for pointer fields.""" + if not isinstance(self.field.type, PointerType): + raise ValueError( + f"Expected PointerType, got {type(self.field.type)} for field" + f" {self.field.name}" + ) + field_type: PointerType = self.field.type + inner_type_name = ( + field_type.inner_type.name + if isinstance(field_type.inner_type, ValueType) + else "" + ) + ptr_field_expr = f"ptr_->{self.field.name}" + array_size_str = "" + + if self.field.array_extent: + array_size_str = parse_array_extent( + self.field.array_extent, self.struct_wrapper_name, self.field.name + ) + elif self.field.name in constants.BYTE_FIELDS.keys(): + # for byte fields, we need to cast the pointer to uint8_t* + # so embind can correctly interpret the memory view + ptr_field_expr = ( + f"static_cast({ptr_field_expr})" + ) + # for these byte fields, there is no array_extent, so we add the size of + # in the config file based in the documentation + extent = (constants.BYTE_FIELDS[self.field.name]["size"],) + array_size_str = parse_array_extent( + extent, self.struct_wrapper_name, self.field.name + ) + elif inner_type_name == "mjString": + return WrappedFieldData( + definition=struct_field_code_builder.build_string_field_definition( + self.field + ), + binding=struct_field_code_builder.build_simple_property_binding( + self.field, + self.struct_wrapper_name, + add_setter=True, + add_return_value_policy_as_ref=True, + ), + ) + elif inner_type_name.startswith("mj") and inner_type_name.endswith("Vec"): + return WrappedFieldData( + definition=struct_field_code_builder.build_mjvec_pointer_definition( + self.field, inner_type_name + ), + binding=struct_field_code_builder.build_simple_property_binding( + self.field, + self.struct_wrapper_name, + add_setter=False, + add_return_value_policy_as_ref=True, + ), + ) + elif inner_type_name in constants.PRIMITIVE_TYPES: + return self._get_manual_definition( + comment_type="primitive pointer field with complex extents" + ) + + if ( + inner_type_name.startswith("mj") + and inner_type_name not in constants.PRIMITIVE_TYPES + ): + debug_print( + f"\tcomplex pointer type: needs manual wrapper: {self.field.name}" + ) + # it's a pointer to a single struct, + # like the `element` field in mjs structs + # and the struct is not manually added + if ( + not self.field.array_extent + and self.struct_wrapper_name + not in constants.MANUALLY_ADDED_FIELDS_FROM_TEMPLATE.keys() + ): + ptr_field = cast(PointerType, self.field.type) + wrapper_field_name = common.uppercase_first_letter( + cast(ValueType, ptr_field.inner_type).name + ) + return WrappedFieldData( + definition=f"{wrapper_field_name} {self.field.name};", + binding=struct_field_code_builder.build_simple_property_binding( + self.field, + self.struct_wrapper_name, + add_setter=False, + add_return_value_policy_as_ref=True, + ), + initialization=f", {self.field.name}(ptr_->{self.field.name})", + ) + else: + debug_print( + "\tcomplex pointer type with array extent: needs manual wrapper:" + f" {self.field.name}" + ) + return self._get_manual_definition(comment_type="complex pointer field") + + return WrappedFieldData( + definition=( + struct_field_code_builder.build_memory_view_definition( + self.field, array_size_str, ptr_field_expr + ) + ), + binding=self.simple_property_binding, + ) + + def _handle_array(self) -> WrappedFieldData: + """Handles the generation of C++ definition and binding code for array fields.""" + field_type = self.field.type + if not isinstance(field_type, ArrayType): + raise ValueError( + f"Expected ArrayType, got {type(field_type)} for field" + f" {self.field.name}" + ) + inner_type = field_type.inner_type + size = math.prod(field_type.extents) + + if isinstance(inner_type, ValueType): + if inner_type.name in constants.PRIMITIVE_TYPES: + ptr_expr = f"ptr_->{self.field.name}" + if len(field_type.extents) > 1: + # for multi-dimensional arrays, we need to cast the field + # to a pointer, so embind can correctly interpret the memory + # view + ptr_expr = f"reinterpret_cast<{inner_type.name}*>({ptr_expr})" + return WrappedFieldData( + definition=( + struct_field_code_builder.build_memory_view_definition( + self.field, str(size), ptr_expr + ) + ), + binding=self.simple_property_binding, + is_primitive_or_fixed_size=True, + ) + elif inner_type.name.startswith("mj") and not inner_type.name.startswith( + "mjt" + ): + debug_print(f"\tarray to vector wrapper needed: {self.field.name}") + return self._get_manual_definition(comment_type="array field") + + debug_print(f"\tNOT IMPLEMENTED ARRAY field: {self.field.name}") + return WrappedFieldData( + definition=( + f"// TODO: NOT IMPLEMENTED ARRAY wrapper for {self.field.name}" + ), + binding=f"// TODO: NOT IMPLEMENTED ARRAY binding for {self.field.name}", + ) + + def _handle_mj_struct(self) -> WrappedFieldData: + """Handles the generation of C++ definition and binding code for mj struct fields.""" + if ( + isinstance(self.field.type, ValueType) + and self.field.name not in self.manually_added_fields + and self.field.type.name in constants.STRUCTS_TO_BIND + ): + # TODO(manevi): Find a better way to do this instead of checking the + # struct wrapper name. + definition = "" + if self.struct_wrapper_name not in constants.HARDCODED_WRAPPER_STRUCTS: + wrapper_field_name = common.uppercase_first_letter(self.field.type.name) + definition = f"{wrapper_field_name} {self.field.name};" + return WrappedFieldData( + definition=definition, + binding=struct_field_code_builder.build_simple_property_binding( + self.field, + self.struct_wrapper_name, + add_setter=False, + add_return_value_policy_as_ref=True, + ), + initialization=f", {self.field.name}(&ptr_->{self.field.name})", + ptr_copy_reset=f"{self.field.name}.set(&ptr_->{self.field.name});", + is_primitive_or_fixed_size=True, + ) + return self._get_manual_definition(comment_type="struct field") + + def _handle_anonymous_struct(self) -> WrappedFieldData: + """Handles the generation of C++ definition and binding code for anonymous struct fields.""" + + anonymous_struct_name = "" + for name, value in constants.ANONYMOUS_STRUCTS.items(): + if ( + common.uppercase_first_letter(value["parent"]) + == self.struct_wrapper_name + and value["field_name"] == self.field.name + ): + anonymous_struct_name = name + break + + if ( + isinstance(self.field.type, AnonymousStructDecl) + and self.field.name not in self.manually_added_fields + and anonymous_struct_name in constants.STRUCTS_TO_BIND + ): + return WrappedFieldData( + binding=struct_field_code_builder.build_simple_property_binding( + self.field, + self.struct_wrapper_name, + add_setter=False, + add_return_value_policy_as_ref=True, + ), + initialization=f", {self.field.name}(&ptr_->{self.field.name})", + ptr_copy_reset=f"{self.field.name}.set(&ptr_->{self.field.name});", + is_primitive_or_fixed_size=True, + ) + return self._get_manual_definition(comment_type="anonymous struct field") + + def _undefined(self) -> WrappedFieldData: + """This function adds a TODO comment for fields that are not handled by this class yet.""" + return WrappedFieldData( + definition=f"// TODO: UNDEFINED definition for {self.field.name}", + binding=f"// TODO: UNDEFINED binding for {self.field.name}", + ) + + def _get_manual_definition(self, comment_type: str = "") -> WrappedFieldData: + """Helper method to generate a comment as a definition for manually added fields.""" + if self.field.name in self.manually_added_fields: + return WrappedFieldData( + definition=( + f"// {comment_type} is defined manually. {self.field.name}" + ), + binding=self.simple_property_binding, + ) + + return WrappedFieldData( + definition=( + f"// TODO: Define {comment_type} manually for {self.field.name}" + ), + binding=f"// TODO: {self.simple_property_binding}", + ) + + +def parse_array_extent( + extents: Tuple[Union[str, int], ...], wrapper_name: str, field_name: str +) -> str: + """Parses the array extent of a field, returning a string representing the resolved extents.""" + if not extents: + return "" + return " * ".join( + resolve_extent(extent, wrapper_name, field_name) for extent in extents + ) + + +def resolve_extent( + extent: Union[str, int], wrapper_name: str, field_name: str +) -> str: + """Resolves the extent of an array, handling integers and references to other struct fields. + + Args: + extent: The extent to resolve, can be an int or a string referencing a + field. + wrapper_name: The name of the struct wrapper. + field_name: The name of the field being processed. + + Returns: + A string representing the resolved extent, either as a number or a field + reference. + """ + if isinstance(extent, int): + return str(extent) + # if starts with mj, it's a mujoco constant, + # so we don't need to get a parent struct ptr + if extent.startswith("mj"): + return str(extent) + if wrapper_name == "MjData" and field_name not in constants.MJDATA_SIZES: + var_name = "model" + else: + var_name = "ptr_" + return f"{var_name}->{extent}" diff --git a/wasm/codegen/helpers/struct_field_handler_test.py b/wasm/codegen/helpers/struct_field_handler_test.py new file mode 100644 index 00000000..45eac91e --- /dev/null +++ b/wasm/codegen/helpers/struct_field_handler_test.py @@ -0,0 +1,241 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from absl.testing import absltest +from introspect import ast_nodes + +from wasm.codegen.helpers import struct_field_handler + + +StructFieldDecl = ast_nodes.StructFieldDecl +ValueType = ast_nodes.ValueType +PointerType = ast_nodes.PointerType +ArrayType = ast_nodes.ArrayType + + +class StructFieldHandlerTest(absltest.TestCase): + + def test_scalar_field(self): + """Test that a scalar type field is handled correctly.""" + field_scalar = StructFieldDecl( + name='ngeom', + type=ValueType(name='int'), + doc='number of geoms', + ) + + field_handler_scalar = struct_field_handler.StructFieldHandler( + field_scalar, 'MjModel' + ) + wrapped_field_data = field_handler_scalar.generate() + self.assertEqual( + wrapped_field_data.definition, + """ +int ngeom() const { + return ptr_->ngeom; +} +void set_ngeom(int value) { + ptr_->ngeom = value; +} +""".strip(), + ) + self.assertEqual( + wrapped_field_data.binding, + '.property("ngeom", &MjModel::ngeom, &MjModel::set_ngeom, reference())', + ) + + def test_pointer_type_field(self): + """Test that a pointer type field is handled correctly.""" + field = StructFieldDecl( + name='geom_rgba', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='rgba when material is omitted', + array_extent=('ngeom', 4), + ) + wrapped_field_data = struct_field_handler.StructFieldHandler( + field, 'MjModel' + ).generate() + + self.assertEqual( + wrapped_field_data.definition, + (""" +emscripten::val geom_rgba() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 4, ptr_->geom_rgba)); +} +""".strip()), + ) + + self.assertEqual( + wrapped_field_data.binding, + '.property("geom_rgba", &MjModel::geom_rgba)', + ) + + def test_pointer_type_field_for_byte_type(self): + """Test that a pointer type field for a byte type is handled correctly.""" + field = StructFieldDecl( + name='buffer', + type=PointerType( + inner_type=ValueType(name='void'), + ), + doc='main buffer; all pointers point in it (nbuffer bytes)', + ) + wrapped_field_data = struct_field_handler.StructFieldHandler( + field, 'MjData' + ).generate() + + self.assertEqual( + wrapped_field_data.definition, + (""" +emscripten::val buffer() const { + return emscripten::val(emscripten::typed_memory_view(model->nbuffer, static_cast(ptr_->buffer))); +} +""".strip()), + ) + assert ( + wrapped_field_data.binding + == '.property("buffer", &MjData::buffer)' + ) + + def test_pointer_type_field_for_mj_struct(self): + """Test that a pointer type field for a mj struct is handled correctly.""" + field = StructFieldDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + doc='', + ) + wrapped_field_data = struct_field_handler.StructFieldHandler( + field, 'MjsTexture' + ).generate() + self.assertEqual(wrapped_field_data.definition, "MjsElement element;") + + self.assertEqual( + wrapped_field_data.binding, + '.property("element", &MjsTexture::element, reference())', + ) + self.assertEqual( + wrapped_field_data.initialization, + ', element(ptr_->element)', + ) + + def test_array_type_field(self): + """Test that an array type field is handled correctly.""" + field = StructFieldDecl( + name='gravity', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(3,), + ), + doc='gravitational acceleration', + ) + wrapped_field_data = struct_field_handler.StructFieldHandler( + field, 'MjOption' + ).generate() + + self.assertEqual( + wrapped_field_data.definition, + (""" +emscripten::val gravity() const { + return emscripten::val(emscripten::typed_memory_view(3, ptr_->gravity)); +} +""".strip()), + ) + self.assertEqual( + wrapped_field_data.binding, + '.property("gravity", &MjOption::gravity)', + ) + + def test_array_field_with_multi_dimensional_array(self): + """Test that multi-dimensional arrays are handled correctly.""" + field = StructFieldDecl( + name='multi_dim_array', + type=ArrayType( + inner_type=ValueType(name='float'), + extents=(3, 4), + ), + doc='description', + ) + wrapped_field_data = struct_field_handler.StructFieldHandler( + field, 'MjModel' + ).generate() + self.assertEqual( + wrapped_field_data.definition, + """ +emscripten::val multi_dim_array() const { + return emscripten::val(emscripten::typed_memory_view(12, reinterpret_cast(ptr_->multi_dim_array))); +} +""".strip(), + ) + self.assertEqual( + wrapped_field_data.binding, + '.property("multi_dim_array", &MjModel::multi_dim_array)', + ) + + def test_parse_array_extent(self): + """Test that parse_array_extent handles various cases correctly.""" + self.assertEqual( + struct_field_handler.parse_array_extent((1, 2), 'MjModel', 'geom_rgba'), + '1 * 2', + ) + self.assertEqual( + struct_field_handler.parse_array_extent( + (1, 'ngeom'), 'MjModel', 'geom_rgba' + ), + '1 * ptr_->ngeom', + ) + self.assertEqual( + struct_field_handler.parse_array_extent( + (1, 'mjConstant'), 'MjModel', 'geom_rgba' + ), + '1 * mjConstant', + ) + + def test_resolve_extent(self): + """Test that resolve_extent handles various cases correctly.""" + # for integer just return the number + self.assertEqual( + struct_field_handler.resolve_extent(1, 'MjModel', 'geom_rgba'), '1' + ) + + # for string that does not start with mj, it's a member of the struct + self.assertEqual( + struct_field_handler.resolve_extent('ngeom', 'MjModel', 'geom_rgba'), + 'ptr_->ngeom', + ) + + # when it's MjData and the field is in MJDATA_SIZES, it should use ptr_-> + self.assertEqual( + struct_field_handler.resolve_extent('size_value', 'MjData', 'efc_AR'), + 'ptr_->size_value', + ) + # when it's MjData and the field is not in MJDATA_SIZES, + # it should use model-> + self.assertEqual( + struct_field_handler.resolve_extent( + 'size_value', 'MjData', 'data_field' + ), + 'model->size_value', + ) + self.assertEqual( + struct_field_handler.resolve_extent( + 'mjConstant', 'MjModel', 'model_field' + ), + 'mjConstant', + ) + + +if __name__ == '__main__': + absltest.main() diff --git a/wasm/codegen/helpers/structs_parser.py b/wasm/codegen/helpers/structs_parser.py new file mode 100644 index 00000000..05e4f5a8 --- /dev/null +++ b/wasm/codegen/helpers/structs_parser.py @@ -0,0 +1,201 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Parser for MuJoCo structs.""" + +import collections +from typing import Dict, List + +from introspect import ast_nodes +from introspect import structs + +from wasm.codegen.helpers import common +from wasm.codegen.helpers import constants +from wasm.codegen.helpers import struct_constructor_code_builder +from wasm.codegen.helpers import struct_field_handler +from wasm.codegen.helpers import structs_wrappers_data + + +WrappedFieldData = structs_wrappers_data.WrappedFieldData +WrappedStructData = structs_wrappers_data.WrappedStructData +StructFieldHandler = struct_field_handler.StructFieldHandler + +AnonymousStructDecl = ast_nodes.AnonymousStructDecl +StructFieldDecl = ast_nodes.StructFieldDecl +debug_print = common.debug_print + +introspect_structs = structs.STRUCTS + + +def generate_wasm_bindings( + wrapped_structs: Dict[str, WrappedStructData], +) -> Dict[str, WrappedStructData]: + """Generates WASM bindings for MuJoCo structs.""" + + for struct_name, wrap_data in wrapped_structs.items(): + if struct_name in introspect_structs: + struct_fields = introspect_structs[struct_name].fields + elif struct_name in constants.ANONYMOUS_STRUCTS: + anonymous_struct = _get_anonymous_struct_field(struct_name) + if not anonymous_struct or not isinstance( + anonymous_struct.type, AnonymousStructDecl + ): + raise RuntimeError(f"Anonymous struct not found: {struct_name}") + struct_fields = anonymous_struct.type.fields + else: + raise RuntimeError(f"Struct not found: {struct_name}") + + debug_print(f"Wrapping struct: {struct_name}") + + fields_with_init: List[WrappedFieldData] = [] + for field in struct_fields: + field_gen = StructFieldHandler(field, wrap_data.wrap_name).generate() + # If the struct has at least one non-primitive or fixed size field + # we avoid shallow copy to avoid uninitialized memory. + if not field_gen.is_primitive_or_fixed_size: + wrap_data.use_shallow_copy = False + if field_gen.initialization: + fields_with_init.append(field_gen) + wrap_data.wrapped_fields.append(field_gen) + + wrap_data.wrapped_header = ( + struct_constructor_code_builder.build_struct_header( + struct_name, + wrap_data.use_shallow_copy, + fields_with_init, + wrap_data.wrapped_fields, + ) + ) + wrap_data.wrapped_source = ( + struct_constructor_code_builder.build_struct_source( + struct_name, + get_default_func_name(struct_name), + fields_with_init, + wrap_data.use_shallow_copy, + ) + ) + return wrapped_structs + + +def _get_anonymous_struct_field( + anonymous_structs_key: str, +) -> StructFieldDecl | None: + """Looks up the given key in the anonymous_structs dict and generates bindings for its fields.""" + info = constants.ANONYMOUS_STRUCTS[anonymous_structs_key] + parent_decl = introspect_structs[info["parent"]] + target_field = next( + ( + f + for f in parent_decl.fields + if hasattr(f, "name") + and f.name == info["field_name"] + and hasattr(f, "type") + and isinstance(f.type, AnonymousStructDecl) + ), + None, + ) + return target_field + + +def get_default_func_name(struct_name: str) -> str: + """Returns the default function name for the given struct.""" + if ( + struct_name in constants.ANONYMOUS_STRUCTS.keys() + or struct_name in constants.NO_DEFAULT_CONSTRUCTORS + or ( + common.uppercase_first_letter(struct_name) + in constants.MANUALLY_ADDED_FIELDS_FROM_TEMPLATE.keys() + ) + ): + return "" + elif struct_name.startswith("mjs"): + return f"mjs_default{struct_name.removeprefix('mjs')}" + elif struct_name.startswith("mjv"): + return f"mjv_default{struct_name.removeprefix('mjv')}" + else: + return f"mj_default{struct_name.removeprefix('mj')}" + + +def _get_field_struct_type(field_type): + """Extracts the base struct name if the field type is a struct or pointer to a struct.""" + if isinstance(field_type, ast_nodes.ValueType): + return field_type.name + if isinstance(field_type, ast_nodes.PointerType): + if isinstance(field_type.inner_type, ast_nodes.ValueType): + return field_type.inner_type.name + return None + + +def sort_structs_by_dependency(struct_names: List[str]) -> List[str]: + """Sorts structs based on their field dependencies using topological sort. + + Structs with no dependencies on other structs in the list come first. + If struct A has a field of type struct B, B must come before A in the + sorted list. + + Args: + struct_names: A list of struct names to sort. + + Returns: + A new list of struct names sorted by dependency. + + Raises: + RuntimeError: If a cyclic dependency is detected. + """ + adj = collections.defaultdict(list) + in_degree = collections.defaultdict(int) + struct_set = set(struct_names) + sorted_struct_names = sorted(struct_names) + + for struct_name in sorted_struct_names: + if struct_name not in introspect_structs: + # Skip anonymous or other structs not in the main introspect map + continue + + struct_decl = introspect_structs[struct_name] + for field in struct_decl.fields: + if isinstance(field, ast_nodes.AnonymousStructDecl): + continue + + field_type_name = _get_field_struct_type(field.type) + if ( + field_type_name + and field_type_name != struct_name + and field_type_name in struct_set + ): + if struct_name not in adj[field_type_name]: + adj[field_type_name].append(struct_name) + in_degree[struct_name] += 1 + + queue = collections.deque( + [name for name in sorted_struct_names if in_degree[name] == 0] + ) + sorted_list = [] + + while queue: + u = queue.popleft() + sorted_list.append(u) + for v in adj[u]: + in_degree[v] -= 1 + if in_degree[v] == 0: + queue.append(v) + + if len(sorted_list) == len(struct_names): + return sorted_list + else: + remaining = set(struct_names) - set(sorted_list) + raise RuntimeError( + "Cycle detected in struct dependencies, involving: " + f"{', '.join(sorted(list(remaining)))}" + ) diff --git a/wasm/codegen/helpers/structs_parser_test.py b/wasm/codegen/helpers/structs_parser_test.py new file mode 100644 index 00000000..efacd730 --- /dev/null +++ b/wasm/codegen/helpers/structs_parser_test.py @@ -0,0 +1,110 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for structs_parser.""" + +from absl.testing import absltest +from introspect import ast_nodes +from introspect import structs as introspect_structs +from wasm.codegen.helpers import structs_parser +from wasm.codegen.helpers import structs_wrappers_data + + +class StructsParserTest(absltest.TestCase): + + def setUp(self): + super().setUp() + self.wrapped_structs = structs_parser.generate_wasm_bindings( + structs_wrappers_data.create_wrapped_structs_set_up_data([ + "mjModel", + "mjData", + "mjVisualGlobal", + "mjVisualQuality", + "mjVisual", + ]) + ) + + def test_sort_structs_by_dependency(self): + mock_introspect_structs = { + "mjA": ast_nodes.StructDecl( + name="mjA", + declname="mjA", + fields=[ + ast_nodes.StructFieldDecl( + doc="", name="b_field", type=ast_nodes.ValueType(name="mjB") + ) + ], + ), + "mjB": ast_nodes.StructDecl( + name="mjB", + declname="mjB", + fields=[ + ast_nodes.StructFieldDecl( + doc="", name="c_field", type=ast_nodes.ValueType(name="mjC") + ) + ], + ), + "mjC": ast_nodes.StructDecl(name="mjC", declname="mjC", fields=[]), + "mjD": ast_nodes.StructDecl(name="mjD", declname="mjD", fields=[]), + } + with absltest.mock.patch.dict( + introspect_structs.STRUCTS, mock_introspect_structs + ): + struct_names = ["mjA", "mjB", "mjC", "mjD"] + sorted_names = structs_parser.sort_structs_by_dependency(struct_names) + self.assertEqual(sorted_names, ["mjC", "mjD", "mjB", "mjA"]) + + def test_generate_wasm_bindings(self): + self.assertEqual(self.wrapped_structs["mjModel"].wrap_name, "MjModel") + self.assertEqual(self.wrapped_structs["mjData"].wrap_name, "MjData") + self.assertEqual( + self.wrapped_structs["mjVisualGlobal"].wrap_name, "MjVisualGlobal" + ) + self.assertEqual( + self.wrapped_structs["mjVisualQuality"].wrap_name, "MjVisualQuality" + ) + self.assertEqual(self.wrapped_structs["mjVisual"].wrap_name, "MjVisual") + self.assertNotEmpty(self.wrapped_structs["mjModel"].wrapped_fields) + self.assertNotEmpty(self.wrapped_structs["mjData"].wrapped_fields) + self.assertNotEmpty(self.wrapped_structs["mjVisualGlobal"].wrapped_fields) + self.assertNotEmpty(self.wrapped_structs["mjVisualQuality"].wrapped_fields) + self.assertNotEmpty(self.wrapped_structs["mjVisual"].wrapped_fields) + + def test_generate_wasm_bindings_with_error(self): + with self.assertRaises(RuntimeError): + structs_parser.generate_wasm_bindings( + structs_wrappers_data.create_wrapped_structs_set_up_data( + ["mjFakeStruct"] + ) + ) + with self.assertRaises(RuntimeError): + structs_parser.generate_wasm_bindings( + structs_wrappers_data.create_wrapped_structs_set_up_data( + ["mjFakeAnonymousStruct"] + ) + ) + + def test_get_default_func_name_mjv(self): + self.assertEqual( + structs_parser.get_default_func_name("mjvPerturb"), "mjv_defaultPerturb" + ) + + def test_get_default_func_name(self): + self.assertEqual( + structs_parser.get_default_func_name("mjOption"), "mj_defaultOption" + ) + + +if __name__ == "__main__": + absltest.main() diff --git a/wasm/codegen/helpers/structs_wrappers_data.py b/wasm/codegen/helpers/structs_wrappers_data.py new file mode 100644 index 00000000..f6f6d308 --- /dev/null +++ b/wasm/codegen/helpers/structs_wrappers_data.py @@ -0,0 +1,75 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Classes used by the parser to generate the structs wrappers and bindings statements.""" + +import dataclasses +from typing import Dict, List + +from wasm.codegen.helpers import common + + +@dataclasses.dataclass +class WrappedFieldData: + """Data class for struct field definition and binding.""" + + # Line for struct field binding + binding: str + + # Line for struct field definition + definition: str | None = None + + # Initialization code for fields that require it + initialization: str | None = None + + # Statement to reset the inner pointer when copying the field + ptr_copy_reset: str | None = None + + # Whether the field is a primitive or fixed size + is_primitive_or_fixed_size: bool = False + + +@dataclasses.dataclass +class WrappedStructData: + """Data class for struct wrapper definition and binding.""" + + # Name of wrapper struct + wrap_name: str + + # List of WrappedFieldData for this struct + wrapped_fields: List[WrappedFieldData] + + # Struct header code + wrapped_header: str + + # Struct source code + wrapped_source: str + + # Whether to use shallow copy for this struct + use_shallow_copy: bool = True + + +def create_wrapped_structs_set_up_data( + struct_names: List[str], +) -> Dict[str, WrappedStructData]: + """Creates a dictionary of WrappedStructData for the given struct names.""" + return { + name: WrappedStructData( + wrap_name=common.uppercase_first_letter(name), + wrapped_fields=[], + wrapped_header="", + wrapped_source="", + ) + for name in struct_names + } diff --git a/wasm/codegen/templates/bindings.cc b/wasm/codegen/templates/bindings.cc new file mode 100644 index 00000000..f5b30881 --- /dev/null +++ b/wasm/codegen/templates/bindings.cc @@ -0,0 +1,1711 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// NOLINTBEGIN(whitespace/line_length) +// NOLINTBEGIN(whitespace/semicolon) + +#include "third_party/mujoco/wasm/codegen/generated/bindings.h" + +#include +#include + +#include +#include +#include +#include // NOLINT + +#include +#include +#include +#include // NOLINT +#include // NOLINT +#include + +#include +#include +#include +#include "engine/engine_util_errmem.h" +#include "unpack.h" + +namespace mujoco::wasm { + +using emscripten::enum_; +using emscripten::class_; +using emscripten::function; +using emscripten::val; +using emscripten::constant; +using emscripten::register_optional; +using emscripten::register_type; +using emscripten::register_vector; +using emscripten::return_value_policy::reference; +using emscripten::return_value_policy::take_ownership; + +// ERROR HANDLER +void ThrowMujocoErrorToJS(const char* msg) { + // Get a handle to the JS global Error constructor function, create a new + // object instance and then throw the object as an exception using the + // val::throw_() helper function. + val(val::global("Error").new_(val("MuJoCo Error: " + std::string(msg)))) + .throw_(); +} +__attribute__((constructor)) void InitMuJoCoErrorHandler() { + mju_user_error = ThrowMujocoErrorToJS; +} + +// CONSTANTS +template +val MakeValArray(const char* (&strings)[N]) { + val result = val::array(); + for (int i = 0; i < N; i++) { + result.call("push", val(strings[i])); + } + return result; +} + +template +val MakeValArray3(const char* (&strings)[N][M]) { + val result = val::array(); + for (int i = 0; i < N; i++) { + val inner = val::array(); + for (int j = 0; j < M; j++) { + inner.call("push", val(strings[i][j])); + } + result.call("push", inner); + } + return result; +} + +val get_mjDISABLESTRING() { return MakeValArray(mjDISABLESTRING); } +val get_mjENABLESTRING() { return MakeValArray(mjENABLESTRING); } +val get_mjTIMERSTRING() { return MakeValArray(mjTIMERSTRING); } +val get_mjLABELSTRING() { return MakeValArray(mjLABELSTRING); } +val get_mjFRAMESTRING() { return MakeValArray(mjFRAMESTRING); } +val get_mjVISSTRING() { return MakeValArray3(mjVISSTRING); } +val get_mjRNDSTRING() { return MakeValArray3(mjRNDSTRING); } + +EMSCRIPTEN_BINDINGS(constants) { + // from mjmodel.h + constant("mjPI", mjPI); + constant("mjMAXVAL", mjMAXVAL); + constant("mjMINMU", mjMINMU); + constant("mjMINIMP", mjMINIMP); + constant("mjMAXIMP", mjMAXIMP); + constant("mjMAXCONPAIR", mjMAXCONPAIR); + constant("mjNEQDATA", mjNEQDATA); + constant("mjNDYN", mjNDYN); + constant("mjNGAIN", mjNGAIN); + constant("mjNBIAS", mjNBIAS); + constant("mjNREF", mjNREF); + constant("mjNIMP", mjNIMP); + constant("mjNSOLVER", mjNSOLVER); + + // from mjvisualize.h + constant("mjNGROUP", mjNGROUP); + constant("mjMAXLIGHT", mjMAXLIGHT); + constant("mjMAXOVERLAY", mjMAXOVERLAY); + constant("mjMAXLINE", mjMAXLINE); + constant("mjMAXLINEPNT", mjMAXLINEPNT); + constant("mjMAXPLANEGRID", mjMAXPLANEGRID); + + // from mujoco.h + constant("mjVERSION_HEADER", mjVERSION_HEADER); + + // from mjtnum.h + constant("mjMINVAL", mjMINVAL); + + // emscripten::constant() is designed for simple, compile-time literal values + // (like numbers or a single string literal), complex values need to be + // bound as functions. + emscripten::function("get_mjDISABLESTRING", &get_mjDISABLESTRING); + emscripten::function("get_mjENABLESTRING", &get_mjENABLESTRING); + emscripten::function("get_mjTIMERSTRING", &get_mjTIMERSTRING); + emscripten::function("get_mjLABELSTRING", &get_mjLABELSTRING); + emscripten::function("get_mjFRAMESTRING", &get_mjFRAMESTRING); + emscripten::function("get_mjVISSTRING", &get_mjVISSTRING); + emscripten::function("get_mjRNDSTRING", &get_mjRNDSTRING); +} + +EMSCRIPTEN_BINDINGS(mujoco_enums) { +// {{ ENUM_BINDINGS }} +} + +// STRUCTS +// =============== MjLROpt =============== // +// INSERT-GENERATED-MjLROpt-CONSTRUCTOR + +// =============== MjOption =============== // +// INSERT-GENERATED-MjOption-CONSTRUCTOR + +// =============== MjStatistic =============== // +// INSERT-GENERATED-MjStatistic-CONSTRUCTOR + +// =============== MjVisual... =============== // +// INSERT-GENERATED-MjVisualGlobal-CONSTRUCTOR + +// INSERT-GENERATED-MjVisualQuality-CONSTRUCTOR + +// INSERT-GENERATED-MjVisualHeadlight-CONSTRUCTOR + +// INSERT-GENERATED-MjVisualMap-CONSTRUCTOR + +// INSERT-GENERATED-MjVisualScale-CONSTRUCTOR + +// INSERT-GENERATED-MjVisualRgba-CONSTRUCTOR + +// INSERT-GENERATED-MjVisual-CONSTRUCTOR + +// =============== MjSolverStat =============== // +// INSERT-GENERATED-MjSolverStat-CONSTRUCTOR + +// =============== MjTimerStat =============== // +// INSERT-GENERATED-MjTimerStat-CONSTRUCTOR + +// =============== MjWarningStat =============== // +// INSERT-GENERATED-MjWarningStat-CONSTRUCTOR + +// =============== MjContact =============== // +// INSERT-GENERATED-MjContact-CONSTRUCTOR + +// =============== MjModel =============== // +MjModel::MjModel(mjModel *m) + : ptr_(m), opt(&m->opt), stat(&m->stat), vis(&m->vis) {} +MjModel::MjModel(const MjModel &other) + : ptr_(mj_copyModel(nullptr, other.get())), + opt(&ptr_->opt), + stat(&ptr_->stat), + vis(&ptr_->vis) {} +MjModel::~MjModel() { + if (ptr_) { + mj_deleteModel(ptr_); + } +} + +// TODO(manevi): Consider passing `const MjModel& m` here, mj_makeData uses a const model. +// =============== MjData =============== // +MjData::MjData(MjModel *m) { + model = m->get(); + ptr_ = mj_makeData(model); + if (ptr_) { + solver = InitSolverArray(); + timer = InitTimerArray(); + warning = InitWarningArray(); + } +} +MjData::MjData(const MjModel &model, const MjData &other) + : ptr_(mj_copyData(nullptr, model.get(), other.get())), model(model.get()) { + if (ptr_) { + solver = InitSolverArray(); + timer = InitTimerArray(); + warning = InitWarningArray(); + } +} +MjData::~MjData() { + if (ptr_) { + mj_deleteData(ptr_); + } +} +std::vector MjData::InitSolverArray() { + std::vector arr; + arr.reserve(mjNSOLVER * mjNISLAND); + for (int i = 0; i < mjNSOLVER * mjNISLAND; i++) { + arr.emplace_back(&get()->solver[i]); + } + return arr; +} +std::vector MjData::InitTimerArray() { + std::vector arr; + arr.reserve(mjNTIMER); + for (int i = 0; i < mjNTIMER; i++) { + arr.emplace_back(&get()->timer[i]); + } + return arr; +} +std::vector +MjData::InitWarningArray() { + std::vector arr; + arr.reserve(mjNWARNING); + for (int i = 0; i < mjNWARNING; i++) { + arr.emplace_back(&get()->warning[i]); + } + return arr; +} +std::vector MjData::contact() const { + std::vector contacts; + contacts.reserve(get()->ncon); + for (int i = 0; i < get()->ncon; ++i) { + contacts.emplace_back(&get()->contact[i]); + } + return contacts; +} +// =============== MjvPerturb =============== // +// INSERT-GENERATED-MjvPerturb-CONSTRUCTOR + +// =============== MjvCamera =============== // +// INSERT-GENERATED-MjvCamera-CONSTRUCTOR + +// =============== MjvGLCamera =============== // +// INSERT-GENERATED-MjvGLCamera-CONSTRUCTOR + +// =============== MjvGeom =============== // +MjvGeom::MjvGeom(mjvGeom *ptr) { ptr_ = ptr; }; +MjvGeom::MjvGeom() : ptr_(new mjvGeom) { + owned_ = true; + mjv_initGeom(ptr_, mjGEOM_NONE, nullptr, nullptr, nullptr, nullptr); +}; +MjvGeom::MjvGeom(const MjvGeom &other) : MjvGeom() { + *ptr_ = *other.get(); +} +MjvGeom &MjvGeom::operator=( + const MjvGeom &other) { + if (this == &other) { + return *this; + } + *ptr_ = *other.get(); + return *this; +} +MjvGeom::~MjvGeom() { + if (owned_ && ptr_) delete ptr_; +} +std::unique_ptr MjvGeom::copy() { + return std::make_unique(*this); +} + +// =============== MjvLight =============== // +// INSERT-GENERATED-MjvLight-CONSTRUCTOR + +// =============== MjvOption =============== // +// INSERT-GENERATED-MjvOption-CONSTRUCTOR + +// =============== MjvScene =============== // +MjvScene::MjvScene() { + owned_ = true; + ptr_ = new mjvScene; + mjv_defaultScene(ptr_); + mjv_makeScene(nullptr, ptr_, 0); + lights = InitLightsArray(); + camera = InitCameraArray(); +}; + +MjvScene::MjvScene(MjModel *m, int maxgeom) { + owned_ = true; + model = m->get(); + ptr_ = new mjvScene; + mjv_defaultScene(ptr_); + mjv_makeScene(model, ptr_, maxgeom); + lights = InitLightsArray(); + camera = InitCameraArray(); +}; +MjvScene::~MjvScene() { + if (owned_ && ptr_) { + mjv_freeScene(ptr_); + delete ptr_; + } +} + +// Taken from the python mujoco bindings code for MjvScene Wrapper +int MjvScene::GetSumFlexFaces() const { + int nflexface = 0; + int flexfacenum = 0; + for (int f = 0; f < model->nflex; f++) { + if (model->flex_dim[f] == 0) { + // 1D : 0 + flexfacenum = 0; + } else if (model->flex_dim[f] == 2) { + // 2D: 2*fragments + 2*elements + flexfacenum = 2 * model->flex_shellnum[f] + 2 * model->flex_elemnum[f]; + } else { + // 3D: max(fragments, 4*maxlayer) + // find number of elements in biggest layer + int maxlayer = 0, layer = 0, nlayer = 1; + while (nlayer) { + nlayer = 0; + for (int e = 0; e < model->flex_elemnum[f]; e++) { + if (model->flex_elemlayer[model->flex_elemadr[f] + e] == layer) { + nlayer++; + } + } + maxlayer = mjMAX(maxlayer, nlayer); + layer++; + } + flexfacenum = mjMAX(model->flex_shellnum[f], 4 * maxlayer); + } + + // accumulate over flexes + nflexface += flexfacenum; + } + return nflexface; +} + +std::vector MjvScene::InitLightsArray() { + std::vector arr; + arr.reserve(mjMAXLIGHT); + for (int i = 0; i < mjMAXLIGHT; i++) { + arr.emplace_back(&ptr_->lights[i]); + } + return arr; +} + +std::vector MjvScene::InitCameraArray() { + std::vector arr; + arr.reserve(2); + for (int i = 0; i < 2; i++) { + arr.emplace_back(&ptr_->camera[i]); + } + return arr; +} + +std::vector MjvScene::geoms() const { + std::vector geoms; + geoms.reserve(ptr_->ngeom); + for (int i = 0; i < ptr_->ngeom; ++i) { + geoms.emplace_back(&ptr_->geoms[i]); + } + return geoms; +} + +// =============== MjvFigure =============== // +// INSERT-GENERATED-MjvFigure-CONSTRUCTOR + +// =============== MjsElement =============== // +// INSERT-GENERATED-MjsElement-CONSTRUCTOR + +// =============== MjsCompiler =============== // +// INSERT-GENERATED-MjsCompiler-CONSTRUCTOR + +// =============== MjSpec =============== // +MjSpec::MjSpec() + : ptr_(mj_makeSpec()), + option(&ptr_->option), + visual(&ptr_->visual), + stat(&ptr_->stat), + compiler(&ptr_->compiler), + element(ptr_->element) { + owned_ = true; + mjs_defaultSpec(ptr_); +}; + +MjSpec::MjSpec(mjSpec *ptr) + : ptr_(ptr), + option(&ptr_->option), + visual(&ptr_->visual), + stat(&ptr_->stat), + compiler(&ptr_->compiler), + element(ptr_->element) {} + +MjSpec::MjSpec(const MjSpec &other) + : ptr_(mj_copySpec(other.get())), + option(&ptr_->option), + visual(&ptr_->visual), + stat(&ptr_->stat), + compiler(&ptr_->compiler), + element(ptr_->element) { + owned_ = true; +} + +MjSpec& MjSpec::operator=(const MjSpec &other) { + if (this == &other) { + return *this; + } + if (owned_ && ptr_) { + mj_deleteSpec(ptr_); + } + ptr_ = mj_copySpec(other.get()); + owned_ = true; + option.set(&ptr_->option); + visual.set(&ptr_->visual); + stat.set(&ptr_->stat); + compiler.set(&ptr_->compiler); + element.set(ptr_->element); + return *this; +} + +MjSpec::~MjSpec() { + if (ptr_ && owned_) { + mj_deleteSpec(ptr_); + } +} + +// =============== MjsOrientation =============== // +// INSERT-GENERATED-MjsOrientation-CONSTRUCTOR + +// =============== MjsBody =============== // +// INSERT-GENERATED-MjsBody-CONSTRUCTOR + +// =============== MjsGeom =============== // +// INSERT-GENERATED-MjsGeom-CONSTRUCTOR + +// =============== MjsFrame =============== // +// INSERT-GENERATED-MjsFrame-CONSTRUCTOR + +// =============== MjsJoint =============== // +// INSERT-GENERATED-MjsJoint-CONSTRUCTOR + +// =============== MjsSite =============== // +// INSERT-GENERATED-MjsSite-CONSTRUCTOR + +// =============== MjsCamera =============== // +// INSERT-GENERATED-MjsCamera-CONSTRUCTOR + +// =============== MjsLight =============== // +// INSERT-GENERATED-MjsLight-CONSTRUCTOR + +// =============== MjsFlex =============== // +// INSERT-GENERATED-MjsFlex-CONSTRUCTOR + +// =============== MjsMesh =============== // +// INSERT-GENERATED-MjsMesh-CONSTRUCTOR + +// =============== MjsHField =============== // +// INSERT-GENERATED-MjsHField-CONSTRUCTOR + +// =============== MjsSkin =============== // +// INSERT-GENERATED-MjsSkin-CONSTRUCTOR + +// =============== MjsTexture =============== // +// INSERT-GENERATED-MjsTexture-CONSTRUCTOR + +// =============== MjsMaterial =============== // +// INSERT-GENERATED-MjsMaterial-CONSTRUCTOR + +// =============== MjsPair =============== // +// INSERT-GENERATED-MjsPair-CONSTRUCTOR + +// =============== MjsExclude =============== // +// INSERT-GENERATED-MjsExclude-CONSTRUCTOR + +// =============== MjsEquality =============== // +// INSERT-GENERATED-MjsEquality-CONSTRUCTOR + +// =============== MjsTendon =============== // +// INSERT-GENERATED-MjsTendon-CONSTRUCTOR + +// =============== MjsWrap =============== // +// INSERT-GENERATED-MjsWrap-CONSTRUCTOR + +// =============== MjsActuator =============== // +// INSERT-GENERATED-MjsActuator-CONSTRUCTOR + +// =============== MjsSensor =============== // +// INSERT-GENERATED-MjsSensor-CONSTRUCTOR + +// =============== MjsNumeric =============== // +// INSERT-GENERATED-MjsNumeric-CONSTRUCTOR + +// =============== MjsText =============== // +// INSERT-GENERATED-MjsText-CONSTRUCTOR + +// =============== MjsTuple =============== // +// INSERT-GENERATED-MjsTuple-CONSTRUCTOR + +// =============== MjsKey =============== // +// INSERT-GENERATED-MjsKey-CONSTRUCTOR + +// =============== MjsDefault =============== // +// INSERT-GENERATED-MjsDefault-CONSTRUCTOR + +// =============== MjsPlugin =============== // +// INSERT-GENERATED-MjsPlugin-CONSTRUCTOR + +// =============== MjVFS =============== // +MjVFS::MjVFS(mjVFS *ptr) : ptr_(ptr) {} +MjVFS::MjVFS() : ptr_(new mjVFS) { + owned_ = true; + mj_defaultVFS(ptr_); +} +MjVFS::~MjVFS() { + if (owned_ && ptr_) { + mj_deleteVFS(ptr_); + } +} + +// ======= FACTORY AND HELPER FUNCTIONS ========= // +std::unique_ptr loadFromXML(std::string filename) { + char error[1000]; + mjModel *model = mj_loadXML(filename.c_str(), nullptr, error, sizeof(error)); + if (!model) { + printf("Loading error: %s\n", error); + return nullptr; + } + return std::unique_ptr(new MjModel(model)); +} + +std::unique_ptr parseXMLString(const std::string &xml) { + char error[1000]; + mjSpec *ptr = mj_parseXMLString(xml.c_str(), nullptr, error, sizeof(error)); + if (!ptr) { + printf("Could not create Spec from XML string: %s\n", error); + return nullptr; + } + return std::unique_ptr(new MjSpec(ptr)); +} + +EMSCRIPTEN_BINDINGS(mujoco_bindings) { + function("parseXMLString", &parseXMLString, take_ownership()); + + emscripten::class_("MjLROpt") + .constructor<>() + .function("copy", &MjLROpt::copy, take_ownership()) + // INSERT-GENERATED-MjLROpt-BINDINGS + ; + emscripten::class_("MjModel") + .class_function("loadFromXML", &loadFromXML, take_ownership()) + .constructor() + // INSERT-GENERATED-MjModel-BINDINGS + ; + emscripten::class_("MjData") + .constructor() + .constructor() + // INSERT-GENERATED-MjData-BINDINGS + ; + emscripten::class_("MjOption") + .constructor<>() + .function("copy", &MjOption::copy, take_ownership()) + // INSERT-GENERATED-MjOption-BINDINGS + ; + emscripten::class_("MjStatistic") + .constructor<>() + .function("copy", &MjStatistic::copy, take_ownership()) + // INSERT-GENERATED-MjStatistic-BINDINGS + ; + emscripten::class_("MjVisualGlobal") + .constructor<>() + .function("copy", &MjVisualGlobal::copy, take_ownership()) + // INSERT-GENERATED-MjVisualGlobal-BINDINGS + ; + emscripten::class_("MjVisualQuality") + .constructor<>() + .function("copy", &MjVisualQuality::copy, take_ownership()) + // INSERT-GENERATED-MjVisualQuality-BINDINGS + ; + emscripten::class_("MjVisualHeadlight") + .constructor<>() + .function("copy", &MjVisualHeadlight::copy, take_ownership()) + // INSERT-GENERATED-MjVisualHeadlight-BINDINGS + ; + emscripten::class_("MjVisualMap") + .constructor<>() + .function("copy", &MjVisualMap::copy, take_ownership()) + // INSERT-GENERATED-MjVisualMap-BINDINGS + ; + emscripten::class_("MjVisualScale") + .constructor<>() + .function("copy", &MjVisualScale::copy, take_ownership()) + // INSERT-GENERATED-MjVisualScale-BINDINGS + ; + emscripten::class_("MjVisualRgba") + .constructor<>() + .function("copy", &MjVisualRgba::copy, take_ownership()) + // INSERT-GENERATED-MjVisualRgba-BINDINGS + ; + emscripten::class_("MjVisual") + .constructor<>() + .function("copy", &MjVisual::copy, take_ownership()) + // INSERT-GENERATED-MjVisual-BINDINGS + ; + emscripten::class_("MjSolverStat") + .constructor<>() + .function("copy", &MjSolverStat::copy, take_ownership()) + // INSERT-GENERATED-MjSolverStat-BINDINGS + ; + emscripten::class_("MjTimerStat") + .constructor<>() + .function("copy", &MjTimerStat::copy, take_ownership()) + // INSERT-GENERATED-MjTimerStat-BINDINGS + ; + emscripten::class_("MjWarningStat") + .constructor<>() + .function("copy", &MjWarningStat::copy, take_ownership()) + // INSERT-GENERATED-MjWarningStat-BINDINGS + ; + emscripten::class_("MjContact") + .constructor<>() + .function("copy", &MjContact::copy, take_ownership()) + // INSERT-GENERATED-MjContact-BINDINGS + ; + emscripten::class_("MjvPerturb") + .constructor<>() + .function("copy", &MjvPerturb::copy, take_ownership()) + // INSERT-GENERATED-MjvPerturb-BINDINGS + ; + emscripten::class_("MjvCamera") + .constructor<>() + .function("copy", &MjvCamera::copy, take_ownership()) + // INSERT-GENERATED-MjvCamera-BINDINGS + ; + emscripten::class_("MjvGLCamera") + .constructor<>() + .function("copy", &MjvGLCamera::copy, take_ownership()) + // INSERT-GENERATED-MjvGLCamera-BINDINGS + ; + emscripten::class_("MjvGeom") + .constructor<>() + .function("copy", &MjvGLCamera::copy, take_ownership()) + // INSERT-GENERATED-MjvGeom-BINDINGS + ; + emscripten::class_("MjvLight") + .constructor<>() + .function("copy", &MjvLight::copy, take_ownership()) + // INSERT-GENERATED-MjvLight-BINDINGS + ; + emscripten::class_("MjvOption") + .constructor<>() + .function("copy", &MjvOption::copy, take_ownership()) + // INSERT-GENERATED-MjvOption-BINDINGS + ; + + emscripten::class_("MjvScene") + .constructor<>() + .constructor() + // INSERT-GENERATED-MjvScene-BINDINGS + ; + + emscripten::class_("MjvFigure") + .constructor<>() + .function("copy", &MjvFigure::copy, take_ownership()) + // INSERT-GENERATED-MjvFigure-BINDINGS + ; + + emscripten::class_("MjSpec") + .constructor() + // INSERT-GENERATED-MjSpec-BINDINGS + ; + + emscripten::class_("MjsElement") + // INSERT-GENERATED-MjsElement-BINDINGS + ; + + emscripten::class_("MjsCompiler") + // INSERT-GENERATED-MjsCompiler-BINDINGS + ; + + emscripten::class_("MjsOrientation") + .function("copy", &MjsOrientation::copy, take_ownership()) + // INSERT-GENERATED-MjsOrientation-BINDINGS + ; + + emscripten::class_("MjsBody") + // INSERT-GENERATED-MjsBody-BINDINGS + ; + + emscripten::class_("MjsGeom") + // INSERT-GENERATED-MjsGeom-BINDINGS + ; + + emscripten::class_("MjsFrame") + // INSERT-GENERATED-MjsFrame-BINDINGS + ; + + emscripten::class_("MjsJoint") + // INSERT-GENERATED-MjsJoint-BINDINGS + ; + + emscripten::class_("MjsSite") + // INSERT-GENERATED-MjsSite-BINDINGS + ; + + emscripten::class_("MjsCamera") + // INSERT-GENERATED-MjsCamera-BINDINGS + ; + + emscripten::class_("MjsLight") + // INSERT-GENERATED-MjsLight-BINDINGS + ; + + emscripten::class_("MjsFlex") + // INSERT-GENERATED-MjsFlex-BINDINGS + ; + + emscripten::class_("MjsMesh") + // INSERT-GENERATED-MjsMesh-BINDINGS + ; + + emscripten::class_("MjsHField") + // INSERT-GENERATED-MjsHField-BINDINGS + ; + + emscripten::class_("MjsSkin") + // INSERT-GENERATED-MjsSkin-BINDINGS + ; + + emscripten::class_("MjsTexture") + // INSERT-GENERATED-MjsTexture-BINDINGS + ; + + emscripten::class_("MjsMaterial") + // INSERT-GENERATED-MjsMaterial-BINDINGS + ; + + emscripten::class_("MjsPair") + // INSERT-GENERATED-MjsPair-BINDINGS + ; + + emscripten::class_("MjsExclude") + // INSERT-GENERATED-MjsExclude-BINDINGS + ; + + emscripten::class_("MjsEquality") + // INSERT-GENERATED-MjsEquality-BINDINGS + ; + + emscripten::class_("MjsTendon") + // INSERT-GENERATED-MjsTendon-BINDINGS + ; + + emscripten::class_("MjsWrap") + // INSERT-GENERATED-MjsWrap-BINDINGS + ; + + emscripten::class_("MjsActuator") + // INSERT-GENERATED-MjsActuator-BINDINGS + ; + + emscripten::class_("MjsSensor") + // INSERT-GENERATED-MjsSensor-BINDINGS + ; + + emscripten::class_("MjsNumeric") + // INSERT-GENERATED-MjsNumeric-BINDINGS + ; + + emscripten::class_("MjsText") + // INSERT-GENERATED-MjsText-BINDINGS + ; + + emscripten::class_("MjsTuple") + // INSERT-GENERATED-MjsTuple-BINDINGS + ; + + emscripten::class_("MjsKey") + // INSERT-GENERATED-MjsKey-BINDINGS + ; + + emscripten::class_("MjsDefault") + // INSERT-GENERATED-MjsDefault-BINDINGS + ; + + emscripten::class_("MjsPlugin") + // INSERT-GENERATED-MjsPlugin-BINDINGS + ; + + emscripten::class_("MjVFS").constructor<>() + // INSERT-GENERATED-MjVFS-BINDINGS + ; + + // TODO: should be generated in future CLs -- // + emscripten::register_vector("MjSolverStatVec"); + emscripten::register_vector("MjTimerStatVec"); + emscripten::register_vector("MjWarningStatVec"); + emscripten::register_vector("MjContactVec"); + emscripten::register_vector("MjvLightVec"); + emscripten::register_vector("MjvGLCameraVec"); + emscripten::register_vector("MjvGeomVec"); +} + +// FUNCTIONS +EMSCRIPTEN_DECLARE_VAL_TYPE(NumberArray); +EMSCRIPTEN_DECLARE_VAL_TYPE(String); + +// Raises an error if the given val is null or undefined. +// A macro is used so that the error contains the name of the variable. +// TODO(matijak): Remove this when we can handle strings using UNPACK_STRING? +#define CHECK_VAL(val) \ + if (val.isNull()) { \ + mju_error("Invalid argument: %s is null", #val); \ + } else if (val.isUndefined()) { \ + mju_error("Invalid argument: %s is undefined", #val); \ + } +void error_wrapper(const String& msg) { mju_error("%s\n", msg.as().data()); } + +// {{ WRAPPER_FUNCTIONS }} + + +void mju_printMatSparse_wrapper(const NumberArray& mat, const NumberArray& rownnz, const NumberArray& rowadr, const NumberArray& colind) +{ + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(int, rownnz); + UNPACK_ARRAY(int, rowadr); + UNPACK_ARRAY(int, colind); + CHECK_SIZES(rownnz, rowadr); + mju_printMatSparse(mat_.data(), rowadr_.size(), + rownnz_.data(), + rowadr_.data(), + colind_.data()); +} + +void mj_solveM_wrapper(const MjModel& m, MjData& d, const val& x, const NumberArray& y) +{ + UNPACK_VALUE(mjtNum, x); + UNPACK_ARRAY(mjtNum, y); + CHECK_SIZES(x, y); + CHECK_DIVISIBLE(x, m.nv()); + mj_solveM(m.get(), d.get(), x_.data(), y_.data(), x_div.quot); +} + +void mj_solveM2_wrapper(const MjModel& m, MjData& d, + const val& x, const NumberArray& y, + const NumberArray& sqrtInvD) { + UNPACK_VALUE(mjtNum, x); + UNPACK_ARRAY(mjtNum, y); + UNPACK_ARRAY(mjtNum, sqrtInvD); + CHECK_SIZES(x, y); + CHECK_SIZE(sqrtInvD, m.nv()); + CHECK_DIVISIBLE(x, m.nv()); + mj_solveM2(m.get(), d.get(), x_.data(), y_.data(), sqrtInvD_.data(), x_div.quot); +} + +void mj_rne_wrapper(const MjModel& m, MjData& d, int flg_acc, const val& result) +{ + UNPACK_VALUE(mjtNum, result); + CHECK_SIZE(result, m.nv()); + mj_rne(m.get(), d.get(), flg_acc, result_.data()); +} + +int mj_saveLastXML_wrapper(const String& filename, const MjModel& m) { + CHECK_VAL(filename); + std::array error; + int result = mj_saveLastXML(filename.as().data(), m.get(), error.data(), error.size()); + if (!result) { + mju_error("%s", error.data()); + } + return result; +} + +int mj_setLengthRange_wrapper(const MjModel& m, const MjData& d, int index, const MjLROpt& opt) { + std::array error; + int result = mj_setLengthRange(m.get(), d.get(), index, opt.get(), error.data(), error.size()); + if (!result) { + mju_error("%s", error.data()); + } + return result; +} + +void mj_constraintUpdate_wrapper(const MjModel& m, MjData& d, const NumberArray& jar, const val& cost, int flg_coneHessian) +{ + UNPACK_ARRAY(mjtNum, jar); + UNPACK_NULLABLE_VALUE(mjtNum, cost); + CHECK_SIZE(cost, 1); + CHECK_SIZE(jar, d.nefc()); + mj_constraintUpdate(m.get(), d.get(), jar_.data(), cost_.data(), flg_coneHessian); +} + +void mj_getState_wrapper(const MjModel& m, const MjData& d, const val& state, unsigned int spec) +{ + UNPACK_VALUE(mjtNum, state); + CHECK_SIZE(state, mj_stateSize(m.get(), spec)); + mj_getState(m.get(), d.get(), state_.data(), spec); +} + +void mj_setState_wrapper(const MjModel& m, MjData& d, const NumberArray& state, unsigned int spec) +{ + UNPACK_ARRAY(mjtNum, state); + CHECK_SIZE(state, mj_stateSize(m.get(), spec)); + mj_setState(m.get(), d.get(), state_.data(), spec); +} + +void mj_mulJacVec_wrapper(const MjModel& m, const MjData& d, const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(res, d.nefc()); + CHECK_SIZE(vec, m.nv()); + mj_mulJacVec(m.get(), d.get(), res_.data(), vec_.data()); +} + +void mj_mulJacTVec_wrapper(const MjModel& m, const MjData& d, const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(res, m.nv()); + CHECK_SIZE(vec, d.nefc()); + mj_mulJacTVec(m.get(), d.get(), res_.data(), vec_.data()); +} + +void mj_jac_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, const NumberArray& point, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + UNPACK_ARRAY(mjtNum, point); + CHECK_SIZE(point, 3); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jac(m.get(), d.get(), jacp_.data(), jacr_.data(), point_.data(), body); +} + +void mj_jacBody_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacBody(m.get(), d.get(), jacp_.data(), jacr_.data(), body); +} + +void mj_jacBodyCom_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacBodyCom(m.get(), d.get(), jacp_.data(), jacr_.data(), body); +} + +void mj_jacSubtreeCom_wrapper(const MjModel& m, MjData& d, const val& jacp, int body) +{ + UNPACK_VALUE(mjtNum, jacp); + CHECK_SIZE(jacp, m.nv() * 3); + mj_jacSubtreeCom(m.get(), d.get(), jacp_.data(), body); +} + +void mj_jacGeom_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, int geom) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacGeom(m.get(), d.get(), jacp_.data(), jacr_.data(), geom); +} + +void mj_jacSite_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, int site) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacSite(m.get(), d.get(), jacp_.data(), jacr_.data(), site); +} + +void mj_jacPointAxis_wrapper(const MjModel& m, MjData& d, const val& jacPoint, const val& jacAxis, const NumberArray& point, const NumberArray& axis, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacPoint); + UNPACK_NULLABLE_VALUE(mjtNum, jacAxis); + UNPACK_ARRAY(mjtNum, point); + UNPACK_ARRAY(mjtNum, axis); + CHECK_SIZE(point, 3); + CHECK_SIZE(axis, 3); + CHECK_SIZE(jacPoint, m.nv() * 3); + CHECK_SIZE(jacAxis, m.nv() * 3); + mj_jacPointAxis(m.get(), d.get(), jacPoint_.data(), jacAxis_.data(), point_.data(), axis_.data(), body); +} + +void mj_jacDot_wrapper(const MjModel& m, const MjData& d, const val& jacp, const val& jacr, const NumberArray& point, int body) +{ + UNPACK_NULLABLE_VALUE(mjtNum, jacp); + UNPACK_NULLABLE_VALUE(mjtNum, jacr); + UNPACK_ARRAY(mjtNum, point); + CHECK_SIZE(point, 3); + CHECK_SIZE(jacp, m.nv() * 3); + CHECK_SIZE(jacr, m.nv() * 3); + mj_jacDot(m.get(), d.get(), jacp_.data(), jacr_.data(), point_.data(), body); +} + +void mj_angmomMat_wrapper(const MjModel& m, MjData& d, const val& mat, int body) +{ + UNPACK_VALUE(mjtNum, mat); + CHECK_SIZE(mat, m.nv() * 3); + mj_angmomMat(m.get(), d.get(), mat_.data(), body); +} + +void mj_fullM_wrapper(const MjModel& m, const val& dst, const NumberArray& M) +{ + UNPACK_VALUE(mjtNum, dst); + UNPACK_ARRAY(mjtNum, M); + CHECK_SIZE(M, m.nM()); + CHECK_SIZE(dst, m.nv() * m.nv()); + mj_fullM(m.get(), dst_.data(), M_.data()); +} + +void mj_mulM_wrapper(const MjModel& m, const MjData& d, const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(res, m.nv()); + CHECK_SIZE(vec, m.nv()); + mj_mulM(m.get(), d.get(), res_.data(), vec_.data()); +} + +void mj_mulM2_wrapper(const MjModel& m, const MjData& d, const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(res, m.nv()); + CHECK_SIZE(vec, m.nv()); + mj_mulM2(m.get(), d.get(), res_.data(), vec_.data()); +} + +void mj_addM_wrapper(const MjModel& m, MjData& d, const val& dst, const val& rownnz, const val& rowadr, const val& colind) +{ + UNPACK_VALUE(mjtNum, dst); + UNPACK_NULLABLE_VALUE(int, rownnz); + UNPACK_NULLABLE_VALUE(int, rowadr); + UNPACK_NULLABLE_VALUE(int, colind); + CHECK_SIZE(rownnz, m.nv()); + CHECK_SIZE(rowadr, m.nv()); + CHECK_SIZE(colind, m.nM()); + CHECK_SIZE(dst, m.nM()); + mj_addM(m.get(), d.get(), dst_.data(), rownnz_.data(), rowadr_.data(), colind_.data()); +} + +void mj_applyFT_wrapper(const MjModel& m, MjData& d, const NumberArray& force, const NumberArray& torque, const NumberArray& point, int body, const val& qfrc_target) +{ + UNPACK_NULLABLE_ARRAY(mjtNum, force); + UNPACK_NULLABLE_ARRAY(mjtNum, torque); + UNPACK_ARRAY(mjtNum, point); + UNPACK_VALUE(mjtNum, qfrc_target); + CHECK_SIZE(qfrc_target, m.nv()); + CHECK_SIZE(force, 3); + CHECK_SIZE(torque, 3); + CHECK_SIZE(point, 3); + mj_applyFT(m.get(), d.get(), force_.data(), torque_.data(), point_.data(), body, qfrc_target_.data()); +} + +mjtNum mj_geomDistance_wrapper(const MjModel& m, const MjData& d, int geom1, int geom2, mjtNum distmax, const val& fromto) +{ + UNPACK_NULLABLE_VALUE(mjtNum, fromto); + CHECK_SIZE(fromto, 6); + return mj_geomDistance(m.get(), d.get(), geom1, geom2, distmax, fromto_.data()); +} + +void mj_differentiatePos_wrapper(const MjModel& m, const val& qvel, mjtNum dt, const NumberArray& qpos1, const NumberArray& qpos2) +{ + UNPACK_VALUE(mjtNum, qvel); + UNPACK_ARRAY(mjtNum, qpos1); + UNPACK_ARRAY(mjtNum, qpos2); + CHECK_SIZE(qvel, m.nv()); + CHECK_SIZE(qpos1, m.nq()); + CHECK_SIZE(qpos2, m.nq()); + mj_differentiatePos(m.get(), qvel_.data(), dt, qpos1_.data(), qpos2_.data()); +} + +void mj_integratePos_wrapper(const MjModel& m, const val& qpos, const NumberArray& qvel, mjtNum dt) +{ + UNPACK_VALUE(mjtNum, qpos); + UNPACK_ARRAY(mjtNum, qvel); + CHECK_SIZE(qpos, m.nq()); + CHECK_SIZE(qvel, m.nv()); + mj_integratePos(m.get(), qpos_.data(), qvel_.data(), dt); +} + +void mj_normalizeQuat_wrapper(const MjModel& m, const val& qpos) +{ + UNPACK_VALUE(mjtNum, qpos); + CHECK_SIZE(qpos, m.nq()); + mj_normalizeQuat(m.get(), qpos_.data()); +} + +void mj_multiRay_wrapper(const MjModel& m, MjData& d, const NumberArray& pnt, const NumberArray& vec, const val& geomgroup, mjtByte flg_static, int bodyexclude, const val& geomid, const val& dist, int nray, mjtNum cutoff) +{ + UNPACK_ARRAY(mjtNum, pnt); + UNPACK_ARRAY(mjtNum, vec); + UNPACK_VALUE(mjtByte, geomgroup); + UNPACK_VALUE(int, geomid); + UNPACK_VALUE(mjtNum, dist); + CHECK_SIZE(dist, nray); + CHECK_SIZE(geomid, nray); + CHECK_SIZE(vec, 3 * nray); + mj_multiRay(m.get(), d.get(), pnt_.data(), vec_.data(), geomgroup_.data(), flg_static, bodyexclude, geomid_.data(), dist_.data(), nray, cutoff); +} + +void mju_zero_wrapper(const val& res) +{ + UNPACK_VALUE(mjtNum, res); + mju_zero(res_.data(), res_.size()); +} + +void mju_fill_wrapper(const val& res, mjtNum val) +{ + UNPACK_VALUE(mjtNum, res); + mju_fill(res_.data(), val, res_.size()); +} + +void mju_copy_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_copy(res_.data(), vec_.data(), res_.size()); +} + +mjtNum mju_sum_wrapper(const NumberArray& vec) +{ + UNPACK_ARRAY(mjtNum, vec); + return mju_sum(vec_.data(), vec_.size()); +} + +mjtNum mju_L1_wrapper(const NumberArray& vec) +{ + UNPACK_ARRAY(mjtNum, vec); + return mju_L1(vec_.data(), vec_.size()); +} + +void mju_scl_wrapper(const val& res, const NumberArray& vec, mjtNum scl) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_scl(res_.data(), vec_.data(), scl, res_.size()); +} + +void mju_add_wrapper(const val& res, const NumberArray& vec1, const NumberArray& vec2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + CHECK_SIZES(res, vec1); + CHECK_SIZES(res, vec2); + mju_add(res_.data(), vec1_.data(), vec2_.data(), res_.size()); +} + +void mju_sub_wrapper(const val& res, const NumberArray& vec1, const NumberArray& vec2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + CHECK_SIZES(res, vec1); + CHECK_SIZES(res, vec2); + mju_sub(res_.data(), vec1_.data(), vec2_.data(), res_.size()); +} + +void mju_addTo_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_addTo(res_.data(), vec_.data(), res_.size()); +} + +void mju_subFrom_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_subFrom(res_.data(), vec_.data(), res_.size()); +} + +void mju_addToScl_wrapper(const val& res, const NumberArray& vec, mjtNum scl) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_addToScl(res_.data(), vec_.data(), scl, res_.size()); +} + +void mju_addScl_wrapper(const val& res, const NumberArray& vec1, const NumberArray& vec2, mjtNum scl) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + CHECK_SIZES(res, vec1); + CHECK_SIZES(res, vec2); + mju_addScl(res_.data(), vec1_.data(), vec2_.data(), scl, res_.size()); +} + +mjtNum mju_normalize_wrapper(const val& res) +{ + UNPACK_VALUE(mjtNum, res); + return mju_normalize(res_.data(), res_.size()); +} + +mjtNum mju_norm_wrapper(const NumberArray& res) +{ + UNPACK_ARRAY(mjtNum, res); + return mju_norm(res_.data(), res_.size()); +} + +mjtNum mju_dot_wrapper(const NumberArray& vec1, const NumberArray& vec2) +{ + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, vec2); + CHECK_SIZES(vec1, vec2); + return mju_dot(vec1_.data(), vec2_.data(), vec1_.size()); +} + +void mju_mulMatVec_wrapper(const val& res, const NumberArray& mat, + const NumberArray& vec, int nr, int nc) { + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(res, nr); + CHECK_SIZE(vec, nc); + mju_mulMatVec(res_.data(), mat_.data(), vec_.data(), nr, nc); +} + +void mju_mulMatTVec_wrapper(const val& res, const NumberArray& mat, + const NumberArray& vec, int nr, int nc) { + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(res, nc); + CHECK_SIZE(vec, nr); + mju_mulMatTVec(res_.data(), mat_.data(), vec_.data(), nr, nc); +} + +mjtNum mju_mulVecMatVec_wrapper(const NumberArray& vec1, const NumberArray& mat, const NumberArray& vec2) +{ + UNPACK_ARRAY(mjtNum, vec1); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec2); + int64_t vec1_times_vec2 = vec1_.size() * vec2_.size(); + CHECK_SIZES(vec1, vec2); + CHECK_SIZE(mat, vec1_times_vec2); + return mju_mulVecMatVec(vec1_.data(), mat_.data(), vec2_.data(), vec1_.size()); +} + +void mju_transpose_wrapper(const val& res, const NumberArray& mat, int nr, int nc) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(res, nr * nc); + mju_transpose(res_.data(), mat_.data(), nr, nc); +} + +void mju_symmetrize_wrapper(const val& res, const NumberArray& mat, int n) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + CHECK_SIZE(mat, n * n); + CHECK_SIZE(res, n * n); + mju_symmetrize(res_.data(), mat_.data(), n); +} + +void mju_eye_wrapper(const val& mat) +{ + UNPACK_VALUE(mjtNum, mat); + CHECK_PERFECT_SQUARE(mat); + mju_eye(mat_.data(), mat_sqrt); +} + +void mju_mulMatMat_wrapper(const val& res, const NumberArray& mat1, const NumberArray& mat2, int r1, int c1, int c2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat1); + UNPACK_ARRAY(mjtNum, mat2); + CHECK_SIZE(res, r1 * c2); + CHECK_SIZE(mat1, r1 * c1); + CHECK_SIZE(mat2, c1 * c2); + mju_mulMatMat(res_.data(), mat1_.data(), mat2_.data(), r1, c1, c2); +} + +void mju_mulMatMatT_wrapper(const val& res, const NumberArray& mat1, const NumberArray& mat2, int r1, int c1, int r2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat1); + UNPACK_ARRAY(mjtNum, mat2); + CHECK_SIZE(res, r1 * r2); + CHECK_SIZE(mat1, r1 * c1); + CHECK_SIZE(mat2, r2 * c1); + mju_mulMatMatT(res_.data(), mat1_.data(), mat2_.data(), r1, c1, r2); +} + +void mju_mulMatTMat_wrapper(const val& res, const NumberArray& mat1, const NumberArray& mat2, int r1, int c1, int c2) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat1); + UNPACK_ARRAY(mjtNum, mat2); + CHECK_SIZE(res, c1 * c2); + CHECK_SIZE(mat1, r1 * c1); + CHECK_SIZE(mat2, r1 * c2); + mju_mulMatTMat(res_.data(), mat1_.data(), mat2_.data(), r1, c1, c2); +} + +void mju_sqrMatTD_wrapper(const val& res, const NumberArray& mat, const NumberArray& diag, int nr, int nc) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, diag); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(res, nc * nc); + CHECK_SIZE(diag, nr); + mju_sqrMatTD(res_.data(), mat_.data(), diag_.data(), nr, nc); +} + +int mju_dense2sparse_wrapper(const val& res, const NumberArray& mat, int nr, int nc, const val& rownnz, const val& rowadr, const val& colind) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_VALUE(int, rownnz); + UNPACK_VALUE(int, rowadr); + UNPACK_VALUE(int, colind); + CHECK_SIZE(mat, nr * nc); + CHECK_SIZE(rownnz, nr); + CHECK_SIZE(rowadr, nr); + CHECK_SIZE(colind, res_.size()); + return mju_dense2sparse(res_.data(), mat_.data(), nr, nc, rownnz_.data(), rowadr_.data(), colind_.data(), res_.size()); +} + +void mju_sparse2dense_wrapper(const val& res, const NumberArray& mat, int nr, int nc, const NumberArray& rownnz, const NumberArray& rowadr, const NumberArray& colind) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(int, rownnz); + UNPACK_ARRAY(int, rowadr); + UNPACK_ARRAY(int, colind); + CHECK_SIZE(res, nr * nc); + CHECK_SIZE(rownnz, nr); + CHECK_SIZE(rowadr, nr); + mju_sparse2dense(res_.data(), mat_.data(), nr, nc, rownnz_.data(), rowadr_.data(), colind_.data()); +} + +int mju_cholFactor_wrapper(const val& mat, mjtNum mindiag) +{ + UNPACK_VALUE(mjtNum, mat); + CHECK_PERFECT_SQUARE(mat); + return mju_cholFactor(mat_.data(), mat_sqrt, mindiag); +} + +void mju_cholSolve_wrapper(const val& res, const NumberArray& mat, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_PERFECT_SQUARE(mat); + CHECK_SIZE(res, mat_sqrt); + CHECK_SIZE(vec, mat_sqrt); + mju_cholSolve(res_.data(), mat_.data(), vec_.data(), mat_sqrt); +} + +int mju_cholUpdate_wrapper(const val& mat, const val& x, int flg_plus) +{ + UNPACK_VALUE(mjtNum, mat); + UNPACK_VALUE(mjtNum, x); + CHECK_PERFECT_SQUARE(mat); + CHECK_SIZE(x, mat_sqrt); + return mju_cholUpdate(mat_.data(), x_.data(), mat_sqrt, flg_plus); +} + +mjtNum mju_cholFactorBand_wrapper(const val& mat, int ntotal, int nband, int ndense, mjtNum diagadd, mjtNum diagmul) +{ + UNPACK_VALUE(mjtNum, mat); + CHECK_SIZE(mat, (ntotal - ndense) * nband + ndense * ntotal); + return mju_cholFactorBand(mat_.data(), ntotal, nband, ndense, diagadd, diagmul); +} + +void mju_cholSolveBand_wrapper(const val& res, const NumberArray& mat, const NumberArray& vec, int ntotal, int nband, int ndense) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(mat, (ntotal - ndense) * nband + ndense * ntotal); + CHECK_SIZE(res, ntotal); + CHECK_SIZE(vec, ntotal); + mju_cholSolveBand(res_.data(), mat_.data(), vec_.data(), ntotal, nband, ndense); +} + +void mju_band2Dense_wrapper(const val& res, const NumberArray& mat, int ntotal, int nband, int ndense, mjtByte flg_sym) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + CHECK_SIZE(mat, (ntotal - ndense) * nband + ndense * ntotal); + CHECK_SIZE(res, ntotal * ntotal); + mju_band2Dense(res_.data(), mat_.data(), ntotal, nband, ndense, flg_sym); +} + +void mju_dense2Band_wrapper(const val& res, const NumberArray& mat, int ntotal, int nband, int ndense) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + CHECK_SIZE(mat, ntotal * ntotal); + CHECK_SIZE(res, (ntotal - ndense) * nband + ndense * ntotal); + mju_dense2Band(res_.data(), mat_.data(), ntotal, nband, ndense); +} + +void mju_bandMulMatVec_wrapper(const val& res, const NumberArray& mat, const NumberArray& vec, int ntotal, int nband, int ndense, int nvec, mjtByte flg_sym) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(mjtNum, mat); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZE(mat, (ntotal - ndense) * nband + ndense * ntotal); + CHECK_SIZE(res, ntotal * nvec); + CHECK_SIZE(vec, ntotal * nvec); + mju_bandMulMatVec(res_.data(), mat_.data(), vec_.data(), ntotal, nband, ndense, nvec, flg_sym); +} + +int mju_boxQP_wrapper(const val& res, const val& R, const val& index, const NumberArray& H, const NumberArray& g, const NumberArray& lower, const NumberArray& upper) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_VALUE(mjtNum, R); + UNPACK_NULLABLE_VALUE(int, index); + UNPACK_ARRAY(mjtNum, H); + UNPACK_ARRAY(mjtNum, g); + UNPACK_NULLABLE_ARRAY(mjtNum, lower); + UNPACK_NULLABLE_ARRAY(mjtNum, upper); + CHECK_SIZES(lower, res); + CHECK_SIZES(upper, res); + CHECK_SIZES(index, res); + CHECK_SIZE(R, res_.size() * (res_.size() + 7)) + CHECK_PERFECT_SQUARE(H); + CHECK_SIZES(g, res); + return mju_boxQP(res_.data(), R_.data(), index_.data(), H_.data(), g_.data(), res_.size(), lower_.data(), upper_.data()); +} + +void mju_encodePyramid_wrapper(const val& pyramid, const NumberArray& force, const NumberArray& mu) +{ + UNPACK_VALUE(mjtNum, pyramid); + UNPACK_ARRAY(mjtNum, force); + UNPACK_ARRAY(mjtNum, mu); + CHECK_SIZE(pyramid, 2 * mu_.size()); + CHECK_SIZE(force, mu_.size() + 1); + mju_encodePyramid(pyramid_.data(), force_.data(), mu_.data(), mu_.size()); +} + +void mju_decodePyramid_wrapper(const val& force, const NumberArray& pyramid, const NumberArray& mu) +{ + UNPACK_VALUE(mjtNum, force); + UNPACK_ARRAY(mjtNum, pyramid); + UNPACK_ARRAY(mjtNum, mu); + CHECK_SIZE(pyramid, 2 * mu_.size()); + CHECK_SIZE(force, mu_.size() + 1); + mju_decodePyramid(force_.data(), pyramid_.data(), mu_.data(), mu_.size()); +} + +int mju_isZero_wrapper(const val& vec) +{ + UNPACK_VALUE(mjtNum, vec); + return mju_isZero(vec_.data(), vec_.size()); +} + +void mju_f2n_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(float, vec); + CHECK_SIZES(res, vec); + mju_f2n(res_.data(), vec_.data(), res_.size()); +} + +void mju_n2f_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(float, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_n2f(res_.data(), vec_.data(), res_.size()); +} + +void mju_d2n_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(mjtNum, res); + UNPACK_ARRAY(double, vec); + CHECK_SIZES(res, vec); + mju_d2n(res_.data(), vec_.data(), res_.size()); +} + +void mju_n2d_wrapper(const val& res, const NumberArray& vec) +{ + UNPACK_VALUE(double, res); + UNPACK_ARRAY(mjtNum, vec); + CHECK_SIZES(res, vec); + mju_n2d(res_.data(), vec_.data(), res_.size()); +} + +void mju_insertionSort_wrapper(const val& list) +{ + UNPACK_VALUE(mjtNum, list); + mju_insertionSort(list_.data(), list_.size()); +} + +void mju_insertionSortInt_wrapper(const val& list) +{ + UNPACK_VALUE(int, list); + mju_insertionSortInt(list_.data(), list_.size()); +} + +void mjd_transitionFD_wrapper(const MjModel& m, MjData& d, mjtNum eps, mjtByte flg_centered, const val& A, const val& B, const val& C, const val& D) +{ + UNPACK_NULLABLE_VALUE(mjtNum, A); + UNPACK_NULLABLE_VALUE(mjtNum, B); + UNPACK_NULLABLE_VALUE(mjtNum, C); + UNPACK_NULLABLE_VALUE(mjtNum, D); + CHECK_SIZE(A, (2 * m.nv() + m.na()) * (2 * m.nv() + m.na())); + CHECK_SIZE(B, (2 * m.nv() + m.na()) * m.nu()); + CHECK_SIZE(C, m.nsensordata() * (2 * m.nv() + m.na())); + CHECK_SIZE(D, m.nsensordata() * m.nu()); + mjd_transitionFD(m.get(), d.get(), eps, flg_centered, A_.data(), B_.data(), C_.data(), D_.data()); +} + +void mjd_inverseFD_wrapper(const MjModel& m, MjData& d, mjtNum eps, mjtByte flg_actuation, const val& DfDq, const val& DfDv, const val& DfDa, const val& DsDq, const val& DsDv, const val& DsDa, const val& DmDq) +{ + UNPACK_NULLABLE_VALUE(mjtNum, DfDq); + UNPACK_NULLABLE_VALUE(mjtNum, DfDv); + UNPACK_NULLABLE_VALUE(mjtNum, DfDa); + UNPACK_NULLABLE_VALUE(mjtNum, DsDq); + UNPACK_NULLABLE_VALUE(mjtNum, DsDv); + UNPACK_NULLABLE_VALUE(mjtNum, DsDa); + UNPACK_NULLABLE_VALUE(mjtNum, DmDq); + CHECK_SIZE(DfDq, m.nv() * m.nv()); + CHECK_SIZE(DfDv, m.nv() * m.nv()); + CHECK_SIZE(DfDa, m.nv() * m.nv()); + CHECK_SIZE(DsDq, m.nv() * m.nsensordata()); + CHECK_SIZE(DsDv, m.nv() * m.nsensordata()); + CHECK_SIZE(DsDa, m.nv() * m.nsensordata()); + CHECK_SIZE(DmDq, m.nv() * m.nM()); + mjd_inverseFD(m.get(), d.get(), eps, flg_actuation, DfDq_.data(), DfDv_.data(), DfDa_.data(), + DsDq_.data(), DsDv_.data(), DsDa_.data(), DmDq_.data()); +} + +void mjd_subQuat_wrapper(const NumberArray& qa, const NumberArray& qb, const val& Da, const val& Db) +{ + UNPACK_ARRAY(mjtNum, qa); + UNPACK_ARRAY(mjtNum, qb); + UNPACK_NULLABLE_VALUE(mjtNum, Da); + UNPACK_NULLABLE_VALUE(mjtNum, Db); + CHECK_SIZE(qa, 4); + CHECK_SIZE(qb, 4); + CHECK_SIZE(Da, 9); + CHECK_SIZE(Db, 9); + mjd_subQuat(qa_.data(), qb_.data(), Da_.data(), Db_.data()); +} + +EMSCRIPTEN_BINDINGS(mujoco_functions) { +// {{ FUNCTION_BINDINGS }} + function("error", &error_wrapper); + function("mju_printMatSparse", &mju_printMatSparse_wrapper); + function("mj_solveM", &mj_solveM_wrapper); + function("mj_solveM2", &mj_solveM2_wrapper); + function("mj_rne", &mj_rne_wrapper); + function("mj_saveLastXML", &mj_saveLastXML_wrapper); + function("mj_setLengthRange", &mj_setLengthRange_wrapper); + function("mj_constraintUpdate", &mj_constraintUpdate_wrapper); + function("mj_getState", &mj_getState_wrapper); + function("mj_setState", &mj_setState_wrapper); + function("mj_mulJacVec", &mj_mulJacVec_wrapper); + function("mj_mulJacTVec", &mj_mulJacTVec_wrapper); + function("mj_jac", &mj_jac_wrapper); + function("mj_jacBody", &mj_jacBody_wrapper); + function("mj_jacBodyCom", &mj_jacBodyCom_wrapper); + function("mj_jacSubtreeCom", &mj_jacSubtreeCom_wrapper); + function("mj_jacGeom", &mj_jacGeom_wrapper); + function("mj_jacSite", &mj_jacSite_wrapper); + function("mj_jacPointAxis", &mj_jacPointAxis_wrapper); + function("mj_jacDot", &mj_jacDot_wrapper); + function("mj_angmomMat", &mj_angmomMat_wrapper); + function("mj_fullM", &mj_fullM_wrapper); + function("mj_mulM", &mj_mulM_wrapper); + function("mj_mulM2", &mj_mulM2_wrapper); + function("mj_addM", &mj_addM_wrapper); + function("mj_applyFT", &mj_applyFT_wrapper); + function("mj_geomDistance", &mj_geomDistance_wrapper); + function("mj_differentiatePos", &mj_differentiatePos_wrapper); + function("mj_integratePos", &mj_integratePos_wrapper); + function("mj_normalizeQuat", &mj_normalizeQuat_wrapper); + function("mj_multiRay", &mj_multiRay_wrapper); + function("mju_zero", &mju_zero_wrapper); + function("mju_fill", &mju_fill_wrapper); + function("mju_copy", &mju_copy_wrapper); + function("mju_sum", &mju_sum_wrapper); + function("mju_L1", &mju_L1_wrapper); + function("mju_scl", &mju_scl_wrapper); + function("mju_add", &mju_add_wrapper); + function("mju_sub", &mju_sub_wrapper); + function("mju_addTo", &mju_addTo_wrapper); + function("mju_subFrom", &mju_subFrom_wrapper); + function("mju_addToScl", &mju_addToScl_wrapper); + function("mju_addScl", &mju_addScl_wrapper); + function("mju_normalize", &mju_normalize_wrapper); + function("mju_norm", &mju_norm_wrapper); + function("mju_dot", &mju_dot_wrapper); + function("mju_mulMatVec", &mju_mulMatVec_wrapper); + function("mju_mulMatTVec", &mju_mulMatTVec_wrapper); + function("mju_mulVecMatVec", &mju_mulVecMatVec_wrapper); + function("mju_transpose", &mju_transpose_wrapper); + function("mju_symmetrize", &mju_symmetrize_wrapper); + function("mju_eye", &mju_eye_wrapper); + function("mju_mulMatMat", &mju_mulMatMat_wrapper); + function("mju_mulMatMatT", &mju_mulMatMatT_wrapper); + function("mju_mulMatTMat", &mju_mulMatTMat_wrapper); + function("mju_sqrMatTD", &mju_sqrMatTD_wrapper); + function("mju_dense2sparse", &mju_dense2sparse_wrapper); + function("mju_sparse2dense", &mju_sparse2dense_wrapper); + function("mju_cholFactor", &mju_cholFactor_wrapper); + function("mju_cholSolve", &mju_cholSolve_wrapper); + function("mju_cholUpdate", &mju_cholUpdate_wrapper); + function("mju_cholFactorBand", &mju_cholFactorBand_wrapper); + function("mju_cholSolveBand", &mju_cholSolveBand_wrapper); + function("mju_band2Dense", &mju_band2Dense_wrapper); + function("mju_dense2Band", &mju_dense2Band_wrapper); + function("mju_bandMulMatVec", &mju_bandMulMatVec_wrapper); + function("mju_boxQP", &mju_boxQP_wrapper); + function("mju_encodePyramid", &mju_encodePyramid_wrapper); + function("mju_decodePyramid", &mju_decodePyramid_wrapper); + function("mju_isZero", &mju_isZero_wrapper); + function("mju_f2n", &mju_f2n_wrapper); + function("mju_n2f", &mju_n2f_wrapper); + function("mju_d2n", &mju_d2n_wrapper); + function("mju_n2d", &mju_n2d_wrapper); + function("mju_insertionSort", &mju_insertionSort_wrapper); + function("mju_insertionSortInt", &mju_insertionSortInt_wrapper); + function("mjd_transitionFD", &mjd_transitionFD_wrapper); + function("mjd_inverseFD", &mjd_inverseFD_wrapper); + function("mjd_subQuat", &mjd_subQuat_wrapper); + class_>("FloatBuffer") + .constructor() + .class_function("FromArray", &WasmBuffer::FromArray) + .function("GetPointer", &WasmBuffer::GetPointer) + .function("GetElementCount", &WasmBuffer::GetElementCount) + .function("GetView", &WasmBuffer::GetView); + class_>("DoubleBuffer") + .constructor() + .class_function("FromArray", &WasmBuffer::FromArray) + .function("GetPointer", &WasmBuffer::GetPointer) + .function("GetElementCount", &WasmBuffer::GetElementCount) + .function("GetView", &WasmBuffer::GetView); + class_>("IntBuffer") + .constructor() + .class_function("FromArray", &WasmBuffer::FromArray) + .function("GetPointer", &WasmBuffer::GetPointer) + .function("GetElementCount", &WasmBuffer::GetElementCount) + .function("GetView", &WasmBuffer::GetView); + register_vector("mjStringVec"); + register_vector("mjIntVec"); + register_vector("mjIntVecVec"); + register_vector("mjFloatVec"); + register_vector("mjFloatVecVec"); + register_vector("mjDoubleVec"); + // register_type gives better type information (val is mapped to any by default) + register_type("number[]"); + register_type("string"); + register_vector("mjByteVec"); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); + register_optional(); +} + +} // namespace mujoco::wasm +// NOLINTEND(whitespace/semicolon) +// NOLINTEND(whitespace/line_length) diff --git a/wasm/codegen/templates/bindings.h b/wasm/codegen/templates/bindings.h new file mode 100644 index 00000000..b599a1ca --- /dev/null +++ b/wasm/codegen/templates/bindings.h @@ -0,0 +1,348 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// NOLINTBEGIN(whitespace/line_length) +#ifndef MUJOCO_WASM_CODEGEN_GENERATED_BINDINGS_H_ +#define MUJOCO_WASM_CODEGEN_GENERATED_BINDINGS_H_ +#include +#include + +#include +#include +#include + +#include + +namespace mujoco::wasm { + +// Create the types for anonymous structs +using mjVisualGlobal = decltype(::mjVisual::global); +using mjVisualQuality = decltype(::mjVisual::quality); +using mjVisualHeadlight = decltype(::mjVisual::headlight); +using mjVisualMap = decltype(::mjVisual::map); +using mjVisualScale = decltype(::mjVisual::scale); +using mjVisualRgba = decltype(::mjVisual::rgba); + +// {{ AUTOGENNED_STRUCT_DEFINITIONS }} +struct MjVisualGlobal { + MjVisualGlobal(); + explicit MjVisualGlobal(mjVisualGlobal *ptr); + MjVisualGlobal(const MjVisualGlobal &); + MjVisualGlobal &operator=(const MjVisualGlobal &); + ~MjVisualGlobal(); + std::unique_ptr copy(); + // INSERT-GENERATED-MjVisualGlobal-DEFINITIONS + mjVisualGlobal* get() const { return ptr_; } + void set(mjVisualGlobal* ptr) { ptr_ = ptr; } + + private: + mjVisualGlobal* ptr_; + bool owned_ = false; +}; + +struct MjVisualQuality { + MjVisualQuality(); + explicit MjVisualQuality(mjVisualQuality *ptr); + MjVisualQuality(const MjVisualQuality &); + MjVisualQuality &operator=(const MjVisualQuality &); + ~MjVisualQuality(); + std::unique_ptr copy(); + // INSERT-GENERATED-MjVisualQuality-DEFINITIONS + mjVisualQuality* get() const { return ptr_; } + void set(mjVisualQuality* ptr) { ptr_ = ptr; } + + private: + mjVisualQuality* ptr_; + bool owned_ = false; +}; + +struct MjVisualHeadlight { + MjVisualHeadlight(); + explicit MjVisualHeadlight(mjVisualHeadlight *ptr); + MjVisualHeadlight(const MjVisualHeadlight &); + MjVisualHeadlight &operator=(const MjVisualHeadlight &); + ~MjVisualHeadlight(); + std::unique_ptr copy(); + // INSERT-GENERATED-MjVisualHeadlight-DEFINITIONS + mjVisualHeadlight* get() const { return ptr_; } + void set(mjVisualHeadlight* ptr) { ptr_ = ptr; } + + private: + mjVisualHeadlight* ptr_; + bool owned_ = false; +}; + +struct MjVisualMap { + MjVisualMap(); + explicit MjVisualMap(mjVisualMap *ptr); + MjVisualMap(const MjVisualMap &); + MjVisualMap &operator=(const MjVisualMap &); + ~MjVisualMap(); + std::unique_ptr copy(); + // INSERT-GENERATED-MjVisualMap-DEFINITIONS + mjVisualMap* get() const { return ptr_; } + void set(mjVisualMap* ptr) { ptr_ = ptr; } + + private: + mjVisualMap* ptr_; + bool owned_ = false; +}; + +struct MjVisualScale { + MjVisualScale(); + explicit MjVisualScale(mjVisualScale *ptr); + MjVisualScale(const MjVisualScale &); + MjVisualScale &operator=(const MjVisualScale &); + ~MjVisualScale(); + std::unique_ptr copy(); + // INSERT-GENERATED-MjVisualScale-DEFINITIONS + mjVisualScale* get() const { return ptr_; } + void set(mjVisualScale* ptr) { ptr_ = ptr; } + + private: + mjVisualScale* ptr_; + bool owned_ = false; +}; + +struct MjVisualRgba { + MjVisualRgba(); + explicit MjVisualRgba(mjVisualRgba *ptr); + MjVisualRgba(const MjVisualRgba &); + MjVisualRgba &operator=(const MjVisualRgba &); + ~MjVisualRgba(); + std::unique_ptr copy(); + // INSERT-GENERATED-MjVisualRgba-DEFINITIONS + mjVisualRgba* get() const { return ptr_; } + void set(mjVisualRgba* ptr) { ptr_ = ptr; } + + private: + mjVisualRgba* ptr_; + bool owned_ = false; +}; + +struct MjVisual { + MjVisual(); + explicit MjVisual(mjVisual *ptr_); + MjVisual(const MjVisual &); + MjVisual &operator=(const MjVisual &); + ~MjVisual(); + std::unique_ptr copy(); + // INSERT-GENERATED-MjVisual-DEFINITIONS + mjVisual* get() const { return ptr_; } + void set(mjVisual* ptr) { ptr_ = ptr; } + + private: + mjVisual* ptr_; + bool owned_ = false; + + public: + MjVisualGlobal global; + MjVisualQuality quality; + MjVisualHeadlight headlight; + MjVisualMap map; + MjVisualScale scale; + MjVisualRgba rgba; +}; + +struct MjModel { + explicit MjModel(mjModel *m); + explicit MjModel(const MjModel &other); + ~MjModel(); + std::unique_ptr copy(); + // INSERT-GENERATED-MjModel-DEFINITIONS + mjModel* get() const { return ptr_; } + void set(mjModel* ptr) { ptr_ = ptr; } + + private: + mjModel* ptr_; + + public: + MjOption opt; + MjStatistic stat; + MjVisual vis; +}; + +struct MjData { + MjData(MjModel *m); + explicit MjData(const MjModel &, const MjData &); + ~MjData(); + std::vector InitSolverArray(); + std::vector InitTimerArray(); + std::vector InitWarningArray(); + std::vector contact() const; + std::unique_ptr copy(); + // INSERT-GENERATED-MjData-DEFINITIONS + mjData* get() const { return ptr_; } + void set(mjData* ptr) { ptr_ = ptr; } + + private: + mjData* ptr_; + + public: + mjModel *model; + std::vector solver; + std::vector timer; + std::vector warning; +}; + +struct MjvScene { + MjvScene(); + MjvScene(MjModel *m, int maxgeom); + // MjvScene(const MjvScene &); + ~MjvScene(); + std::unique_ptr copy(); + int GetSumFlexFaces() const; + std::vector InitLightsArray(); + std::vector InitCameraArray(); + + std::vector geoms() const; + + emscripten::val geomorder() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->ngeom, ptr_->geomorder)); + } + emscripten::val flexedgeadr() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexedgeadr)); + } + emscripten::val flexedgenum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexedgenum)); + } + emscripten::val flexvertadr() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexvertadr)); + } + emscripten::val flexvertnum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexvertnum)); + } + emscripten::val flexfaceadr() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexfaceadr)); + } + emscripten::val flexfacenum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexfacenum)); + } + emscripten::val flexfaceused() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nflex, ptr_->flexfaceused)); + } + emscripten::val flexedge() const { + return emscripten::val( + emscripten::typed_memory_view(2 * model->nflexedge, ptr_->flexedge)); + } + emscripten::val flexvert() const { + return emscripten::val( + emscripten::typed_memory_view(3 * model->nflexvert, ptr_->flexvert)); + } + emscripten::val skinfacenum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nskin, ptr_->skinfacenum)); + } + emscripten::val skinvertadr() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nskin, ptr_->skinvertadr)); + } + emscripten::val skinvertnum() const { + return emscripten::val( + emscripten::typed_memory_view(ptr_->nskin, ptr_->skinvertnum)); + } + emscripten::val skinvert() const { + return emscripten::val( + emscripten::typed_memory_view(3 * model->nskinvert, ptr_->skinvert)); + } + emscripten::val skinnormal() const { + return emscripten::val( + emscripten::typed_memory_view(3 * model->nskinvert, ptr_->skinnormal)); + } + emscripten::val flexface() const { + return emscripten::val(emscripten::typed_memory_view( + 9 * MjvScene::GetSumFlexFaces(), ptr_->flexface)); + } + emscripten::val flexnormal() const { + return emscripten::val(emscripten::typed_memory_view( + 9 * MjvScene::GetSumFlexFaces(), ptr_->flexnormal)); + } + emscripten::val flextexcoord() const { + return emscripten::val(emscripten::typed_memory_view( + 6 * MjvScene::GetSumFlexFaces(), ptr_->flextexcoord)); + } + // INSERT-GENERATED-MjvScene-DEFINITIONS + mjvScene* get() const { return ptr_; } + void set(mjvScene* ptr) { ptr_ = ptr; } + + private: + mjvScene* ptr_; + bool owned_ = false; + + public: + mjModel *model; + std::vector lights; + std::vector camera; +}; + +struct MjSpec { + MjSpec(); + explicit MjSpec(mjSpec *ptr); + MjSpec(const MjSpec &); + MjSpec &operator=(const MjSpec &); + ~MjSpec(); + std::unique_ptr copy(); + // INSERT-GENERATED-MjSpec-DEFINITIONS + mjSpec* get() const { return ptr_; } + void set(mjSpec* ptr) { ptr_ = ptr; } + + private: + mjSpec* ptr_; + bool owned_ = false; + + public: + MjOption option; + MjVisual visual; + MjStatistic stat; + MjsCompiler compiler; + MjsElement element; +}; + +// TODO: Refactor, Structs Manually added so functions.cc compile -- // +struct MjpResourceProvider { + MjpResourceProvider(mjpResourceProvider *ptr_) { ptr = ptr_; }; + ~MjpResourceProvider() {} + mjpResourceProvider *get() const { return ptr; } + mjpResourceProvider *ptr; +}; + +struct MjpPlugin { + MjpPlugin(mjpPlugin *ptr_) { ptr = ptr_; }; + ~MjpPlugin() {} + mjpPlugin *get() const { return ptr; } + mjpPlugin *ptr; +}; + +// TODO: Factory and debug helper functions, some should be removed when +// functions are generated -- // +std::unique_ptr loadFromXML(std::string filename); +void step(MjModel *model, MjData *data); +void error(const std::string &msg); +void kinematics(MjModel *m, MjData *d); +std::unique_ptr parseXMLString(const std::string &xml); +std::unique_ptr findBody(MjSpec *spec, const std::string &name); +std::unique_ptr findGeom(MjSpec *spec, const std::string &name); + +} // namespace mujoco::wasm + +#endif // MUJOCO_WASM_CODEGEN_GENERATED_BINDINGS_H_ +// NOLINTEND(whitespace/line_length) diff --git a/wasm/codegen/update.py b/wasm/codegen/update.py new file mode 100644 index 00000000..9bae26e9 --- /dev/null +++ b/wasm/codegen/update.py @@ -0,0 +1,41 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generates WASM bindings for MuJoCo's API. + +This script leverages MuJoCo's introspect dicts to gather information +about its internal structures and then uses a code generation framework to +produce corresponding WASM bindings. +""" + +from wasm.codegen import binding_builder +from wasm.codegen.helpers import common + + +def generate_all_bindings(): + """Generates WASM bindings for MuJoCo.""" + template_path_h, generated_path_h = common.get_file_path( + "templates", "generated", "bindings.h" + ) + template_path_cc, generated_path_cc = common.get_file_path( + "templates", "generated", "bindings.cc" + ) + builder = binding_builder.BindingBuilder( + template_path_h, template_path_cc, generated_path_h, generated_path_cc + ) + builder.set_enums().set_headers().set_structs().set_functions().build() + + +if __name__ == "__main__": + generate_all_bindings() diff --git a/wasm/demo_app/app.ts b/wasm/demo_app/app.ts new file mode 100644 index 00000000..24644341 --- /dev/null +++ b/wasm/demo_app/app.ts @@ -0,0 +1,498 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as THREE from "three" +import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js" +import loadMujoco from "../dist/mujoco_wasm.js" + +declare function loadMujoco(): Promise; + +let mujoco: any; + +const modelXml = ` + + + + + `; + +// Backport of CapsuleGeometry class introduced in THREE.js r139 +class CapsuleGeometry extends THREE.BufferGeometry { + readonly parameters: { + readonly radius: number, + readonly length: number, + readonly capSegments: number, + readonly radialSegments: number + }; + + constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) { + const path = new THREE.Path(); + path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0, false); + path.absarc(0, length / 2, radius, 0, Math.PI * 0.5, false); + const latheGeometry = + new THREE.LatheGeometry(path.getPoints(capSegments), radialSegments); + + super(); + this.setIndex(latheGeometry.getIndex()); + this.setAttribute('position', latheGeometry.getAttribute('position')); + this.setAttribute('normal', latheGeometry.getAttribute('normal')); + this.setAttribute('uv', latheGeometry.getAttribute('uv')); + + this.type = 'CapsuleGeometry'; + + this.parameters = { + radius, + length, + capSegments, + radialSegments, + }; + } +} + +class App { + // TODO(matijak): We can use better types here by doing the following: + // https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#typescript-definitions + mjModel: any; + mjData: any; + mjvOption: any; + mjvPerturb: any; + mjvCamera: any; + mjvScene: any; + + paused = false; + frameId: number|null = null; + maxGeoms: number = 2 ** 15; + + scene: THREE.Scene; + renderer: THREE.WebGLRenderer; + camera: THREE.PerspectiveCamera; + controls: OrbitControls; + meshes: THREE.Mesh[] = []; + bufferGeometryCache = new Map(); + + constructor() { + this.mjvPerturb = new mujoco.MjvPerturb(); + this.mjvOption = new mujoco.MjvOption(); + this.mjvCamera = new mujoco.MjvCamera(); + + this.scene = new THREE.Scene(); + + this.renderer = new THREE.WebGLRenderer(); + this.renderer.setSize(window.innerWidth, window.innerHeight); + this.renderer.shadowMap.enabled = true; + this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; + document.body.appendChild(this.renderer.domElement); + + this.camera = new THREE.PerspectiveCamera( + 45, window.innerWidth / window.innerHeight, .1, 1000); + this.camera.up.set(0, 0, 1); // Mujoco uses z-up + this.camera.position.set(-2, 0, 2); + + this.controls = new OrbitControls(this.camera, this.renderer.domElement); + } + + dispose() { + // Release all C++ objects + if (this.mjvScene) { + this.mjvScene.delete(); + } + if (this.mjvCamera) { + this.mjvCamera.delete(); + } + if (this.mjvPerturb) { + this.mjvPerturb.delete(); + } + if (this.mjvOption) { + this.mjvOption.delete(); + } + if (this.mjData) { + this.mjData.delete(); + } + if (this.mjModel) { + this.mjModel.delete(); + } + + // Release all the THREE.js objects + this.meshes.forEach((mesh) => { + if (mesh.material) { + if (Array.isArray(mesh.material)) { + mesh.material.forEach(material => material.dispose()); + } else { + mesh.material.dispose(); + } + } + if (mesh.geometry) { + mesh.geometry.dispose(); + } + }); + this.bufferGeometryCache.clear(); + + if (this.controls) { + this.controls.dispose(); + } + + if (this.renderer) { + this.renderer.dispose(); + } + + // Stop the animation loop since we've disposed of all the data + if (this.frameId) { + cancelAnimationFrame(this.frameId); + this.frameId = null; + } + } + + loadModel(xmlContent: string) { + // Write xml as a file so that mujoco can find it + (mujoco as any).FS.writeFile('/working/model.xml', xmlContent); + + this.mjModel = mujoco.MjModel.loadFromXML('/working/model.xml'); + if (!app.mjModel) { + throw new Error('Failed to load model'); + } + this.mjData = new mujoco.MjData(this.mjModel); + if (!this.mjData) { + throw new Error('Failed to load data'); + } + + this.initScene(); + } + + pauseButton() { + this.paused = !this.paused; + const button = document.getElementById('pause-button'); + if (button) { + button.textContent = this.paused ? 'Resume' : 'Pause'; + } + } + + // TODO(matijak): Fix the bug where contact cylinders are wrong if the + // simulation is reset while they are being visualized + resetButton() { + if (this.mjModel && this.mjData) { + console.log('Resetting model and data'); + + mujoco.mj_resetData(this.mjModel, this.mjData); + mujoco.mj_forward(this.mjModel, this.mjData); + + this.clearScene(); + this.initScene(); + } + } + + contactButton() { + const index = mujoco.mjtVisFlag.mjVIS_CONTACTPOINT.value; + const value = this.mjvOption.flags[index]; + this.mjvOption.flags[index] = !value; + + const button = document.getElementById('contact-button'); + if (button) { + button.textContent = + this.mjvOption.flags[index] ? 'Hide Contacts' : 'Show Contacts'; + } + + this.clearScene(); + this.initScene(); + } + + initScene() { + this.mjvScene = new mujoco.MjvScene(this.mjModel, this.maxGeoms); + + const pointLight = new THREE.PointLight(0xffffff, .4); + pointLight.position.set(0, 0, 2); + pointLight.castShadow = true; + pointLight.shadow.mapSize.set(2048, 2048); + this.scene.add(pointLight); + + const ambientLight = new THREE.AmbientLight(0xffffff, .2); + this.scene.add(ambientLight); + + const spotLight = new THREE.SpotLight(0xffffff, .2); + spotLight.position.set(0, 0, 2); + spotLight.target.position.set(0, 0, 0); + spotLight.castShadow = true; + spotLight.shadow.mapSize.set(2048, 2048); + this.scene.add(spotLight); + this.scene.add(spotLight.target); + } + + clearScene() { + // clear cached meshes + this.meshes.forEach((mesh) => { + if (mesh.material) { + if (Array.isArray(mesh.material)) { + mesh.material.forEach(material => material.dispose()); + } else { + mesh.material.dispose(); + } + } + if (mesh.geometry) { + mesh.geometry.dispose(); + } + }); + + this.bufferGeometryCache.clear(); + this.meshes.length = 0; + while (this.scene.children.length > 0) { + this.scene.remove(this.scene.children[0]); + } + this.mjvScene.delete(); + } + + getBufferGeometry(mjvGeom: any): [boolean, THREE.BufferGeometry] { + if (!(mjvGeom instanceof mujoco.MjvGeom)) { + throw new Error('mjvGeom is not an instance of mujoco.MjvGeom'); + } + + // Lookup the geometry and return it if found + const key = JSON.stringify([mjvGeom.type, mjvGeom.size, mjvGeom.dataid]); + const found = this.bufferGeometryCache.get(key); + if (found) { + return [false, found]; + } + + // Create geometry + let geom: THREE.BufferGeometry; + if (mjvGeom.type === mujoco.mjtGeom.mjGEOM_PLANE.value) { + geom = new THREE.PlaneGeometry( + 2 * (mjvGeom.size[0] ? mjvGeom.size[0] : 10000), + 2 * (mjvGeom.size[1] ? mjvGeom.size[1] : 10000)); + const uv = geom.getAttribute('uv'); + for (let i = 0; i < uv.count; ++i) { + uv.setY(i, 1 - uv.getY(i)); + } + } else if (mjvGeom.type === mujoco.mjtGeom.mjGEOM_SPHERE.value) { + geom = new THREE.SphereGeometry(mjvGeom.size[0]); + } else if (mjvGeom.type === mujoco.mjtGeom.mjGEOM_CAPSULE.value) { + geom = new CapsuleGeometry(mjvGeom.size[0], 2 * mjvGeom.size[2], 32, 16); + geom.rotateX(0.5 * Math.PI); + } else if (mjvGeom.type === mujoco.mjtGeom.mjGEOM_BOX.value) { + geom = new THREE.BoxGeometry( + 2 * mjvGeom.size[0], 2 * mjvGeom.size[1], 2 * mjvGeom.size[2]); + } else if (mjvGeom.type === mujoco.mjtGeom.mjGEOM_CYLINDER.value) { + geom = new THREE.CylinderGeometry( + mjvGeom.size[0], mjvGeom.size[1], 2 * mjvGeom.size[2], 32); + geom.rotateX(0.5 * Math.PI); + } else if (mjvGeom.type === mujoco.mjtGeom.mjGEOM_ELLIPSOID.value) { + geom = new THREE.SphereGeometry(1); + geom.scale(mjvGeom.size[0], mjvGeom.size[1], mjvGeom.size[2]); + } else { + console.log('Unsupported geom type: ', mjvGeom.type); + geom = new THREE.BufferGeometry(); + } + + this.bufferGeometryCache.set(key, geom); + return [true, geom]; + } + + update() { + if (!this.mjModel || !this.mjData) { + return; + } + + app.controls.update(); + + // Simulate physics for 1/60 sec + if (!app.paused) { + let sim_start = app.mjData.time; + while (app.mjData.time - sim_start < 1. / 60.) { + mujoco.mj_step(app.mjModel, app.mjData); + } + } + + // Update the mujoco scene + mujoco.mjv_updateScene( + this.mjModel, this.mjData, this.mjvOption, this.mjvPerturb, + this.mjvCamera, mujoco.mjtCatBit.mjCAT_ALL.value, this.mjvScene); + + const geoms = this.mjvScene.geoms; + for (let i = 0; i < geoms.size(); i++) { + const mjvGeom = geoms.get(i); + + let mesh: THREE.Mesh; + if (i < this.meshes.length) { + mesh = this.meshes[i]; + } else { + const mjvGeom = geoms.get(i); + const [added, geom] = this.getBufferGeometry(mjvGeom); + + // Create material + let material = new THREE.MeshPhongMaterial(); + material.color.setRGB( + mjvGeom.rgba[0], mjvGeom.rgba[1], mjvGeom.rgba[2]); + material.opacity = mjvGeom.rgba[3]; + material.transparent = mjvGeom.rgba[3] !== 0; + + // Create mesh + mesh = new THREE.Mesh(geom, material); + mesh.castShadow = true; + mesh.receiveShadow = true; + + this.meshes.push(mesh); + this.scene.add(mesh); + } + + mesh.matrixAutoUpdate = false; + const sz = 1; + mesh.matrix.set( + mjvGeom.mat[0], mjvGeom.mat[1], mjvGeom.mat[2] * sz, mjvGeom.pos[0], + mjvGeom.mat[3], mjvGeom.mat[4], mjvGeom.mat[5] * sz, mjvGeom.pos[1], + mjvGeom.mat[6], mjvGeom.mat[7], mjvGeom.mat[8] * sz, mjvGeom.pos[2], + 0, 0, 0, 1); + mesh.matrixWorldNeedsUpdate = true; + + mjvGeom.delete(); + } + + geoms.delete(); + } + + render() { + this.renderer.render(this.scene, this.camera); + } + + run() { + const animate = () => { + try { + this.update(); + + this.render(); + } catch (error) { + console.error('Simulation error:', error); + } + + // Request next frame + this.frameId = requestAnimationFrame(animate); + }; + + // Request first frame + this.frameId = requestAnimationFrame(animate); + } +} + +function setupWindowEvents() { + // Add an event listener to clean up when the page is unloaded + // Tip: put "window.dispatchEvent(new Event('unload'))" in the console to test + window.addEventListener('unload', () => { + app.dispose(); + + (mujoco as any).FS.unmount('/working'); + }); + + window.addEventListener('keydown', (event) => { + if (event.code === 'Backspace') { + app.resetButton(); + } + }); + window.addEventListener('keydown', (event) => { + if (event.code === 'Space') { + app.pauseButton(); + } + }); + window.addEventListener('keydown', (event) => { + if (event.key === 'c') { + app.contactButton(); + } + }); +} + +let app: App; + +async function main() { + try { + mujoco = await loadMujoco(); + + // Set up emscripten virtual file system + (mujoco as any).FS.mkdir('/working'); + (mujoco as any).FS.mount((mujoco as any).MEMFS, {root: '.'}, '/working'); + + app = new App(); + + setupWindowEvents(); + + // Note: all elements will be destroyed with the page + const pauseButtonElement = document.getElementById('pause-button'); + if (pauseButtonElement) { + pauseButtonElement.onclick = () => app.pauseButton(); + } + const resetButtonElement = document.getElementById('reset-button'); + if (resetButtonElement) { + resetButtonElement.onclick = () => app.resetButton(); + } + const contactButtonElement = document.getElementById('contact-button'); + if (contactButtonElement) { + contactButtonElement.onclick = () => app.contactButton(); + } + + app.loadModel(modelXml); + + app.run(); + + } catch (error) { + console.error('Initialization error: ', error); + app.dispose(); + } +} +main(); diff --git a/wasm/demo_app/index.html b/wasm/demo_app/index.html new file mode 100644 index 00000000..b4cf1138 --- /dev/null +++ b/wasm/demo_app/index.html @@ -0,0 +1,41 @@ + + + + + + + MuJoCo WebAssembly + + + + +
+ + + +
+ + diff --git a/wasm/demo_app/vite.demo.config.ts b/wasm/demo_app/vite.demo.config.ts new file mode 100644 index 00000000..b84e280f --- /dev/null +++ b/wasm/demo_app/vite.demo.config.ts @@ -0,0 +1,27 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { defineConfig } from "vite" + +export default defineConfig({ + root: "demo_app", + base: "./", + build: { + outDir: "../demo-dist", + emptyOutDir: true, + }, + server: { + open: true, + }, +}) diff --git a/wasm/package-lock.json b/wasm/package-lock.json new file mode 100644 index 00000000..859f393d --- /dev/null +++ b/wasm/package-lock.json @@ -0,0 +1,1707 @@ +{ + "name": "mujoco_wasm", + "version": "1.0.0-alpha.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mujoco_wasm", + "version": "1.0.0-alpha.1", + "license": "Apache-2.0", + "devDependencies": { + "@types/jasmine": "^5.1.8", + "@types/node": "^24.1.0", + "jasmine": "^5.9.0", + "three": "^0.178.0", + "ts-node": "^10.9.2", + "typescript": "5.8.2", + "vite": "^7.0.6" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@types/jasmine": { + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.12.tgz", + "integrity": "sha512-1BzPxNsFDLDfj9InVR3IeY0ZVf4o9XV+4mDqoCfyPkbsA7dYyKAPAb2co6wLFlHcvxPlt1wShm7zQdV7uTfLGA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "24.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz", + "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", + "dev": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jasmine": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.12.0.tgz", + "integrity": "sha512-KmKeTNuH8rgAuPRL5AUsXWSdJVlDu+pgqi2dLXoZUSH/g3kR+7Ho8B7hEhwDu0fu1PLuiXZtfaxmQ/mB5wqihw==", + "dev": true, + "dependencies": { + "glob": "^10.2.2", + "jasmine-core": "~5.12.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, + "node_modules/jasmine-core": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.12.1.tgz", + "integrity": "sha512-P/UbRZ0LKwXe7wEpwDheuhunPwITn4oPALhrJEQJo6756EwNGnsK/TSQrWojBB4cQDQ+VaxWYws9tFNDuiMh2Q==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/three": { + "version": "0.178.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.178.0.tgz", + "integrity": "sha512-ybFIB0+x8mz0wnZgSGy2MO/WCO6xZhQSZnmfytSPyNpM0sBafGRVhdaj+erYh5U+RhQOAg/eXqw5uVDiM2BjhQ==", + "dev": true + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vite": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", + "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", + "dev": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/wasm/package.json b/wasm/package.json new file mode 100644 index 00000000..d590119f --- /dev/null +++ b/wasm/package.json @@ -0,0 +1,29 @@ +{ + "name": "mujoco_wasm", + "version": "1.0.0-alpha.1", + "description": "MuJoCo JavaScript Bindings", + "directories": { + "example": "examples", + "lib": "lib", + "test": "tests" + }, + "scripts": { + "test": "node --loader ts-node/esm --trace-warnings tests/run-tests.mjs", + "dev:sandbox": "vite --config tests/sandbox/vite.sandbox.config.ts", + "build:sandbox": "vite build --config tests/sandbox/vite.sandbox.config.ts", + "dev:demo": "vite --config demo_app/vite.demo.config.ts", + "build:demo": "vite build --config demo_app/vite.demo.config.ts" + }, + "author": "Google DeepMind", + "license": "Apache-2.0", + "devDependencies": { + "@types/jasmine": "^5.1.8", + "@types/node": "^24.1.0", + "jasmine": "^5.9.0", + "three": "^0.178.0", + "ts-node": "^10.9.2", + "typescript": "5.8.2", + "vite": "^7.0.6" + }, + "type": "module" +} diff --git a/wasm/tests/CMakeLists.txt b/wasm/tests/CMakeLists.txt new file mode 100644 index 00000000..91ab9b52 --- /dev/null +++ b/wasm/tests/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright 2025 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/wasm/dist") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -O3") + +set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/wasm) + +include_directories(${PROJECT_SOURCE_DIR}/include) +include_directories(${PROJECT_SOURCE_DIR}/src) +include_directories(${PROJECT_SOURCE_DIR}/wasm) + +link_directories(${CMAKE_BINARY_DIR}/lib) + +file(GLOB MUJOCO_WASM_FILES + "benchmark_test.cc" + "../unpack.cc" +) + +if(NOT MUJOCO_WASM_FILES) + message(FATAL_ERROR "No source files found") +endif() + +add_compile_options(-pthread) + +# Set Emscripten linker flags +set(EMCC_LINKER_FLAGS + "--bind" + "-s ASSERTIONS=1" + "-s ALLOW_MEMORY_GROWTH=1" + "-s EXPORT_ES6=1" + "-s MODULARIZE=1" + "-s FORCE_FILESYSTEM=1" + "-s EXPORTED_RUNTIME_METHODS=['ccall','cwrap','FS','MEMFS']" + "-s EXPORT_NAME=loadMujoco" + "-gsource-map" + "-g" + "--emit-tsd mujoco_wasm_benchmark.d.ts" +) +string (REPLACE ";" " " EMCC_LINKER_FLAGS_STR "${EMCC_LINKER_FLAGS}") + +add_executable(mujoco_wasm_benchmark ${MUJOCO_WASM_FILES}) + +set_target_properties(mujoco_wasm_benchmark PROPERTIES LINK_FLAGS "${EMCC_LINKER_FLAGS_STR}") + +target_link_libraries(mujoco_wasm_benchmark ccd lodepng mujoco tinyxml2 qhullstatic_r) + +install(TARGETS mujoco_wasm_benchmark DESTINATION ${DIVISIBLE_INSTALL_BIN_DIR}) diff --git a/wasm/tests/benchmark_test.cc b/wasm/tests/benchmark_test.cc new file mode 100644 index 00000000..412eb782 --- /dev/null +++ b/wasm/tests/benchmark_test.cc @@ -0,0 +1,54 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include +#include "unpack.h" + +namespace mujoco::wasm { +using emscripten::val; + +EMSCRIPTEN_DECLARE_VAL_TYPE(NumberArray); + +// TODO(matijak): Add a benchmark where the buffer shared with JS and C++ is +// created in C++ rather than JS. + +val BenchmarkSortNumberArray(const NumberArray& vec) { + UNPACK_ARRAY(mjtNum, vec); + std::sort(vec_.data(), vec_.data() + vec_.size()); + return val::array(vec_.data(), vec_.data() + vec_.size()); +} + +val BenchmarkSortDoubleBuffer(const val& vec) { + UNPACK_VALUE(mjtNum, vec); + std::sort(vec_.data(), vec_.data() + vec_.size()); + return vec; +} + +EMSCRIPTEN_BINDINGS(mujoco_benchmark_functions) { + emscripten::class_>("DoubleBuffer") + .constructor() + .class_function("FromArray", &WasmBuffer::FromArray) + .function("GetPointer", &WasmBuffer::GetPointer) + .function("GetElementCount", &WasmBuffer::GetElementCount) + .function("GetView", &WasmBuffer::GetView); + emscripten::function("SortNumberArray", &BenchmarkSortNumberArray); + emscripten::function("SortDoubleBuffer", &BenchmarkSortDoubleBuffer); + emscripten::register_type("number[]"); +} + +} // namespace mujoco::wasm diff --git a/wasm/tests/benchmark_test.ts b/wasm/tests/benchmark_test.ts new file mode 100644 index 00000000..9928ff55 --- /dev/null +++ b/wasm/tests/benchmark_test.ts @@ -0,0 +1,113 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'jasmine'; + +import { MainModule, DoubleBuffer } from "../dist/mujoco_wasm_benchmark" +import loadMujoco from "../dist/mujoco_wasm_benchmark.js" + +describe('MuJoCo WASM Benchmark Tests', () => { + let mujoco: MainModule; + + function isNumberArraySorted(arr: number[]): boolean { + for (let i = 0; i < arr.length - 1; i++) { + if (arr[i] > arr[i + 1]) { + return false; + } + } + return true; + } + + function verifySorted(state: any, kind: string): void { + let array: number[] = []; + if (kind === 'NumberArray') { + array = state as number[]; + } else { + const bufferView = (state as DoubleBuffer).GetView(); + array = Array.from(bufferView) as number[]; + } + + const sorted = isNumberArraySorted(array); + if (!sorted) { + console.error(` Verification failed for ${kind}. Array was ${array}`); + } + } + + function runBenchmark(iterations: number, items: number, kind: string) { + let state: any; + let func: (state: any) => any; + if (kind === 'NumberArray') { + state = new Array(items).map(() => Math.random()) + func = (state) => mujoco.SortNumberArray(state); + } else if (kind === 'DoubleBuffer') { + state = mujoco.DoubleBuffer.FromArray( + new Array(items).map(() => Math.random())); + func = (state) => mujoco.SortDoubleBuffer(state); + } else { + throw new Error(`Unsupported benchmark type: ${kind}`); + } + + // Warmup JIT compiler to get stable results + for (let i = 0; i < 1000; i++) { + const sortedState = func(state); + verifySorted(sortedState, kind); + } + + // Measurement + const totalStartTime = performance.now(); + for (let i = 0; i < iterations; i++) { + func(state); + } + const totalEndTime = performance.now(); + + if (kind === 'DoubleBuffer') { + state.delete(); + } + + // Report results + const totalTime = totalEndTime - totalStartTime; + const avgTimeMilliseconds = totalTime / iterations; + console.log(`Benchmark: "Sort ${kind} ${iterations} iterations with ${ + items} items" - AVG time per call: ${ + avgTimeMilliseconds.toFixed(2)} ms`); + return { + totalTime, avgTimeMilliseconds, + } + } + + beforeAll(async () => { + mujoco = await loadMujoco(); + }); + + it('should benchmark NumberArray and DoubleBuffer and compare results', + () => { + const na1 = runBenchmark(100, 1_000, 'NumberArray'); + const na2 = runBenchmark(100, 400_000, 'NumberArray'); + + const db1 = runBenchmark(100, 1_000, 'DoubleBuffer'); + const db2 = runBenchmark(100, 400_000, 'DoubleBuffer'); + + // The actual time should be much faster than that but the intention of + // the check is to catch huge regressions without a flakey test + const _100ms = 100; + + expect(db2.avgTimeMilliseconds).toBeLessThan(_100ms); + + expect(db1.totalTime).toBeLessThan(na1.totalTime); + expect(db2.totalTime).toBeLessThan(na2.totalTime); + + expect(db1.avgTimeMilliseconds).toBeLessThan(na1.avgTimeMilliseconds); + expect(db2.avgTimeMilliseconds).toBeLessThan(na2.avgTimeMilliseconds); + }); +}); diff --git a/wasm/tests/bindings_test.ts b/wasm/tests/bindings_test.ts new file mode 100644 index 00000000..84163e80 --- /dev/null +++ b/wasm/tests/bindings_test.ts @@ -0,0 +1,1805 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'jasmine'; + +import {MainModule, MjContact, MjContactVec, MjData, MjLROpt, MjModel, +MjOption, MjsGeom, MjSolverStat, MjSpec, MjStatistic, MjTimerStat, MjvCamera, +MjvFigure, MjvGeom, MjvGLCamera, MjvLight, MjvOption, MjvPerturb, MjvScene, +MjWarningStat} from '../dist/mujoco_wasm.js'; + +import loadMujoco from '../dist/mujoco_wasm.js' + +function assertExists(value: T | null | undefined, message?: string): +asserts value is T { + if (value === null || value === undefined) { + throw new Error(message ?? 'Expected value to be defined.'); + } +} + + +// Corresponds to bindings_test.py:TEST_XML +const TEST_XML = ` + + + + +`; + +type TypedArray =|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array| + Int32Array|Uint32Array|Float32Array|Float64Array; + +function norm(arr: number[]): number { + return Math.sqrt(arr.reduce((acc, val) => acc + val * val, 0)); +} + +function expectArraysClose(arr1: TypedArray, arr2: TypedArray, precision = 1) { + expect(arr1.length).toEqual(arr2.length); + for (let i = 0; i < arr1.length; i++) { + expect(arr1[i]).toBeCloseTo(arr2[i], precision); + } +} + +function expectArraysEqual(arr1: TypedArray, arr2: TypedArray) { + expect(arr1.length).toEqual(arr2.length); + for (let i = 0; i < arr1.length; i++) { + expect(arr1[i]).toEqual(arr2[i]); + } +} + +describe('MuJoCo WASM Bindings', () => { + let mujoco: MainModule; + let model: MjModel|null = null; + let data: MjData|null = null; + + beforeAll(async () => { + mujoco = await loadMujoco(); + }); + + function unlinkXMLFile(filename: string) { + try { + (mujoco as any).FS.unlink(filename); + } catch (e) { + console.warn(`Failed to unlink temporary XML file: ${e}`); + } + } + + function writeXMLFile(filename: string, xmlContent: string) { + try { + (mujoco as any).FS.writeFile(filename, xmlContent); + } catch (e) { + throw new Error(`Failed to write temporary XML file: ${e}`); + } + } + + beforeEach(() => { + const tempXmlFilename = '/tmp/model.xml'; + + writeXMLFile(tempXmlFilename, TEST_XML); + + model = mujoco.MjModel!.loadFromXML(tempXmlFilename); + if (!model) { + unlinkXMLFile(tempXmlFilename); + throw new Error('Failed to load model from XML'); + } + + unlinkXMLFile(tempXmlFilename); + + data = new mujoco.MjData(model); + if (!data) { + throw new Error('Failed to create data from model'); + } + }); + + afterEach(() => { + model?.delete(); + data?.delete(); + }); + + describe('Buffer API', () => { + it('should construct from an element count', () => { + const buf = new mujoco.DoubleBuffer(5); + expect(buf.GetElementCount()).toBe(5); + expect(buf.GetPointer()).toBeDefined(); + expect(buf.GetView()).toBeDefined(); + }); + + it('should construct from a Javascript array', () => { + const array: number[] = [0, 1, 4, 9, 16]; + const buf = mujoco.DoubleBuffer.FromArray(array); + expect(buf.GetElementCount()).toBe(5); + expect(buf.GetPointer()).toBeDefined(); + expect(buf.GetView()).toBeDefined(); + expectArraysClose(buf.GetView(), new Float64Array(array)); + }); + + it('should construct from Float64Array', () => { + const array = new Float64Array([1 / 11, 7 / 11]); + const buf = mujoco.DoubleBuffer.FromArray(array); + expect(buf.GetElementCount()).toBe(2); + expect(buf.GetPointer()).toBeDefined(); + expect(buf.GetView()).toBeDefined(); + expectArraysClose(buf.GetView(), array); + }); + }); + + describe('mj_addM', () => { + let simpleModel: MjModel|null = null; + let simpleData: MjData|null = null; + const tempXmlFilename = '/tmp/simple_model.xml'; + // A simpler model with nv=1 and nM=1 to avoid WASM binding bugs. + const simpleXmlContent = ` + + + + + + + +`; + + beforeEach(() => { + writeXMLFile(tempXmlFilename, simpleXmlContent); + simpleModel = mujoco.MjModel!.loadFromXML(tempXmlFilename); + assertExists(simpleModel); + simpleData = new mujoco.MjData(simpleModel); + assertExists(simpleData); + }); + + afterEach(() => { + simpleModel?.delete(); + simpleData?.delete(); + unlinkXMLFile(tempXmlFilename); + }); + + it('should compute the sparse inertia matrix', () => { + const nM = simpleModel!.nM; + const dstSparse = new mujoco.DoubleBuffer(nM); + try { + mujoco.mj_forward(simpleModel!, simpleData!); + mujoco.mj_addM( + simpleModel!, simpleData!, dstSparse, simpleModel!.M_rownnz, + simpleModel!.M_rowadr, simpleModel!.M_colind); + + expect(dstSparse.GetView().length).toBe(1); + expect(dstSparse.GetView()[0]).toBeCloseTo(1.0); + } finally { + dstSparse.delete(); + } + }); + + it('should throw an error for incorrect sparse matrix dimensions', () => { + const nM = simpleModel!.nM; + const dstSparse = new mujoco.DoubleBuffer(nM + 1); + try { + expect( + () => mujoco.mj_addM( + simpleModel!, simpleData!, dstSparse, simpleModel!.M_rownnz, + simpleModel!.M_rowadr, simpleModel!.M_colind)) + .toThrowError( + 'MuJoCo Error: [mj_addM] dst must have size 1, got 2'); + } finally { + dstSparse.delete(); + } + }); + + it('should compute the sparse inertia matrix with null pointers', () => { + const nM = simpleModel!.nM; + const dstSparse = new mujoco.DoubleBuffer(nM); + try { + mujoco.mj_forward(simpleModel!, simpleData!); + mujoco.mj_addM(simpleModel!, simpleData!, dstSparse, null, null, null); + + expect(dstSparse.GetView().length).toBe(1); + } finally { + dstSparse.delete(); + } + }); + }); + + it('should compute geom distance without returning fromto', () => { + mujoco.mj_forward(model!, data!); + const dist = mujoco.mj_geomDistance(model!, data!, 0, 2, 200, null); + expect(dist).toEqual(41.9); + }); + + it('should handle box QP solver with null optionals', () => { + const n = 5; + const res = new mujoco.DoubleBuffer(n); + const r = new mujoco.DoubleBuffer(n * (n + 7)); + const h = new mujoco.DoubleBuffer(n * n); + const g = mujoco.DoubleBuffer.FromArray(new Array(n).fill(1)); + try { + for (let i = 0; i < n; i++) { + h.GetView()[i * (n + 1)] = 1; + } + const rank = mujoco.mju_boxQP( + res, r, null, h.GetView(), g.GetView(), null as any, null as any); + expect(rank).toBeGreaterThan(-1); + } finally { + res.delete(); + r.delete(); + h.delete(); + g.delete(); + } + }); + + // Corresponds to engine_core_constraint_test.cc:TEST_F(CoreConstraintTest, + // ConstraintUpdateImpl) + it('should correctly compute constraint cost', () => { + const xmlString = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `; + const tempXmlFilename = '/tmp/model_c.xml'; + writeXMLFile(tempXmlFilename, xmlString); + const model = mujoco.MjModel!.loadFromXML(tempXmlFilename); + expect(model).not.toBeNull(); + const data = new mujoco.MjData(model!); + expect(data).not.toBeNull(); + unlinkXMLFile(tempXmlFilename); + try { + mujoco.mj_resetData(model, data!); + let steps = 0; + while (data!.ncon === 0 && steps < 100) { + mujoco.mj_step(model!, data!); + steps++; + } + mujoco.mj_forward(model!, data!); + const res = new mujoco.DoubleBuffer(data!.nefc); + mujoco.mj_mulJacVec(model!, data!, res, data!.qacc); + mujoco.mju_subFrom(res, data!.efc_aref); + const cost = mujoco.DoubleBuffer.FromArray([0]); + mujoco.mj_constraintUpdate( + model!, data!, res.GetView(), cost, /*flg_coneHessian=*/ 1); + + expect(cost.GetView()[0]).toBeCloseTo(3355.837); + + res.delete(); + cost.delete(); + } finally { + data!.delete(); + model!.delete(); + } + }); + + it('should compute body jacobian', () => { + mujoco.mj_forward(model!, data!); + const bodyId = + mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_BODY.value, 'mybox'); + const point = [0.1, 0.2, 0.3]; + const jacp = new mujoco.DoubleBuffer(3 * model!.nv); + const jacr = new mujoco.DoubleBuffer(3 * model!.nv); + try { + mujoco.mj_jac(model!, data!, jacp, jacr, point, bodyId); + expect(norm(jacp.GetView())).toBeGreaterThan(0); + expect(norm(jacr.GetView())).toBeGreaterThan(0); + expect(() => mujoco.mj_jac(model!, data!, null, null, point, bodyId)) + .not.toThrow(); + } finally { + jacp.delete(); + jacr.delete(); + } + }); + + it('should compute body frame jacobian', () => { + mujoco.mj_forward(model!, data!); + const bodyId = + mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_BODY.value, 'mybox'); + const jacp = new mujoco.DoubleBuffer(3 * model!.nv); + const jacr = new mujoco.DoubleBuffer(3 * model!.nv); + try { + mujoco.mj_jacBody(model!, data!, jacp, jacr, bodyId); + expect(norm(jacp.GetView())).toBeGreaterThan(0); + expect(norm(jacr.GetView())).toBeGreaterThan(0); + expect(() => mujoco.mj_jacBody(model!, data!, null, null, bodyId)) + .not.toThrow(); + } finally { + jacp.delete(); + jacr.delete(); + } + }); + + it('should compute body CoM jacobian', () => { + mujoco.mj_forward(model!, data!); + const bodyId = + mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_BODY.value, 'mybox'); + const jacp = new mujoco.DoubleBuffer(3 * model!.nv); + const jacr = new mujoco.DoubleBuffer(3 * model!.nv); + try { + mujoco.mj_jacBodyCom(model!, data!, jacp, jacr, bodyId); + expect(norm(jacp.GetView())).toBeGreaterThan(0); + expect(norm(jacr.GetView())).toBeGreaterThan(0); + expect(() => mujoco.mj_jacBodyCom(model!, data!, null, null, bodyId)) + .not.toThrow(); + } finally { + jacp.delete(); + jacr.delete(); + } + }); + + it('should compute geom jacobian', () => { + mujoco.mj_forward(model!, data!); + const geomId = + mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_GEOM.value, 'mybox'); + const jacp = new mujoco.DoubleBuffer(3 * model!.nv); + const jacr = new mujoco.DoubleBuffer(3 * model!.nv); + try { + mujoco.mj_jacGeom(model!, data!, jacp, jacr, geomId); + expect(norm(jacp.GetView())).toBeGreaterThan(0); + expect(norm(jacr.GetView())).toBeGreaterThan(0); + expect(() => mujoco.mj_jacGeom(model!, data!, null, null, geomId)) + .not.toThrow(); + } finally { + jacp.delete(); + jacr.delete(); + } + }); + + it('should compute point-axis jacobian', () => { + mujoco.mj_forward(model!, data!); + const bodyId = + mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_BODY.value, 'mybox'); + const point = [0.1, 0.2, 0.3]; + const axis = [0, 0, 1]; + const jacPoint = new mujoco.DoubleBuffer(3 * model!.nv); + const jacAxis = new mujoco.DoubleBuffer(3 * model!.nv); + try { + mujoco.mj_jacPointAxis( + model!, data!, jacPoint, jacAxis, point, axis, bodyId); + expect(norm(jacPoint.GetView())).toBeGreaterThan(0); + expect(norm(jacAxis.GetView())).toBeGreaterThan(0); + expect( + () => mujoco.mj_jacPointAxis( + model!, data!, null, null, point, axis, bodyId)) + .not.toThrow(); + } finally { + jacPoint.delete(); + jacAxis.delete(); + } + }); + + it('should compute jacobian time derivative', () => { + mujoco.mj_forward(model!, data!); + const bodyId = + mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_BODY.value, 'mybox'); + const point = [0.1, 0.2, 0.3]; + const jacp = new mujoco.DoubleBuffer(3 * model!.nv); + const jacr = new mujoco.DoubleBuffer(3 * model!.nv); + try { + mujoco.mj_jacDot(model!, data!, jacp, jacr, point, bodyId); + expect(jacp.GetView().length).toBe(3 * model!.nv); + expect(jacr.GetView().length).toBe(3 * model!.nv); + expect(() => mujoco.mj_jacDot(model!, data!, null, null, point, bodyId)) + .not.toThrow(); + } finally { + jacp.delete(); + jacr.delete(); + } + }); + + it('should apply external force and torque', () => { + const force = [1, .5, 1]; + const torque = [.3, 1, .22]; + const point = [0, 0, 0]; + const bodyId = + mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_BODY.value, 'mybox'); + const qfrcTarget = new mujoco.DoubleBuffer(model!.nv); + try { + mujoco.mj_forward(model!, data!); + mujoco.mj_applyFT( + model!, data!, force, torque, point, bodyId, qfrcTarget); + expect(norm(qfrcTarget.GetView())).toBeGreaterThan(0); + + qfrcTarget.GetView().fill(0); + mujoco.mj_applyFT( + model!, data!, null as any, null as any, point, bodyId, qfrcTarget); + expect(norm(qfrcTarget.GetView())).toEqual(0); + } finally { + qfrcTarget.delete(); + } + }); + + it('should compute finite-differenced transition matrices', () => { + const eps = 1e-6; + const flg_centered = 0; + const dim = 2 * model!.nv + model!.na; + const A = new mujoco.DoubleBuffer(dim * dim); + const B = new mujoco.DoubleBuffer(dim * model!.nu); + const C = new mujoco.DoubleBuffer(model!.nsensordata * dim); + const D = new mujoco.DoubleBuffer(model!.nsensordata * model!.nu); + try { + mujoco.mjd_transitionFD(model!, data!, eps, flg_centered, A, B, C, D); + expect(norm(A.GetView())).toBeGreaterThan(0); + expect(norm(B.GetView())).toBeGreaterThan(0); + expect(norm(C.GetView())).toBeGreaterThan(0); + expect( + () => mujoco.mjd_transitionFD( + model!, data!, eps, flg_centered, null, null, null, null)) + .not.toThrow(); + } finally { + A.delete(); + B.delete(); + C.delete(); + D.delete(); + } + }); + + it('should solve a system of linear equations', () => { + const n = 2; + const mat = [4, 1, 1, 3]; + const vec = [1, 2]; + const res = new mujoco.DoubleBuffer(n); + const expected = new Float64Array([1 / 11, 7 / 11]); + const matFactor = mujoco.DoubleBuffer.FromArray(mat); + try { + const rank = mujoco.mju_cholFactor(matFactor, 1e-9); + expect(rank).toBe(n); + mujoco.mju_cholSolve(res, matFactor.GetView(), vec); + const result = res.GetView(); + expect(result[0]).toBeCloseTo(expected[0]); + expect(result[1]).toBeCloseTo(expected[1]); + } finally { + res.delete(); + matFactor.delete(); + } + }); + + it('should update a Cholesky factorization', () => { + const n = 2; + const mat = [4, 1, 1, 3]; + const x = [1, 1]; + const matFactor = mujoco.DoubleBuffer.FromArray(mat); + const xBuf = mujoco.DoubleBuffer.FromArray(x); + const res = new mujoco.DoubleBuffer(n); + try { + // Factorize original matrix. + mujoco.mju_cholFactor(matFactor, 1e-9); + + // Update factorization with x. + const flg_plus = 1; + mujoco.mju_cholUpdate(matFactor, xBuf.GetView(), flg_plus); + + // Solve a system with the updated factor to verify. + // A' = A + x*x' = [[5, 2], [2, 4]] + // A' * y = vec => [[5, 2], [2, 4]] * y = [7, 6] + // Solution is y = [1, 1]. + const vec = [7, 6]; + mujoco.mju_cholSolve(res, matFactor.GetView(), vec); + const result = res.GetView(); + const expectedSolution = new Float64Array([1, 1]); + expect(result[0]).toBeCloseTo(expectedSolution[0]); + expect(result[1]).toBeCloseTo(expectedSolution[1]); + } finally { + matFactor.delete(); + xBuf.delete(); + res.delete(); + } + }); + + it('should multiply a transposed matrix by another matrix', () => { + const r1 = 3, c1 = 2, c2 = 2; + const mat1 = mujoco.DoubleBuffer.FromArray([1, 4, 2, 5, 3, 6]); + const mat2 = mujoco.DoubleBuffer.FromArray([7, 8, 9, 10, 11, 12]); + const res = new mujoco.DoubleBuffer(c1 * c2); + const expected = new Float64Array([58, 64, 139, 154]); + + try { + mujoco.mju_mulMatTMat(res, mat1.GetView(), mat2.GetView(), r1, c1, c2); + expectArraysClose(res.GetView(), expected); + } finally { + mat1.delete(); + mat2.delete(); + res.delete(); + } + }); + + it('should throw an error because of incompatible matrix sizes', () => { + const r1 = 3, c1 = 2, c2 = 2; + const mat1 = mujoco.DoubleBuffer.FromArray([1, 4, 2, 5, 3, 6]); + const mat2 = mujoco.DoubleBuffer.FromArray([7, 8, 9, 10, 11]); + const res = new mujoco.DoubleBuffer(c1 * c2); + try { + expect( + () => mujoco.mju_mulMatTMat( + res, mat1.GetView(), mat2.GetView(), r1, c1, c2)) + .toThrowError( + 'MuJoCo Error: [mju_mulMatTMat] mat2 must have size 6, got 5'); + } finally { + mat1.delete(); + mat2.delete(); + res.delete(); + } + }); + + it('should convert a dense matrix to sparse and return non-zero count', + () => { + const nr = 2; + const nc = 3; + const mat = [0.0, 1.0, 0.0, 2.0, 0.0, 3.0]; + const rownnz = new mujoco.IntBuffer(nr); + const rowadr = new mujoco.IntBuffer(nr); + const colind = new mujoco.IntBuffer(nc); + const res = new mujoco.DoubleBuffer(nc); + try { + const nnz = + mujoco.mju_dense2sparse(res, mat, nr, nc, rownnz, rowadr, colind); + expectArraysEqual(res.GetView(), new Float64Array([1.0, 2.0, 3.0])); + expectArraysEqual(rownnz.GetView(), new Int32Array([1, 2])); + expectArraysEqual(rowadr.GetView(), new Int32Array([0, 1])); + expectArraysEqual(colind.GetView(), new Int32Array([1, 0, 2])); + } finally { + res.delete(); + rownnz.delete(); + rowadr.delete(); + colind.delete(); + } + }); + + it('should convert a sparse matrix to a dense matrix', () => { + const nr = 2; + const nc = 3; + const mat = [1.0, 2.0, 3.0]; + const rownnz = [1, 2]; + const rowadr = [0, 1]; + const colind = [1, 0, 2]; + const res = new mujoco.DoubleBuffer(nr * nc); + const expected = new Float64Array([0.0, 1.0, 0.0, 2.0, 0.0, 3.0]); + try { + mujoco.mju_sparse2dense(res, mat, nr, nc, rownnz, rowadr, colind); + expectArraysEqual(res.GetView(), expected); + } finally { + res.delete(); + } + }); + + it('should throw an error when mju_eye is called with a null argument', () => { + expect(() => { + mujoco.mju_eye(null as any); + }) + .toThrowError( + 'MuJoCo Error: [mju_eye] Invalid argument. Expected a TypedArray or WasmBuffer, got null.'); + }); + + it('should return undefined', () => { + const spec = mujoco.parseXMLString(TEST_XML); + const body = mujoco.mjs_findBody(spec, 'some_name_that_doesnt_exist'); + expect(body).toBeUndefined(); + body?.delete(); + spec?.delete(); + }); + + it('should check constants values', () => { + expect(mujoco.mjNEQDATA).toBe(11); + expect(mujoco.get_mjDISABLESTRING()).toEqual([ + 'Constraint', 'Equality', 'Frictionloss', 'Limit', 'Contact', 'Spring', + 'Damper', 'Gravity', 'Clampctrl', 'Warmstart', 'Filterparent', + 'Actuation', 'Refsafe', 'Sensor', 'Midphase', 'Eulerdamp', 'AutoReset', + 'NativeCCD', 'Island' + ]); + expect(mujoco.get_mjRNDSTRING()).toEqual([ + ['Shadow', '1', 'S'], ['Wireframe', '0', 'W'], ['Reflection', '1', 'R'], + ['Additive', '0', 'L'], ['Skybox', '1', 'K'], ['Fog', '0', 'G'], + ['Haze', '1', '/'], ['Segment', '0', ','], ['Id Color', '0', ''], + ['Cull Face', '1', ''] + ]); + expect(mujoco.get_mjFRAMESTRING().length) + .toEqual(mujoco.mjtFrame.mjNFRAME.value); + expect(mujoco.get_mjVISSTRING().length) + .toEqual(mujoco.mjtVisFlag.mjNVISFLAG.value); + expect(mujoco.get_mjVISSTRING()[mujoco.mjtVisFlag.mjVIS_INERTIA.value]) + .toEqual(['Inertia', '0', 'I']); + }); + + it('should create a spec from XML', () => { + const spec = mujoco.parseXMLString(TEST_XML); + try { + expect(spec).toBeDefined(); + expect(spec!.modelname).toEqual('test'); + } finally { + spec?.delete(); + } + }); + + it('should return the max value', () => { + expect(mujoco.mju_max(10, 2)).toEqual(10); + }); + + // Corresponds to bindings_test.py:test_mju_rotVecQuat + it('should rotate a vector by a quaternion', () => { + const res = mujoco.DoubleBuffer.FromArray([0, 0, 0]); + const vec = [1, 0, 0]; + const angle = 7 * Math.PI / 12; + const quat = [Math.cos(angle), 0, 0, Math.sin(angle)]; + const expected = new Float64Array([-0.8660377, -0.5, 0]); + try { + mujoco.mju_rotVecQuat(res, vec, quat); + expectArraysClose(res.GetView(), expected, 4); + } finally { + res.delete(); + } + }); + + it('should get correct geom name', () => { + const spec = mujoco.parseXMLString(TEST_XML); + const geomEl = + mujoco.mjs_findElement(spec, mujoco.mjtObj.mjOBJ_GEOM, 'myplane'); + try { + assertExists(geomEl); + const geom = mujoco.mjs_asGeom(geomEl); + assertExists(geom); + const geomName = mujoco.mjs_getName(geom.element); + expect(geomName).toEqual('myplane'); + mujoco.mjs_setName(geom.element, 'myplane2'); + expect(mujoco.mjs_getName(geom.element)).toEqual('myplane2'); + } finally { + spec?.delete(); + } + }); + + it('should override geom userdata', () => { + const spec = mujoco.parseXMLString(TEST_XML); + const geomEl = + mujoco.mjs_findElement(spec, mujoco.mjtObj.mjOBJ_GEOM, 'myplane'); + try { + assertExists(geomEl); + const geom = mujoco.mjs_asGeom(geomEl); + assertExists(geom); + geom.userdata.set(0, 10); + expect(geom.userdata.get(0)).toEqual(10); + } finally { + spec.delete(); + } + }) + + it('should override model geom_rgba', () => { + const newValues = new Float32Array([0.1, 0.2, 0.3, 0.4]); + model!.geom_rgba.set(newValues); + const expected = new Float32Array( + [0.1, 0.2, 0.3, 0.4, 0.5, 0.5, 0.5, 1, 0.5, 0.5, 0.5, 1]); + expectArraysClose(model!.geom_rgba, expected); + }) + + it('should add a scaled vector to another vector', () => { + const res = mujoco.DoubleBuffer.FromArray([1, 2, 3]); + const vec = [4, 5, 6]; + const scale = 2; + const expected = new Float64Array([9, 12, 15]); + try { + mujoco.mju_addToScl(res, vec, scale); + expectArraysClose(res.GetView(), expected, 4); + } finally { + res.delete(); + } + }); + + it('should throw an error when mju_addToScl is called with incompatible sizes', + () => { + const res = mujoco.DoubleBuffer.FromArray([1, 2, 3]); + const vec = [4, 5]; + const scale = 2; + try { + expect(() => { + mujoco.mju_addToScl(res, vec, scale); + }) + .toThrowError( + 'MuJoCo Error: [mju_addToScl] res and vec must have equal size, got 3 and 2'); + } finally { + res.delete(); + } + }); + + it('should sort an array with insertion sort', () => { + const arr = mujoco.DoubleBuffer.FromArray([5, 2, 8, 1, 9]); + const expected = new Float64Array([1, 2, 5, 8, 9]); + try { + mujoco.mju_insertionSort(arr); + expectArraysEqual(arr.GetView(), expected); + } finally { + arr.delete(); + } + }); + + it('should find the attached spec', () => { + const bXml = ` + + + + + + + + + `; + const xmlWithAttachedSpec = ` + + + + + + + + + + `; + const mainXmlFilename = 'main.xml'; + const bXmlFilename = 'b.xml'; + writeXMLFile(mainXmlFilename, xmlWithAttachedSpec); + writeXMLFile(bXmlFilename, bXml); + const spec = mujoco.parseXMLString(xmlWithAttachedSpec); + const attachedSpec = mujoco.mjs_findSpec(spec, 'b'); + + try { + assertExists(attachedSpec); + const geomEl = mujoco.mjs_findElement( + attachedSpec, mujoco.mjtObj.mjOBJ_GEOM, 'attached_geom_b'); + assertExists(geomEl); + const geom = mujoco.mjs_asGeom(geomEl); + expect(geom).toBeDefined(); + expect(mujoco.mjs_getName(geom!.element)).toEqual('attached_geom_b'); + } finally { + attachedSpec?.delete(); + spec?.delete(); + unlinkXMLFile(mainXmlFilename); + unlinkXMLFile(bXmlFilename); + } + }); + + // Corresponds to bindings_test.py:test_load_xml_can_handle_name_clash + it('should handle name clashes when loading XML with includes', () => { + const xml1 = ` + + + + + + + + `; + const xml2 = ``; + const xml3 = ``; + + const modelXmlFilename = 'model.xml'; + const model1XmlFilename = 'model_.xml'; + const model2XmlFilename = 'model__.xml'; + + writeXMLFile(modelXmlFilename, xml1); + writeXMLFile(model1XmlFilename, xml2); + writeXMLFile(model2XmlFilename, xml3); + + const model = mujoco.MjModel!.loadFromXML(modelXmlFilename); + + try { + expect(model).toBeDefined(); + expect(mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_GEOM.value, 'plane')) + .toBe(0); + expect(mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_GEOM.value, 'box')) + .toBe(1); + expect(mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_GEOM.value, 'ball')) + .toBe(2); + } finally { + model?.delete(); + unlinkXMLFile(modelXmlFilename); + unlinkXMLFile(model1XmlFilename); + unlinkXMLFile(model2XmlFilename); + } + }); + + // Corresponds to bindings_test.py:test_can_read_array + it('should read an array from the model', () => { + const expected = + new Float64Array([0, 0, 0, 0, 0, 0.1, 0, 0, 0, 0, 0, 0, 42, 0, 42]); + const bodyPos = new Float64Array(model!.body_pos); + expectArraysEqual(bodyPos, expected); + }); + + // Corresponds to bindings_test.py:test_can_set_array + it('should set an array from the data', () => { + const value = 0.12345; + data!.qpos.fill(value); + const expected = new Float64Array(data!.qpos.length).fill(value); + expectArraysEqual(data!.qpos, expected); + }); + + // Corresponds to bindings_test.py:test_array_is_a_view + it('should check that array is a view', () => { + const qposRef = data!.qpos; + const value = 0.789; + data!.qpos.fill(value); + const expected = new Float64Array(data!.qpos.length).fill(value); + expectArraysEqual(qposRef, expected); + }); + + // Corresponds to bindings_test.py:test_mjmodel_can_read_and_write_opt + it('should read and write MjOption', () => { + expect(model!.opt.timestep).toEqual(0.002); + expectArraysEqual(model!.opt.gravity, new Float64Array([0, 0, -9.81])); + + const optRef = model!.opt; + model!.opt.timestep = 0.001; + expect(optRef.timestep).toEqual(0.001); + + const gravityRef = optRef.gravity; + model!.opt.gravity[1] = 0.1; + expectArraysEqual(gravityRef, new Float64Array([0, 0.1, -9.81])); + + model!.opt.gravity.fill(0.2); + expectArraysEqual(gravityRef, new Float64Array([0.2, 0.2, 0.2])); + }); + + // Corresponds to bindings_test.py:test_mjmodel_can_read_and_write_stat + it('should read and write MjStat', () => { + expect(model!.stat.meanmass).not.toEqual(0); + + const statRef = model!.stat; + model!.stat.meanmass = 1.2; + expect(statRef.meanmass).toEqual(1.2); + }); + + // Corresponds to bindings_test.py:test_mjmodel_can_read_and_write_vis + it('should read and write MjVis', () => { + expect(model!.vis.quality.shadowsize).toEqual(51); + + const visRef = model!.vis; + model!.vis.quality.shadowsize = 100; + expect(visRef.quality.shadowsize).toEqual(100); + }); + + // Corresponds to bindings_test.py:test_mjmodel_can_access_names_directly + it('should access names directly from the model', () => { + const modelName = new TextDecoder().decode( + model!.names.slice(0, model!.names.indexOf(0))); + expect(modelName).toEqual('test'); + + const startGeomNameIndex = model!.name_geomadr[0]; + const endGeomNameIndex = model!.names.indexOf(0, startGeomNameIndex); + const geomName = new TextDecoder().decode( + model!.names.slice(startGeomNameIndex, endGeomNameIndex)); + expect(geomName).toEqual('myplane'); + }); + + // Corresponds to bindings_test.py:test_mjmodel_names_doesnt_copy + it('should not copy names when accessing them multiple times', () => { + const names1 = model!.names; + const names2 = model!.names; + expect(names1).toEqual(names2); + }); + + // Corresponds to bindings_test.py:test_mjoption_can_make_default + it('should create a default MjOption', () => { + const opt = new mujoco.MjOption(); + expect(opt.timestep).toEqual(0.002); + expectArraysEqual(opt.gravity, new Float64Array([0, 0, -9.81])); + }); + + // Corresponds to bindings_test.py:test_mjoption_can_copy + it('should copy MjOption', () => { + const opt1 = new mujoco.MjOption(); + opt1.timestep = 0.001; + opt1.gravity.set([2, 2, 2]); + + const opt2 = opt1.copy(); + expect(opt2.timestep).toEqual(0.001); + expectArraysEqual(opt2.gravity, new Float64Array([2, 2, 2])); + + opt1.timestep = 0.005; + opt1.gravity.set([5, 5, 5]); + expect(opt2.timestep).toEqual(0.001); + expectArraysEqual(opt2.gravity, new Float64Array([2, 2, 2])); + }); + + // Corresponds to bindings_test.py:test_mjdata_can_read_warning_array + it('should read warning array from MjData', () => { + expect(data!.warning.size()).toEqual(mujoco.mjtWarning.mjNWARNING.value); + data!.qpos[0] = NaN; + mujoco.mj_checkPos(model!, data!); + expect(data!.warning.get(mujoco.mjtWarning.mjWARN_BADQPOS.value)!.number) + .toEqual(1); + }); + + // Corresponds to bindings_test.py:test_mjcontact_can_copy + it('should copy MjContact', () => { + mujoco.mj_forward(model!, data!!); + const contacts: MjContactVec = data!.contact; + const originalContact = contacts.get(0)!; + const originalPos = new Float64Array(originalContact.pos); + const copiedContact = originalContact.copy(); + copiedContact.delete(); + + expectArraysClose(originalContact.pos, originalPos); + originalContact.delete(); + }); + + // Corresponds to bindings_test.py:test_mj_step + it('should step the simulation forward', () => { + const displacement = 0.55; + data!.qpos[2] += displacement; + mujoco.mj_forward(model!, data!); + + const gravity = -model!.opt.gravity[2]; + const expectedContactTime = Math.sqrt(2 * displacement / gravity); + + model!.opt.timestep = 2 ** -9; + expect(data!.time).toEqual(0); + while (data!.time < expectedContactTime) { + expect(data!.ncon).toEqual(0); + expect(data!.efc_type.length).toEqual(0); + const prevTime = data!.time; + mujoco.mj_step(model!, data!); + expect(data!.time).toEqual(prevTime + model!.opt.timestep); + } + mujoco.mj_forward(model!, data!); + const contact = data!.contact; + expect(data!.ncon).toEqual(4); + expect(data!.efc_type.length).toEqual(16); + + expectArraysClose( + contact.get(0)!.pos.slice(0, 2), new Float64Array([-0.1, -0.1])); + expectArraysClose( + contact.get(1)!.pos.slice(0, 2), new Float64Array([0.1, -0.1])); + expectArraysClose( + contact.get(2)!.pos.slice(0, 2), new Float64Array([-0.1, 0.1])); + expectArraysClose( + contact.get(3)!.pos.slice(0, 2), new Float64Array([0.1, 0.1])); + + mujoco.mj_resetData(model!, data!); + expect(data!.ncon).toEqual(0); + expect(data!.efc_type.length).toEqual(0); + }); + + // Corresponds to bindings_test.py:test_mj_struct_equality_array + it('should check MjContact equality with array', () => { + const contact1 = new mujoco.MjContact(); + const contact2 = new mujoco.MjContact(); + try { + contact1.H[3] = 1; + expect(contact1.H).not.toEqual(contact2.H); + contact2.H[3] = 1; + expect(contact1).toEqual(contact2); + } finally { + contact1.delete(); + contact2.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mj_struct_list_equality + it('should check MjContactVec equality', () => { + const tempXmlFilename2 = '/tmp/model2.xml'; + writeXMLFile(tempXmlFilename2, TEST_XML); + const model2 = mujoco.MjModel!.loadFromXML(tempXmlFilename2); + const data2 = new mujoco.MjData(model2); + try { + mujoco.mj_forward(model!, data!); + expect(data!.ncon).toEqual(4); + mujoco.mj_forward(model2, data2); + expect(data2.ncon).toEqual(4); + expect(data2.contact).toEqual(data!.contact); + + data!.qpos[3] = Math.cos(Math.PI / 8); + data!.qpos[4] = Math.sin(Math.PI / 8); + data!.qpos[5] = 0; + data!.qpos[6] = 0; + data!.qpos[2] = (Math.sqrt(2) - 1) * 0.1 - 1e-6; + mujoco.mj_forward(model!, data!); + + expect(data!.ncon).toEqual(2); + expect(data2.contact.size()).not.toEqual(data!.contact.size()); + expect(data!.contact).not.toBe(data!.warning); + } finally { + model2.delete(); + data2.delete(); + unlinkXMLFile(tempXmlFilename2); + } + }); + + // Corresponds to bindings_test.py:test_getsetstate + it('should get and set the state', () => { + mujoco.mj_step(model!, data!); + + const invalidSig = 2 ** mujoco.mjtState.mjNSTATE.value; + expect(() => { + mujoco.mj_stateSize(model!, invalidSig); + }) + .toThrowError( + 'MuJoCo Error: mj_stateSize: invalid state signature 8192 >= 2^mjNSTATE'); + + const sig = mujoco.mjtState.mjSTATE_INTEGRATION.value; + const size = mujoco.mj_stateSize(model!, sig); + const stateBadSize = mujoco.DoubleBuffer.FromArray([size]); + expect(() => { + mujoco.mj_getState(model!, data!, stateBadSize, sig); + }) + .toThrowError( + 'MuJoCo Error: [mj_getState] state must have size 81, got 1'); + + const state0 = new mujoco.DoubleBuffer(size); + mujoco.mj_getState(model!, data!, state0, sig); + + mujoco.mj_step(model!, data!); + const state1a = mujoco.DoubleBuffer.FromArray(new Array(size).fill(1)); + mujoco.mj_getState(model!, data!, state1a, sig); + + mujoco.mj_setState(model!, data!, state0.GetView(), sig); + mujoco.mj_step(model!, data!); + const state1b = mujoco.DoubleBuffer.FromArray(new Array(size).fill(2)); + mujoco.mj_getState(model!, data!, state1b, sig); + + expectArraysEqual(state1a.GetView(), state1b.GetView()); + }); + + // Corresponds to bindings_test.py:test_mj_setKeyframe + it('should set and reset a keyframe', () => { + mujoco.mj_step(model!, data!); + + const invalidKey = 2; + expect(() => { + mujoco.mj_setKeyframe(model!, data!, invalidKey); + }) + .toThrowError( + 'MuJoCo Error: mj_setKeyframe: index must be smaller than 2 (keyframes allocated in model)'); + + const validKey = 1; + const time = data!.time; + const qpos = new Float64Array(data!.qpos); + const qvel = new Float64Array(data!.qvel); + const act = new Float64Array(data!.act); + mujoco.mj_setKeyframe(model!, data!, validKey); + + mujoco.mj_step(model!, data!); + expect(time).not.toEqual(data!.time); + + mujoco.mj_resetDataKeyframe(model!, data!, validKey); + expect(time).toEqual(data!.time); + expectArraysEqual(qpos, data!.qpos); + expectArraysEqual(qvel, data!.qvel); + expectArraysEqual(act, data!.act); + }); + + // Corresponds to bindings_test.py:test_mj_angmomMat + it('should compute angular momentum matrix', () => { + data!.qvel.fill(1); + mujoco.mj_forward(model!, data!); + mujoco.mj_subtreeVel(model!, data!); + + const mat = new mujoco.DoubleBuffer(3 * model!.nv); + try { + mujoco.mj_angmomMat(model!, data!, mat, 0); + + const qvel = new Float64Array(data!.qvel); + const subtreeAngmom = new Float64Array(data!.subtree_angmom.slice(0, 3)); + const matView = mat.GetView(); + const result = new Float64Array(3).fill(0); + + for (let i = 0; i < 3; i++) { + for (let j = 0; j < model!.nv; j++) { + result[i] += matView[i * model!.nv + j] * qvel[j]; + } + } + expectArraysClose(result, subtreeAngmom); + } finally { + mat.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mj_jacSite + it('should compute site jacobian', () => { + mujoco.mj_forward(model!, data!); + const siteId = + mujoco.mj_name2id(model!, mujoco.mjtObj.mjOBJ_SITE.value, 'mysite'); + const jacp = new mujoco.DoubleBuffer(3 * model!.nv); + const jacr = new mujoco.DoubleBuffer(3 * model!.nv); + + try { + mujoco.mj_jacSite(model!, data!, jacp, null, siteId); + const expectedJacp = new Float64Array(3 * model!.nv).fill(0); + expectedJacp[6] = -1; + expectArraysClose(jacp.GetView(), expectedJacp); + + mujoco.mj_jacSite(model!, data!, null, jacr, siteId); + const expectedJacr = new Float64Array(3 * model!.nv).fill(0); + expectedJacr[1 * model!.nv + 6] = 1; + expectArraysClose(jacr.GetView(), expectedJacr, 2); + + jacp.GetView().fill(0); + jacr.GetView().fill(0); + mujoco.mj_jacSite(model!, data!, jacp, jacr, siteId); + expectArraysClose(jacp.GetView(), expectedJacp, 2); + expectArraysClose(jacr.GetView(), expectedJacr, 2); + + const badJacp = new mujoco.DoubleBuffer(3 * 6); + try { + expect(() => { + mujoco.mj_jacSite(model!, data!, badJacp, null, siteId); + }) + .toThrowError( + 'MuJoCo Error: [mj_jacSite] jacp must have size 30, got 18'); + } finally { + badJacp.delete(); + } + + const badJacr = new mujoco.DoubleBuffer(4 * 7); + try { + expect(() => { + mujoco.mj_jacSite(model!, data!, null, badJacr, siteId); + }) + .toThrowError( + 'MuJoCo Error: [mj_jacSite] jacr must have size 30, got 28'); + } finally { + badJacr.delete(); + } + } finally { + jacp.delete(); + jacr.delete(); + } + }); + + // Corresponds to bindings_test.py:test_can_initialize_mjv_structs + it('should initialize mjv structs', () => { + expect(new mujoco.MjvScene()).toBeDefined(); + expect(new mujoco.MjvCamera()).toBeDefined(); + expect(new mujoco.MjvGLCamera()).toBeDefined(); + expect(new mujoco.MjvGeom()).toBeDefined(); + expect(new mujoco.MjvLight()).toBeDefined(); + expect(new mujoco.MjvOption()).toBeDefined(); + expect(new mujoco.MjvFigure()).toBeDefined(); + expect(new mujoco.MjvScene(model, 100)).toBeDefined(); + }); + + // Corresponds to bindings_test.py:test_mjv_camera + it('should handle MjvCamera correctly', () => { + const camera = new mujoco.MjvCamera(); + camera.type = mujoco.mjtCamera.mjCAMERA_TRACKING.value; + camera.fixedcamid = 2 ** 31 - 1; + expect(camera.fixedcamid).toEqual(2 ** 31 - 1); + }); + + // Corresponds to bindings_test.py:test_mjv_scene + it('should handle MjvScene correctly', () => { + const scene = new mujoco.MjvScene(model, 100); + expect(scene.ngeom).toEqual(0); + expect(scene.maxgeom).toEqual(100); + expect(scene.geoms.size()).toEqual(0); + + mujoco.mj_forward(model!, data!); + mujoco.mjv_updateScene( + model!, data!, new mujoco.MjvOption(), new mujoco.MjvPerturb(), + new mujoco.MjvCamera(), mujoco.mjtCatBit.mjCAT_ALL.value, scene); + expect(scene.geoms.size()).toEqual(scene.ngeom); + expect(scene.ngeom).toBeGreaterThan(0); + }); + + // Corresponds to bindings_test.py:test_mjv_scene_without_model + it('should initialize MjvScene without a model', () => { + const scene = new mujoco.MjvScene(); + expect(scene.scale).toEqual(1.0); + expect(scene.maxgeom).toEqual(0); + }); + + // Corresponds to bindings_test.py:test_inverse_fd_none + it('should compute inverse dynamics derivatives with null outputs', () => { + const eps = 1e-6; + const flg_centered = 0; + expect( + () => mujoco.mjd_inverseFD( + model!, data!, eps, flg_centered, null, null, null, null, null, + null, null)) + .not.toThrow(); + }); + + // Corresponds to bindings_test.py:test_inverse_fd + it('should compute inverse dynamics derivatives', () => { + const eps = 1e-6; + const flg_centered = 0; + + const nv = model!.nv; + const nsensordata = model!.nsensordata; + const nM = model!.nM; + + const dfDq = new mujoco.DoubleBuffer(nv * nv); + const dfDv = new mujoco.DoubleBuffer(nv * nv); + const dfDa = new mujoco.DoubleBuffer(nv * nv); + const dsDq = new mujoco.DoubleBuffer(nv * nsensordata); + const dsDv = new mujoco.DoubleBuffer(nv * nsensordata); + const dsDa = new mujoco.DoubleBuffer(nv * nsensordata); + const dmDq = new mujoco.DoubleBuffer(nv * nM); + + try { + mujoco.mjd_inverseFD( + model!, data!, eps, flg_centered, dfDq, dfDv, dfDa, dsDq, dsDv, dsDa, + dmDq); + + expect(norm(dfDq.GetView())).toBeGreaterThan(eps); + expect(norm(dfDv.GetView())).toBeGreaterThan(eps); + expect(norm(dfDa.GetView())).toBeGreaterThan(eps); + expect(norm(dsDq.GetView())).toBeGreaterThan(eps); + expect(norm(dsDv.GetView())).toBeGreaterThan(eps); + expect(norm(dsDa.GetView())).toBeGreaterThan(eps); + } finally { + dfDq.delete(); + dfDv.delete(); + dfDa.delete(); + dsDq.delete(); + dsDv.delete(); + dsDa.delete(); + dmDq.delete(); + } + }); + + // Corresponds to bindings_test.py:test_geom_distance + it('should compute geom distance', () => { + mujoco.mj_forward(model!, data!); + const fromto = new mujoco.DoubleBuffer(6); + try { + const dist = mujoco.mj_geomDistance(model!, data!, 0, 2, 200, fromto); + expect(dist).toEqual(41.9); + expectArraysClose( + fromto.GetView(), + new Float64Array([42.0, 0.0, 0.0, 42.0, 0.0, 41.9])); + } finally { + fromto.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mjd_sub_quat + it('should compute sub quaternion derivatives', () => { + const quat1 = [0.2, 0.3, 0.3, 0.4]; + const quat2 = [0.1, 0.2, 0.4, 0.5]; + const d1 = new mujoco.DoubleBuffer(9); + const d2 = new mujoco.DoubleBuffer(9); + const d3 = new mujoco.DoubleBuffer(9); + const d4 = new mujoco.DoubleBuffer(9); + try { + mujoco.mjd_subQuat(quat1, quat2, d1, d2); + mujoco.mjd_subQuat(quat1, quat2, null, d3); + mujoco.mjd_subQuat(quat1, quat2, d4, null); + expectArraysEqual(d2.GetView(), d3.GetView()); + expectArraysEqual(d1.GetView(), d4.GetView()); + } finally { + d1.delete(); + d2.delete(); + d3.delete(); + d4.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mjd_quat_integrate + it('should compute quaternion derivatives for integration', () => { + const scale = 0.1; + const vel = [0.2, 0.3, 0.3]; + const dQuat = new mujoco.DoubleBuffer(9); + const dVel = new mujoco.DoubleBuffer(9); + const dH = new mujoco.DoubleBuffer(3); + try { + mujoco.mjd_quatIntegrate(vel, scale, dQuat, dVel, dH); + expect(norm(dQuat.GetView())).toBeGreaterThan(0); + expect(norm(dVel.GetView())).toBeGreaterThan(0); + expect(norm(dH.GetView())).toBeGreaterThan(0); + } finally { + dQuat.delete(); + dVel.delete(); + dH.delete(); + } + }); + + // Corresponds to bindings_test.py:test_banded + it('should handle banded matrices', () => { + const nTotal = 4; + const nBand = 1; + const nDense = 1; + const dense = [ + 1.0, + 0, + 0, + 0.1, + 0, + 2.0, + 0, + 0.2, + 0, + 0, + 3.0, + 0.3, + 0.1, + 0.2, + 0.3, + 4.0, + ]; + const band = + new mujoco.DoubleBuffer(nBand * (nTotal - nDense) + nDense * nTotal); + const vec = mujoco.DoubleBuffer.FromArray([2.0, 2.0, 3.0, 4.0]); + const res = new mujoco.DoubleBuffer(4); + try { + mujoco.mju_dense2Band(band, dense, nTotal, nBand, nDense); + for (let i = 0; i < 4; i++) { + const index = mujoco.mju_bandDiag(i, nTotal, nBand, nDense); + expect(band.GetView()[index]).toEqual(i + 1); + } + const dense2 = new mujoco.DoubleBuffer(nTotal * nTotal); + const flgSym = 1; + mujoco.mju_band2Dense( + dense2, band.GetView(), nTotal, nBand, nDense, flgSym); + expectArraysEqual(new Float64Array(dense), dense2.GetView()); + + const nVec = 1; + mujoco.mju_bandMulMatVec( + res, band.GetView(), vec.GetView(), nTotal, nBand, nDense, nVec, + flgSym); + + const expected = new Float64Array([2.4, 4.8, 10.2, 17.5]); + expectArraysClose(res.GetView(), expected); + + const diagAdd = 0; + const diagMul = 0; + mujoco.mju_cholFactorBand(band, nTotal, nBand, nDense, diagAdd, diagMul); + mujoco.mju_cholSolveBand( + res, band.GetView(), vec.GetView(), nTotal, nBand, nDense); + + const expectedSolved = new Float64Array([1.9111, 0.9111, 0.9111, 0.8333]); + expectArraysClose(res.GetView(), expectedSolved); + } finally { + band.delete(); + vec.delete(); + res.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mju_box_qp + it('should handle box QP solver', () => { + const n = 5; + const res = new mujoco.DoubleBuffer(n); + const r = new mujoco.DoubleBuffer(n * (n + 7)); + const index = new mujoco.IntBuffer(n); + const h = new mujoco.DoubleBuffer(n * n); + const g = mujoco.DoubleBuffer.FromArray(new Array(n).fill(1)); + const lower = new Array(n).fill(-1); + const upper = new Array(n).fill(1); + try { + for (let i = 0; i < n; i++) { + h.GetView()[i * (n + 1)] = 1; + } + const rank = mujoco.mju_boxQP( + res, r, index, h.GetView(), g.GetView(), lower, upper); + expect(rank).toBeGreaterThan(-1); + } finally { + res.delete(); + r.delete(); + index.delete(); + h.delete(); + g.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mju_fill + it('should fill an array with a value', () => { + const res = new mujoco.DoubleBuffer(3); + try { + mujoco.mju_fill(res, 1.5); + expectArraysEqual(res.GetView(), new Float64Array([1.5, 1.5, 1.5])); + } finally { + res.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mju_eye + it('should create an identity matrix', () => { + const eye3 = new mujoco.DoubleBuffer(3 * 3); + try { + mujoco.mju_eye(eye3); + const expected = new Float64Array([ + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + ]); + expectArraysEqual(eye3.GetView(), expected); + } finally { + eye3.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mju_symmetrize + it('should symmetrize a matrix', () => { + const mat = [ + 0, 0.066, 0.13, 0.2, 0.26, 0.33, 0.4, 0.46, 0.53, 0.6, 0.66, 0.73, 0.8, + 0.86, 0.93, 1 + ]; + const res = new mujoco.DoubleBuffer(16); + try { + mujoco.mju_symmetrize(res, mat, 4); + const expected = new Float64Array([ + 0, 0.163, 0.33, 0.5, 0.163, 0.33, 0.5, 0.66, 0.33, 0.5, 0.66, 0.83, 0.5, + 0.66, 0.8, 1 + ]); + expectArraysClose(res.GetView(), expected); + } finally { + res.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mju_clip + it('should clip a value', () => { + expect(mujoco.mju_clip(1.5, 1.0, 2.0)).toEqual(1.5); + expect(mujoco.mju_clip(1.5, 2.0, 3.0)).toEqual(2.0); + expect(mujoco.mju_clip(1.5, 0.0, 1.0)).toEqual(1.0); + }); + + // Corresponds to bindings_test.py:test_mju_mul_vec_mat_vec + it('should multiply a vector by a matrix and a vector', () => { + const vec1 = [1.0, 2.0, 3.0]; + const vec2 = [3.0, 2.0, 1.0]; + const mat = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]; + expect(mujoco.mju_mulVecMatVec(vec1, mat, vec2)).toEqual(204.0); + }); + + // Corresponds to bindings_test.py:test_mju_dense_to_sparse + it('should convert a dense matrix to a sparse matrix', () => { + const mat = [0.0, 1.0, 0.0, 2.0, 0.0, 3.0]; + const res = new mujoco.DoubleBuffer(3); + const rowNnz = new mujoco.IntBuffer(2); + const rowAdr = new mujoco.IntBuffer(2); + const colInd = new mujoco.IntBuffer(3); + try { + const status = + mujoco.mju_dense2sparse(res, mat, 2, 3, rowNnz, rowAdr, colInd); + expect(status).toEqual(0); + expectArraysEqual(res.GetView(), new Float64Array([1.0, 2.0, 3.0])); + expectArraysEqual(rowNnz.GetView(), new Int32Array([1, 2])); + expectArraysEqual(rowAdr.GetView(), new Int32Array([0, 1])); + expectArraysEqual(colInd.GetView(), new Int32Array([1, 0, 2])); + } finally { + rowNnz.delete(); + rowAdr.delete(); + colInd.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mju_sparse_to_dense + it('should convert a sparse matrix to a dense matrix', () => { + const mat = [1.0, 2.0, 3.0]; + const expected = new Float64Array([0.0, 1.0, 0.0, 2.0, 0.0, 3.0]); + const rowNnz = [1, 2]; + const rowAdr = [0, 1]; + const colInd = [1, 0, 2]; + const res = new mujoco.DoubleBuffer(6); + try { + mujoco.mju_sparse2dense(res, mat, 2, 3, rowNnz, rowAdr, colInd); + expectArraysEqual(res.GetView(), expected); + } finally { + res.delete(); + } + }); + + // Corresponds to bindings_test.py:test_mju_euler_to_quat + it('should convert euler to quaternion', () => { + const quat = new mujoco.DoubleBuffer(4); + const euler = [0, Math.PI / 2, 0]; + const seq = 'xyz'; + try { + mujoco.mju_euler2Quat(quat, euler, seq); + const expectedQuat = [Math.sqrt(0.5), 0, Math.sqrt(0.5), 0.0]; + expectArraysClose(quat.GetView(), new Float64Array(expectedQuat)); + + expect(() => { + mujoco.mju_euler2Quat(quat, euler, 'xy'); + }) + .toThrowError( + 'MuJoCo Error: mju_euler2Quat: seq must contain exactly 3 characters'); + expect(() => { + mujoco.mju_euler2Quat(quat, euler, 'xyzy'); + }) + .toThrowError( + 'MuJoCo Error: mju_euler2Quat: seq must contain exactly 3 characters'); + expect(() => { + mujoco.mju_euler2Quat(quat, euler, 'xYp'); + }) + .toThrowError( + `MuJoCo Error: mju_euler2Quat: seq[2] is 'p', should be one of x, y, z, X, Y, Z`); + } finally { + quat.delete(); + } + }); + + // Corresponds to bindings_test.py:test_texture_size + it('should load a texture from a model', () => { + const texFilename = 'tex.png'; + writeXMLFile(texFilename, 'tex'); + const tempXmlFilename = '/tmp/with_texture.xml'; + const TEST_XML_TEXTURE = ` + + + + + + + + + + `; + writeXMLFile(tempXmlFilename, TEST_XML_TEXTURE); + + const model = mujoco.MjModel!.loadFromXML(tempXmlFilename); + try { + expect(model).toBeDefined(); + expect(model!.tex_height).toEqual(new Int32Array([512])); + expect(model!.tex_width).toEqual(new Int32Array([512])); + } finally { + model?.delete(); + unlinkXMLFile(texFilename); + unlinkXMLFile(tempXmlFilename); + } + }); + + it('should create distinct MjModel instances and copy correctly', () => { + const model1 = model!; + const model2 = new mujoco.MjModel(model1); + + try { + assertExists(model1); + assertExists(model2); + expect(model1).not.toBe(model2); + expect(model1.opt.timestep).toEqual(model2.opt.timestep); + expect(model1.stat.meanmass).toEqual(model2.stat.meanmass); + model1.opt.timestep = 0.123; + expect(model1.opt.timestep).toEqual(0.123); + expect(model2.opt.timestep).not.toEqual(model1.opt.timestep); + expect(model2.opt.timestep).toEqual(0.002); + } finally { + model2.delete(); + } + }); + + it('should create distinct MjData instances and copy correctly', () => { + const data1 = new mujoco.MjData(model!); + const data2 = new mujoco.MjData(model!, data1); + try { + assertExists(data1); + assertExists(data2); + expect(data1).not.toBe(data2); + expectArraysEqual(data1.qpos, data2.qpos); + expectArraysEqual(data1.qvel, data2.qvel); + data1.qpos[0] = 1.0; + expect(data1.qpos[0]).toEqual(1.0); + expect(data2.qpos[0]).not.toEqual(data1.qpos[0]); + } finally { + data1.delete(); + data2.delete(); + } + }); + + // Corresponds to specs_test.py:test_address + it('should create distinct MjSpec instances and copy correctly', () => { + const spec1 = mujoco.parseXMLString(TEST_XML); + const spec2 = new mujoco.MjSpec(spec1); + + try { + assertExists(spec1); + assertExists(spec2); + + expect(spec1).not.toBe(spec2); + + expect(spec1.modelname).toEqual(spec2.modelname); + expect(spec1.option.timestep).toEqual(spec2.option.timestep); + expect(spec1.visual.quality.shadowsize) + .toEqual(spec2.visual.quality.shadowsize); + expect(spec1.stat.meanmass).toEqual(spec2.stat.meanmass); + + spec1.modelname = 'modified'; + expect(spec2.modelname).not.toEqual(spec1.modelname); + expect(spec2.modelname).toEqual('test'); + } finally { + spec1?.delete(); + spec2?.delete(); + } + }); + + // Corresponds to partial of user_api_test.cc:TEST_F(PluginTest, AttachPlugin) + it('should correctly copy MjSpec instances when attaching plugins', () => { + const xmlPlugin1 = ` + + + + + `; + + const spec1 = mujoco.parseXMLString(xmlPlugin1); + const spec2 = new mujoco.MjSpec(spec1); + const spec3 = new mujoco.MjSpec(spec1); + + try { + assertExists(spec1); + assertExists(spec2); + assertExists(spec3); + + spec2.modelname = 'first_copy'; + spec3.modelname = 'second_copy'; + expect(spec1.modelname).toEqual('MuJoCo Model'); + expect(spec2.modelname).toEqual('first_copy'); + expect(spec3.modelname).toEqual('second_copy'); + } finally { + spec1?.delete(); + spec2?.delete(); + spec3?.delete(); + } + }); + + it('should save the model to an XML file', () => { + const tempXmlFilename = '/tmp/saved_model.xml'; + const xml = ` + + + + + + + + +`; + writeXMLFile(tempXmlFilename, xml); + + const model = mujoco.MjModel!.loadFromXML(tempXmlFilename); + try { + mujoco.mj_saveLastXML(tempXmlFilename, model!); + const savedXmlContent = + (mujoco as any).FS.readFile(tempXmlFilename, {encoding: 'utf8'}); + // Remove whitespaces from the saved XML content to avoid flakiness. + expect(savedXmlContent.replace(/\s/g, '')) + .toEqual(xml.replace(/\s/g, '')); + } finally { + unlinkXMLFile(tempXmlFilename); + } + }); + + it('can call mj_setLengthRange with actuators', () => { + const tempXmlFilename = '/tmp/actuator_model.xml'; + const actuatorXml = ` + + + + + + + + + + `; + writeXMLFile(tempXmlFilename, actuatorXml); + const model = mujoco.MjModel.loadFromXML(tempXmlFilename); + assertExists(model); + const data = new mujoco.MjData(model); + assertExists(data); + const opt = new mujoco.MjLROpt(); + + try { + const result = mujoco.mj_setLengthRange( + model, + data, + /* index= */ 0, + opt, + ); + expect(result).toBe(1); + } finally { + opt.delete(); + model.delete(); + data.delete(); + unlinkXMLFile(tempXmlFilename); + } + }); +}); diff --git a/wasm/tests/enums_test.ts b/wasm/tests/enums_test.ts new file mode 100644 index 00000000..d5294d3c --- /dev/null +++ b/wasm/tests/enums_test.ts @@ -0,0 +1,274 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'jasmine'; + +import { MainModule } from "../dist/mujoco_wasm" +import loadMujoco from "../dist/mujoco_wasm.js" + +let mujoco: MainModule; + +describe('Enums', () => { + beforeAll(async () => { + mujoco = await loadMujoco(); + }); + + it('mjtDisableBit should exist', () => { + expect(mujoco.mjtDisableBit).toBeDefined(); + }); + + it('mjtEnableBit should exist', () => { + expect(mujoco.mjtEnableBit).toBeDefined(); + }); + + it('mjtJoint should exist', () => { + expect(mujoco.mjtJoint).toBeDefined(); + }); + + it('mjtGeom should exist', () => { + expect(mujoco.mjtGeom).toBeDefined(); + }); + + it('mjtCamLight should exist', () => { + expect(mujoco.mjtCamLight).toBeDefined(); + }); + + it('mjtLightType should exist', () => { + expect(mujoco.mjtLightType).toBeDefined(); + }); + + it('mjtTexture should exist', () => { + expect(mujoco.mjtTexture).toBeDefined(); + }); + + it('mjtTextureRole should exist', () => { + expect(mujoco.mjtTextureRole).toBeDefined(); + }); + + it('mjtColorSpace should exist', () => { + expect(mujoco.mjtColorSpace).toBeDefined(); + }); + + it('mjtIntegrator should exist', () => { + expect(mujoco.mjtIntegrator).toBeDefined(); + }); + + it('mjtCone should exist', () => { + expect(mujoco.mjtCone).toBeDefined(); + }); + + it('mjtJacobian should exist', () => { + expect(mujoco.mjtJacobian).toBeDefined(); + }); + + it('mjtSolver should exist', () => { + expect(mujoco.mjtSolver).toBeDefined(); + }); + + it('mjtEq should exist', () => { + expect(mujoco.mjtEq).toBeDefined(); + }); + + it('mjtWrap should exist', () => { + expect(mujoco.mjtWrap).toBeDefined(); + }); + + it('mjtTrn should exist', () => { + expect(mujoco.mjtTrn).toBeDefined(); + }); + + it('mjtDyn should exist', () => { + expect(mujoco.mjtDyn).toBeDefined(); + }); + + it('mjtGain should exist', () => { + expect(mujoco.mjtGain).toBeDefined(); + }); + + it('mjtBias should exist', () => { + expect(mujoco.mjtBias).toBeDefined(); + }); + + it('mjtObj should exist', () => { + expect(mujoco.mjtObj).toBeDefined(); + }); + + it('mjtSensor should exist', () => { + expect(mujoco.mjtSensor).toBeDefined(); + }); + + it('mjtStage should exist', () => { + expect(mujoco.mjtStage).toBeDefined(); + }); + + it('mjtDataType should exist', () => { + expect(mujoco.mjtDataType).toBeDefined(); + }); + + it('mjtConDataField should exist', () => { + expect(mujoco.mjtConDataField).toBeDefined(); + }); + + it('mjtSameFrame should exist', () => { + expect(mujoco.mjtSameFrame).toBeDefined(); + }); + + it('mjtLRMode should exist', () => { + expect(mujoco.mjtLRMode).toBeDefined(); + }); + + it('mjtFlexSelf should exist', () => { + expect(mujoco.mjtFlexSelf).toBeDefined(); + }); + + it('mjtSDFType should exist', () => { + expect(mujoco.mjtSDFType).toBeDefined(); + }); + + it('mjtTaskStatus should exist', () => { + expect(mujoco.mjtTaskStatus).toBeDefined(); + }); + + it('mjtState should exist', () => { + expect(mujoco.mjtState).toBeDefined(); + }); + + it('mjtConstraint should exist', () => { + expect(mujoco.mjtConstraint).toBeDefined(); + }); + + it('mjtConstraintState should exist', () => { + expect(mujoco.mjtConstraintState).toBeDefined(); + }); + + it('mjtWarning should exist', () => { + expect(mujoco.mjtWarning).toBeDefined(); + }); + + it('mjtTimer should exist', () => { + expect(mujoco.mjtTimer).toBeDefined(); + }); + + it('mjtCatBit should exist', () => { + expect(mujoco.mjtCatBit).toBeDefined(); + }); + + it('mjtMouse should exist', () => { + expect(mujoco.mjtMouse).toBeDefined(); + }); + + it('mjtPertBit should exist', () => { + expect(mujoco.mjtPertBit).toBeDefined(); + }); + + it('mjtCamera should exist', () => { + expect(mujoco.mjtCamera).toBeDefined(); + }); + + it('mjtLabel should exist', () => { + expect(mujoco.mjtLabel).toBeDefined(); + }); + + it('mjtFrame should exist', () => { + expect(mujoco.mjtFrame).toBeDefined(); + }); + + it('mjtVisFlag should exist', () => { + expect(mujoco.mjtVisFlag).toBeDefined(); + }); + + it('mjtRndFlag should exist', () => { + expect(mujoco.mjtRndFlag).toBeDefined(); + }); + + it('mjtStereo should exist', () => { + expect(mujoco.mjtStereo).toBeDefined(); + }); + + it('mjtPluginCapabilityBit should exist', () => { + expect(mujoco.mjtPluginCapabilityBit).toBeDefined(); + }); + + it('mjtGridPos should exist', () => { + expect(mujoco.mjtGridPos).toBeDefined(); + }); + + it('mjtFramebuffer should exist', () => { + expect(mujoco.mjtFramebuffer).toBeDefined(); + }); + + it('mjtDepthMap should exist', () => { + expect(mujoco.mjtDepthMap).toBeDefined(); + }); + + it('mjtFontScale should exist', () => { + expect(mujoco.mjtFontScale).toBeDefined(); + }); + + it('mjtFont should exist', () => { + expect(mujoco.mjtFont).toBeDefined(); + }); + + it('mjtGeomInertia should exist', () => { + expect(mujoco.mjtGeomInertia).toBeDefined(); + }); + + it('mjtMeshInertia should exist', () => { + expect(mujoco.mjtMeshInertia).toBeDefined(); + }); + + it('mjtMeshBuiltin should exist', () => { + expect(mujoco.mjtMeshBuiltin).toBeDefined(); + }); + + it('mjtBuiltin should exist', () => { + expect(mujoco.mjtBuiltin).toBeDefined(); + }); + + it('mjtMark should exist', () => { + expect(mujoco.mjtMark).toBeDefined(); + }); + + it('mjtLimited should exist', () => { + expect(mujoco.mjtLimited).toBeDefined(); + }); + + it('mjtAlignFree should exist', () => { + expect(mujoco.mjtAlignFree).toBeDefined(); + }); + + it('mjtInertiaFromGeom should exist', () => { + expect(mujoco.mjtInertiaFromGeom).toBeDefined(); + }); + + it('mjtOrientation should exist', () => { + expect(mujoco.mjtOrientation).toBeDefined(); + }); + + it('mjtButton should exist', () => { + expect(mujoco.mjtButton).toBeDefined(); + }); + + it('mjtEvent should exist', () => { + expect(mujoco.mjtEvent).toBeDefined(); + }); + + it('mjtItem should exist', () => { + expect(mujoco.mjtItem).toBeDefined(); + }); + + it('mjtSection should exist', () => { + expect(mujoco.mjtSection).toBeDefined(); + }); +}); diff --git a/wasm/tests/karma.conf.json b/wasm/tests/karma.conf.json new file mode 100644 index 00000000..23c12962 --- /dev/null +++ b/wasm/tests/karma.conf.json @@ -0,0 +1,10 @@ +{ + "extension": { + "karma": { + "client": { + "pingTimeout": 300000 + }, + "browserDisconnectTimeout": 300000 + } + } +} diff --git a/wasm/tests/run-tests.mjs b/wasm/tests/run-tests.mjs new file mode 100644 index 00000000..948737e4 --- /dev/null +++ b/wasm/tests/run-tests.mjs @@ -0,0 +1,40 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Jasmine from 'jasmine'; + +const jasmine = new Jasmine(); + +jasmine.loadConfig({ + spec_dir: 'tests', + spec_files: ['**/*_test.ts'], + jsLoader: 'import', + random: false, + stopSpecOnExpectationFailure: false, +}); + +try { + console.log('Starting Jasmine test run...'); + const result = await jasmine.execute(); + console.log(`Jasmine test run finished. Status: ${result.overallStatus}`); + + if (result.overallStatus === 'failed') { + process.exit(1); + } + +} catch (error) { + console.error('Test runner script failed:', error); + process.exit(1); +} + diff --git a/wasm/tests/sandbox/index.html b/wasm/tests/sandbox/index.html new file mode 100644 index 00000000..f656be2e --- /dev/null +++ b/wasm/tests/sandbox/index.html @@ -0,0 +1,26 @@ + + + + + + + MuJoCo WebAssembly + + + + + diff --git a/wasm/tests/sandbox/main.ts b/wasm/tests/sandbox/main.ts new file mode 100644 index 00000000..9ec14845 --- /dev/null +++ b/wasm/tests/sandbox/main.ts @@ -0,0 +1,64 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { MainModule, MjData, MjModel } from "../../dist/mujoco_wasm" +import loadMujoco from "../../dist/mujoco_wasm.js" + +declare function loadMujoco(): Promise; + +async function main() { + const mujoco: MainModule = await loadMujoco(); + + (mujoco as any).FS.mkdir('/working'); + (mujoco as any).FS.mount((mujoco as any).MEMFS, {root: '.'}, '/working'); + + const xmlContent = ` + + `; + + (mujoco as any).FS.writeFile('/working/hello.xml', xmlContent); + let model: MjModel|undefined; + let data: MjData|undefined; + + try { + console.log('Hello world!: Loading model'); + model = mujoco.MjModel.loadFromXML('/working/hello.xml'); + if (!model) { + throw new Error('Failed to load model'); + } + data = new mujoco.MjData(model); + if (!data) { + throw new Error('Failed to load data'); + } + + // Add your test code here... + + } finally { + model?.delete(); + data?.delete(); + (mujoco as any).FS.unmount('/working'); + } +} + +main() diff --git a/wasm/tests/sandbox/vite.sandbox.config.ts b/wasm/tests/sandbox/vite.sandbox.config.ts new file mode 100644 index 00000000..123f412b --- /dev/null +++ b/wasm/tests/sandbox/vite.sandbox.config.ts @@ -0,0 +1,27 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { defineConfig } from "vite" + +export default defineConfig({ + root: 'tests/sandbox', + base: './', + build: { + outDir: '../../sandbox-dist', + emptyOutDir: true, + }, + server: { + open: true, + }, +}) diff --git a/wasm/tsconfig.json b/wasm/tsconfig.json new file mode 100644 index 00000000..b675f402 --- /dev/null +++ b/wasm/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "ESNext", + "target": "ES2020", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "types": ["jasmine"] + }, + "include": ["tests/**/*"] +} diff --git a/wasm/unpack.cc b/wasm/unpack.cc new file mode 100644 index 00000000..63dfa7a1 --- /dev/null +++ b/wasm/unpack.cc @@ -0,0 +1,35 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "unpack.h" + +#include +#include +#include + +namespace mujoco::wasm { + +std::string StripWrapperSuffix(const char* func) { + const char* suffix = "_wrapper"; + size_t name_len = strlen(func); + size_t suffix_len = strlen(suffix); + + if (name_len >= suffix_len && + strcmp(func + name_len - suffix_len, suffix) == 0) { + return std::string(func, name_len - suffix_len); + } + return std::string(func); +} + +} // namespace mujoco::wasm diff --git a/wasm/unpack.h b/wasm/unpack.h new file mode 100644 index 00000000..a40fec31 --- /dev/null +++ b/wasm/unpack.h @@ -0,0 +1,297 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MUJOCO_WASM_UNPACK_H_ +#define MUJOCO_WASM_UNPACK_H_ + +#ifdef __EMSCRIPTEN__ + +#include + +#include // NOLINT required for PRId64 +#include +#include +#include +#include +#include +#include +#include + +#include "engine/engine_util_errmem.h" + +namespace mujoco::wasm { + +// Helper to strip "_wrapper" from function names. +std::string StripWrapperSuffix(const char* func); + +// Utility class to write/read from the Heap shared by C++ and Javascript +template +class WasmBuffer { + private: + // Note: Embind does not support binding more than one constructor with the + // same argument count so we bind the factory function fromArray instead + explicit WasmBuffer(const emscripten::val& array) { + std::vector cpp_array = convertJSArrayToNumberVector(array); + bytes_.resize(cpp_array.size() * sizeof(T)); + if (cpp_array.size() > 0) { + memcpy(bytes_.data(), cpp_array.data(), bytes_.size()); + } + } + + public: + // Creates a buffer with the given element count + explicit WasmBuffer(int element_count = 0) { + bytes_.resize(element_count * sizeof(T)); + } + + // Creates a buffer by copying data from a (typed) array + static WasmBuffer FromArray(const emscripten::val& array) { + return WasmBuffer(array); + } + + // Returns the pointer to the data in the buffer + uintptr_t GetPointer() { return reinterpret_cast(bytes_.data()); } + + // Returns the number of elements in the buffer + int GetElementCount() { return bytes_.size() / sizeof(T); } + + // Returns a TypedArray view of the buffer + emscripten::val GetView() { + return emscripten::val(emscripten::typed_memory_view( + bytes_.size() / sizeof(T), reinterpret_cast(bytes_.data()))); + } + + void Zero() { + if (!bytes_.empty()) { + memset(bytes_.data(), 0, bytes_.size()); + } + } + + private: + std::vector bytes_; +}; + +template +class UnpackedParam { + // The C++ representation of the parameter data + std::variant, std::span> data_; + + // Printable representations of the param and function name used for errors + const char* repr_; + const char* func_; + + explicit UnpackedParam(const char* repr, const char* func) + : data_(std::monostate{}), repr_(repr), func_(func) {} + + UnpackedParam(std::vector&& array, const char* repr, const char* func) + : data_(std::move(array)), repr_(repr), func_(func) {} + + UnpackedParam(T* data, std::size_t count, const char* repr, const char* func) + : data_(std::span(data, count)), repr_(repr), func_(func) {} + + // Returns true and raises an error if the val is null or undefined. + static bool ErrorOnNullOrUndefined(const emscripten::val& p, + const char* func, + const char* expected_type) { + if (p.isUndefined()) { + mju_error("[%s] Invalid argument. Expected a %s, got undefined.", + StripWrapperSuffix(func).c_str(), expected_type); + return true; + } else if (p.isNull()) { + mju_error("[%s] Invalid argument. Expected a %s, got null.", + StripWrapperSuffix(func).c_str(), expected_type); + return true; + } + return false; + } + + // Returns true if the val is null or undefined. Use when these are expected. + static bool IsNullOrUndefined(const emscripten::val& p) { + return p.isUndefined() || p.isNull(); + } + + public: + // Create from a nullable Javascript val. Call via UNPACK_NULLABLE_VALUE. + static UnpackedParam FromNullableValue(const emscripten::val& p, + const char* repr, + const char* func) { + if (IsNullOrUndefined(p)) { + return UnpackedParam(repr, func); + } + return FromValue(p, repr, func); + } + + // Create from a nullable Javascript number[]. Call via UNPACK_NULLABLE_ARRAY. + static UnpackedParam FromNullableArray(const emscripten::val& p, + const char* repr, + const char* func) { + if (IsNullOrUndefined(p)) { + return UnpackedParam(repr, func); + } + return UnpackedParam(convertJSArrayToNumberVector(p), repr, func); + } + + // Create from a Javascript number[]. Call via UNPACK_ARRAY. + static UnpackedParam FromArray(const emscripten::val& p, const char* repr, + const char* func) { + ErrorOnNullOrUndefined(p, func, "number[]"); + return UnpackedParam(convertJSArrayToNumberVector(p), repr, func); + } + + // Creates an UnpackedParam from a Javascript a TypedArray or a WasmBuffer. + // Call via UNPACK_VALUE. + static UnpackedParam FromValue(const emscripten::val& p, const char* repr, + const char* func) { + ErrorOnNullOrUndefined(p, func, "TypedArray or WasmBuffer"); + + if (!p["byteOffset"].isUndefined()) { // Javascript TypedArray + T* data = reinterpret_cast(p["byteOffset"].as()); + std::size_t count = p["length"].as(); + return UnpackedParam(data, count, repr, func); + } else if (!p["GetPointer"].isUndefined()) { // C++ WasmBuffer + WasmBuffer& buffer = p.as&>(); + T* data = reinterpret_cast(buffer.GetPointer()); + std::size_t count = buffer.GetElementCount(); + return UnpackedParam(data, count, repr, func); + } + + // TODO(manevi): This error message is not 100% accurate, WasmBuffer class + // isn't surfaced to JS developers + auto param = UnpackedParam(repr, func); + mju_error( + "[%s] Invalid argument. Expected TypedArray or WasmBuffer, got " + "unknown type for %s.", + param.func().c_str(), param.repr()); + return param; + } + + // Returns true if the parameter is not null. Used in if conditions. + explicit operator bool() const { + return !std::holds_alternative(data_); + } + + // Returns the printable representation of the parameter for use in error + // messages. + const char* repr() const { return repr_; } + + // Returns the name of the function the parameter is used in. + std::string func() const { return StripWrapperSuffix(func_); } + + // Returns the size of the parameter. Returns 0 if the parameter is null. + std::size_t size() const { + if (std::holds_alternative>(data_)) { + return std::get>(data_).size(); + } else if (std::holds_alternative>(data_)) { + return std::get>(data_).size(); + } + mju_error("[%s] [%s] UnpackedParam is null", func().c_str(), repr()); + return 0; + } + + // Returns a pointer to the data of the parameter. Returns nullptr if the + // parameter is null. + const T* data() const { + if (std::holds_alternative>(data_)) { + return std::get>(data_).data(); + } else if (std::holds_alternative>(data_)) { + return std::get>(data_).data(); + } + return nullptr; + } + + // Returns a non-const pointer to the data of the parameter. Returns nullptr + // if the parameter is null. + T* data() { + if (std::holds_alternative>(data_)) { + return std::get>(data_).data(); + } else if (std::holds_alternative>(data_)) { + return const_cast(std::get>(data_).data()); + } + return nullptr; + } +}; + +// TODO(matijak): When the bindings are fully auto-generated we could replace +// these macros with a function calls something like this: +// +// template +// UnpackedParam Unpack(U&& u, const char* u_name, +// const std::source_location location = std::source_location::current()) { +// return UnpackedParam::FromValue(std::forward(u), u_name, +// location.file_name(), location.line(), location.function_name()); +// } + +#define UNPACK_VALUE(T, p) \ + UnpackedParam p##_ = UnpackedParam::FromValue(p, #p, __func__) + +#define UNPACK_ARRAY(T, p) \ + UnpackedParam p##_ = UnpackedParam::FromArray(p, #p, __func__) + +#define UNPACK_NULLABLE_VALUE(T, p) \ + UnpackedParam p##_ = UnpackedParam::FromNullableValue(p, #p, __func__) + +#define UNPACK_NULLABLE_ARRAY(T, p) \ + UnpackedParam p##_ = UnpackedParam::FromNullableArray(p, #p, __func__) + +// Raises an error if x##_.size() is not equal to expr. +// Assumes UnpackedParam x##_ is defined. +#define CHECK_SIZE(x, expr) \ + if (x##_) { \ + if (static_cast(x##_.size()) != static_cast(expr)) { \ + mju_error("[%s] %s must have size %" PRId64 ", got %" PRId64, \ + x##_.func().c_str(), x##_.repr(), static_cast(expr), \ + static_cast(x##_.size())); \ + } \ + } + +// Raises an error if x##_.size() is not equal to y##_.size(). +// Assumes UnpackedParams x##_ and y##_ are defined. +#define CHECK_SIZES(x, y) \ + if (x##_ && y##_) { \ + if (static_cast(x##_.size()) != \ + static_cast(y##_.size())) { \ + mju_error("[%s] %s and %s must have equal size, got %" PRId64 \ + " and %" PRId64, \ + x##_.func().c_str(), x##_.repr(), y##_.repr(), \ + static_cast(x##_.size()), \ + static_cast(y##_.size())); \ + } \ + } + +// Raises an error if x##_.size() is not a perfect square. +// Assumes UnpackedParam x##_ is defined. Defines x##_sqrt as an int. +#define CHECK_PERFECT_SQUARE(x) \ + const int x##_sqrt = static_cast(round(sqrt(x##_.size()))); \ + if (x##_sqrt * x##_sqrt != x##_.size()) { \ + mjERROR("[%s] %s must be a perfect square, got %" PRId64, \ + x##_.func().c_str(), x##_.repr(), \ + static_cast(x##_.size())); \ + } + +// Raises an error if x##_.size() is not divisible by divisor. +// Assumes UnpackedParam x##_ is defined. Defines x##_div as an std::div_t. +#define CHECK_DIVISIBLE(x, divisor) \ + const std::div_t x##_div = \ + std::div(static_cast(x##_.size()), static_cast(divisor)); \ + if (x##_div.rem != 0) { \ + mju_error("[%s] %s must be divisible by %d, got quot=%d rem=%d", \ + x##_.func().c_str(), x##_.repr(), static_cast(divisor), \ + x##_div.quot, x##_div.rem); \ + } + +} // namespace mujoco::wasm + +#endif // __EMSCRIPTEN__ + +#endif // MUJOCO_WASM_UNPACK_H_