simview 3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- simview-3.2/LICENSE +28 -0
- simview-3.2/MANIFEST.in +5 -0
- simview-3.2/PKG-INFO +552 -0
- simview-3.2/README.md +522 -0
- simview-3.2/pyproject.toml +89 -0
- simview-3.2/setup.cfg +4 -0
- simview-3.2/simview/__init__.py +69 -0
- simview-3.2/simview/__main__.py +174 -0
- simview-3.2/simview/launcher.py +109 -0
- simview-3.2/simview/live.py +204 -0
- simview-3.2/simview/merge.py +593 -0
- simview-3.2/simview/model.py +690 -0
- simview-3.2/simview/py.typed +0 -0
- simview-3.2/simview/scene.py +603 -0
- simview-3.2/simview/server.py +739 -0
- simview-3.2/simview/state.py +190 -0
- simview-3.2/simview/static/css/controls.css +36 -0
- simview-3.2/simview/static/js/SimView.js +601 -0
- simview-3.2/simview/static/js/components/AnimationController.js +654 -0
- simview-3.2/simview/static/js/components/BatchManager.js +198 -0
- simview-3.2/simview/static/js/components/InteractionController.js +446 -0
- simview-3.2/simview/static/js/components/InteractionControls.js +116 -0
- simview-3.2/simview/static/js/components/Scene.js +166 -0
- simview-3.2/simview/static/js/components/StateStore.js +188 -0
- simview-3.2/simview/static/js/config.js +233 -0
- simview-3.2/simview/static/js/main.js +3 -0
- simview-3.2/simview/static/js/objects/Body.js +616 -0
- simview-3.2/simview/static/js/objects/StaticObject.js +181 -0
- simview-3.2/simview/static/js/objects/Terrain.js +514 -0
- simview-3.2/simview/static/js/objects/utils.js +378 -0
- simview-3.2/simview/static/js/ui/AnalysisPanel.js +233 -0
- simview-3.2/simview/static/js/ui/BatchLegend.js +207 -0
- simview-3.2/simview/static/js/ui/BodyStateWindow.js +521 -0
- simview-3.2/simview/static/js/ui/Controls.js +586 -0
- simview-3.2/simview/static/js/ui/ErrorMetrics.js +664 -0
- simview-3.2/simview/static/js/ui/Legend.js +87 -0
- simview-3.2/simview/static/js/ui/PlaybackControls.js +314 -0
- simview-3.2/simview/static/js/ui/ScalarPlotter.js +575 -0
- simview-3.2/simview/static/js/utils/blobCodec.js +73 -0
- simview-3.2/simview/static/js/utils/bodyTransforms.js +151 -0
- simview-3.2/simview/static/js/utils/csv.js +46 -0
- simview-3.2/simview/static/js/utils/errorMath.js +69 -0
- simview-3.2/simview/static/js/utils/injectStyles.js +16 -0
- simview-3.2/simview/static/js/utils/interpolate.js +65 -0
- simview-3.2/simview/static/js/utils/liveFollow.js +17 -0
- simview-3.2/simview/static/js/utils/loadRecordingLibs.js +34 -0
- simview-3.2/simview/static/js/utils/viewState.js +226 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/index.min.js +8 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/Color.js +49 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/chroma.js +10 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/colors/colorbrewer.js +81 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/colors/w3cx11.js +164 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/generator/average.js +88 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/generator/bezier.js +86 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/generator/blend.js +57 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/generator/cubehelix.js +87 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/generator/mix.js +19 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/generator/random.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/generator/scale.js +394 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/_hsx.js +59 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/hcg.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/hsi.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/hsl.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/hsv.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/index.js +1 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/lab.js +19 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/lch.js +13 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/lrgb.js +19 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/num.js +15 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/oklab.js +19 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/oklch.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/interpolator/rgb.js +18 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/cmyk/cmyk2rgb.js +16 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/cmyk/index.js +27 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/cmyk/rgb2cmyk.js +17 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/css/css2rgb.js +238 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/css/hsl2css.js +26 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/css/index.js +27 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/css/lab2css.js +24 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/css/lch2css.js +24 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/css/oklab2css.js +16 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/css/oklch2css.js +16 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/css/rgb2css.js +61 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/gl/index.js +22 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hcg/hcg2rgb.js +55 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hcg/index.js +27 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hcg/rgb2hcg.js +23 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hex/hex2rgb.js +56 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hex/index.js +29 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hex/rgb2hex.js +29 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hsi/hsi2rgb.js +45 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hsi/index.js +27 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hsi/rgb2hsi.js +31 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hsl/hsl2rgb.js +35 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hsl/index.js +27 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hsl/rgb2hsl.js +45 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hsv/hsv2rgb.js +47 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hsv/index.js +27 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/hsv/rgb2hsv.js +32 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/input.js +4 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lab/index.js +28 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lab/lab-constants.js +120 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lab/lab2rgb.js +101 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lab/rgb2lab.js +67 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lch/hcl2rgb.js +9 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lch/index.js +35 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lch/lab2lch.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lch/lch2lab.js +18 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lch/lch2rgb.js +13 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/lch/rgb2lch.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/named/index.js +30 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/num/index.js +32 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/num/num2rgb.js +13 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/num/rgb2num.js +8 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/oklab/index.js +27 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/oklab/oklab2rgb.js +34 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/oklab/rgb2oklab.js +37 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/oklch/index.js +27 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/oklch/oklch2rgb.js +13 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/oklch/rgb2oklch.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/rgb/index.js +44 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/temp/index.js +22 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/temp/rgb2temperature.js +30 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/io/temp/temperature2rgb.js +39 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/alpha.js +13 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/clipped.js +5 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/darken.js +17 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/get.js +13 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/luminance.js +54 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/mix.js +10 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/premultiply.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/saturate.js +15 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/set.js +43 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/ops/shade.js +11 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/analyze.js +191 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/clip_rgb.js +15 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/contrast.js +12 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/contrastAPCA.js +68 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/delta-e.js +62 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/distance.js +17 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/index.js +27 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/last.js +8 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/limit.js +5 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/multiply-matrices.js +36 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/scales.js +15 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/type.js +18 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/unpack.js +17 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/utils/valid.js +11 -0
- simview-3.2/simview/static/lib/chroma-js-3.1.2/src/version.js +2 -0
- simview-3.2/simview/static/lib/download.js +132 -0
- simview-3.2/simview/static/lib/js-colormaps.js +19335 -0
- simview-3.2/simview/static/lib/tar.js +334 -0
- simview-3.2/simview/static/lib/three-0.174.0/addons/controls/OrbitControls.js +1556 -0
- simview-3.2/simview/static/lib/three-0.174.0/addons/libs/lil-gui.module.min.js +8 -0
- simview-3.2/simview/static/lib/three-0.174.0/three.core.js +48830 -0
- simview-3.2/simview/static/lib/three-0.174.0/three.module.js +17313 -0
- simview-3.2/simview/static/lib/uPlot.esm.js +6140 -0
- simview-3.2/simview/static/lib/uPlot.min.css +2 -0
- simview-3.2/simview/static/textures/contacts/red-cross0.png +0 -0
- simview-3.2/simview/static/textures/points/ball0.png +0 -0
- simview-3.2/simview/static/textures/points/ball1.png +0 -0
- simview-3.2/simview/templates/index.html +81 -0
- simview-3.2/simview/utils.py +41 -0
- simview-3.2/simview.egg-info/PKG-INFO +552 -0
- simview-3.2/simview.egg-info/SOURCES.txt +180 -0
- simview-3.2/simview.egg-info/dependency_links.txt +1 -0
- simview-3.2/simview.egg-info/entry_points.txt +2 -0
- simview-3.2/simview.egg-info/requires.txt +13 -0
- simview-3.2/simview.egg-info/top_level.txt +1 -0
- simview-3.2/tests/test_cli.py +169 -0
- simview-3.2/tests/test_columnar_states.py +288 -0
- simview-3.2/tests/test_contacts.py +29 -0
- simview-3.2/tests/test_launcher.py +146 -0
- simview-3.2/tests/test_live.py +158 -0
- simview-3.2/tests/test_merge.py +535 -0
- simview-3.2/tests/test_roundtrip.py +412 -0
- simview-3.2/tests/test_scene.py +452 -0
- simview-3.2/tests/test_server.py +330 -0
- simview-3.2/tests/test_show.py +101 -0
- simview-3.2/tests/test_terrain.py +164 -0
- simview-3.2/tests/test_trajectory.py +168 -0
- simview-3.2/tests/test_utils.py +74 -0
simview-3.2/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Vision for Robotics and Autonomous Systems
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
simview-3.2/MANIFEST.in
ADDED
simview-3.2/PKG-INFO
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simview
|
|
3
|
+
Version: 3.2
|
|
4
|
+
Summary: THREE.js-based simulation visualization tool
|
|
5
|
+
Author-email: Ales Kucera <kuceral4@fel.cvut.cz>, David Korcak <david.korcak@gmail.com>, Jan Vlk <vlkjan6@fel.cvut.cz>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/vlk-jan/simview
|
|
8
|
+
Project-URL: Repository, https://github.com/vlk-jan/simview
|
|
9
|
+
Project-URL: Issues, https://github.com/vlk-jan/simview/issues
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: fastapi>=0.135.3
|
|
20
|
+
Requires-Dist: httptools>=0.7.1; sys_platform != "win32"
|
|
21
|
+
Requires-Dist: jinja2>=3.1.6
|
|
22
|
+
Requires-Dist: orjson>=3.11.8
|
|
23
|
+
Requires-Dist: uvicorn>=0.44.0
|
|
24
|
+
Requires-Dist: uvloop>=0.22.1; sys_platform != "win32"
|
|
25
|
+
Provides-Extra: authoring
|
|
26
|
+
Requires-Dist: torch; extra == "authoring"
|
|
27
|
+
Requires-Dist: einops; extra == "authoring"
|
|
28
|
+
Requires-Dist: numpy; extra == "authoring"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# SimView Visualizer
|
|
32
|
+
|
|
33
|
+
**SimView** is a powerful and interactive tool for visualizing 3D models and terrain data in simulations. It enables you to explore and analyze multiple simulation scenarios (batches) within a shared environment, all defined through an intuitive JSON format or a Python API.
|
|
34
|
+
|
|
35
|
+
Whether you're simulating physical objects or comparing different runs, SimView provides a flexible and efficient way to bring your data to life using a web-based interface powered by Three.js.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **Batched Simulations**: Visualize multiple simulation instances side-by-side.
|
|
42
|
+
- **Shared Terrain**: Efficient rendering with shared terrain across all batches.
|
|
43
|
+
- **Interactive UI**: Web-based controls for playback, camera, and data inspection.
|
|
44
|
+
- **Python API**: Easy-to-use API for generating scenes and launching the visualizer directly from your code.
|
|
45
|
+
- **JSON Support**: Load and save simulation data using a portable JSON format.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
The easiest way to get started is to run the provided example script:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python example.py
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This script demonstrates how to use the Python API to create a simulation with wavy terrain, dynamic bodies, and time-series data.
|
|
58
|
+
|
|
59
|
+
The Python authoring API (`simview.scene`, `simview.state`, `simview.model`) depends on
|
|
60
|
+
`torch` and `einops`. Install them with the optional `authoring` extra shown below.
|
|
61
|
+
Only these are needed to *build* simulations; *viewing* an existing JSON file does not
|
|
62
|
+
require `torch`.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
Requires **Python 3.12+**.
|
|
69
|
+
|
|
70
|
+
To only view existing simulation JSON files:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install -e .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
To also author simulations from Python (installs `torch` and `einops`):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install -e ".[authoring]"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For independent use of this repository, use `venv` or `uv`:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
python -m venv .venv
|
|
86
|
+
source .venv/bin/activate
|
|
87
|
+
pip install -e ".[authoring]"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv sync --extra authoring
|
|
92
|
+
source .venv/bin/activate
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## CLI Utilities
|
|
98
|
+
|
|
99
|
+
### Cache Management
|
|
100
|
+
|
|
101
|
+
SimView caches some temporary files for visualization. It also cleans up any
|
|
102
|
+
`simview_viz_*.json` temp scene files left behind by older versions (a launched viewer
|
|
103
|
+
now serves an in-memory `SimulationScene` directly, without writing one). You can clear
|
|
104
|
+
all of this using the following command:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
simview clear
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Visualization of exported simulations
|
|
111
|
+
|
|
112
|
+
To visualize a simulation defined in a JSON file, run the following command, replacing `[path_to_json_file]` with the actual path to your JSON data:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
simview [path_to_json_file]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Gzip-compressed files (e.g. `scene.json.gz`) are detected automatically and decompressed
|
|
119
|
+
transparently — no separate flag needed.
|
|
120
|
+
|
|
121
|
+
Useful flags:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
simview scene.json --host 0.0.0.0 --port 8080 # bind to a specific host/port
|
|
125
|
+
simview scene.json --no-browser # don't auto-open a browser tab
|
|
126
|
+
simview --version # print the installed version
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Comparing multiple runs (e.g. real-world vs. simulated)
|
|
130
|
+
|
|
131
|
+
Pass multiple JSON files to merge them into a single scene, each file's batches appended
|
|
132
|
+
as extra batches in the viewer:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
simview real_world.json simulated.json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The files must describe the same physical setup (identical bodies and terrain grid) —
|
|
139
|
+
that's what makes the batches comparable. They don't need to share a timeline: the
|
|
140
|
+
**first** file's timestamps become the merged timeline, and every other file is
|
|
141
|
+
resampled onto it by nearest timestamp (no interpolation), so put the recording you
|
|
142
|
+
care most about matching frame-for-frame first. See [Analysis Panel](#analysis-panel)
|
|
143
|
+
below for a way to quantify the difference between two merged batches.
|
|
144
|
+
|
|
145
|
+
Each merged file's batches are auto-named after its filename (e.g. `real_world`,
|
|
146
|
+
`simulated`), shown in the [Batch Legend](#batch-legend). You can rename them from
|
|
147
|
+
there — renames are saved next to the input file(s) and reloaded automatically the
|
|
148
|
+
next time you open the same file(s). You can also set initial batch names yourself by
|
|
149
|
+
including a `batchNames` array directly in the JSON's `model` object (see
|
|
150
|
+
[JSON Format Specification](#json-format-specification)); renames from the UI take
|
|
151
|
+
precedence over this once saved.
|
|
152
|
+
|
|
153
|
+
To merge files without launching the viewer, e.g. to inspect or re-share the merged
|
|
154
|
+
scene, pass `--save-merged`:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
simview real_world.json simulated.json --save-merged combined.json.gz
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The output is gzipped if the path ends in `.gz`.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Live streaming
|
|
165
|
+
|
|
166
|
+
Instead of saving a scene and viewing it afterwards, `LiveViewer` starts the server
|
|
167
|
+
immediately and pushes each state to an already-open browser tab as your simulation
|
|
168
|
+
produces it, over a WebSocket:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from simview import LiveViewer
|
|
172
|
+
|
|
173
|
+
# `scene` needs its complete model (terrain, bodies, ...) up front; states are
|
|
174
|
+
# streamed in afterwards.
|
|
175
|
+
with LiveViewer(scene, open_browser=True) as live:
|
|
176
|
+
for t in range(num_steps):
|
|
177
|
+
... # step the simulation
|
|
178
|
+
live.push_state(time=t * dt, body_states=[...], scalar_values=...)
|
|
179
|
+
|
|
180
|
+
# scene.states was appended to exactly like scene.add_state would, so it can
|
|
181
|
+
# still be saved once streaming is done:
|
|
182
|
+
scene.save("recording.json.gz", compress=True)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`push_state` has the same signature and validation as `SimulationScene.add_state` --
|
|
186
|
+
it delegates to it directly, then broadcasts the new frame to every connected viewer. A
|
|
187
|
+
viewer opened after the stream has already started still gets the full history so far,
|
|
188
|
+
replayed as a single catch-up message before it starts receiving new frames live. If no
|
|
189
|
+
viewer is connected yet, pushed frames are simply buffered for the next one to connect.
|
|
190
|
+
Playback in the browser follows the live frames automatically as long as you haven't
|
|
191
|
+
scrubbed backward or started a loop; a small "LIVE" badge shows while the socket is open.
|
|
192
|
+
|
|
193
|
+
See `example_live.py` for a runnable end-to-end example.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Jupyter / non-blocking viewing
|
|
198
|
+
|
|
199
|
+
`scene.show()` starts a viewer on a background thread and returns immediately,
|
|
200
|
+
instead of blocking like `SimViewLauncher`/`SimViewServer.run`. This is handy in a
|
|
201
|
+
notebook: evaluating the returned handle as a cell's result embeds the viewer inline
|
|
202
|
+
via an iframe.
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
handle = scene.show() # non-blocking; scene itself is left untouched
|
|
206
|
+
handle # in Jupyter, displays the viewer inline (uses _repr_html_)
|
|
207
|
+
|
|
208
|
+
# ... do other work, or just let the cell above stay interactive ...
|
|
209
|
+
|
|
210
|
+
handle.stop() # or: `with scene.show() as handle: ...` to stop automatically
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Visualization Controls
|
|
216
|
+
|
|
217
|
+
Once the visualizer is running, you can interact with the simulation using the following controls:
|
|
218
|
+
|
|
219
|
+
### Camera
|
|
220
|
+
|
|
221
|
+
- **Rotate**: Left-click + drag OR `Ctrl` (`CMD` on Mac) + Arrow keys
|
|
222
|
+
- **Pan**: Right-click + drag OR Arrow keys
|
|
223
|
+
- **Zoom**: Scroll wheel
|
|
224
|
+
- **Track Body**: Automatically follow a specific body (via the "Camera Options" menu)
|
|
225
|
+
- **Split Screen**: Compare two batches side-by-side (via the "Camera Options" menu, requires ≥2 batches)
|
|
226
|
+
- **Field of View**: Adjust camera FOV (via the "Camera Options" menu)
|
|
227
|
+
- **Copy View Link**: Click the "Copy view link" button (in the "Camera Options" menu) to copy a
|
|
228
|
+
URL that encodes the current camera, playback time, focused batch, and visualization toggles --
|
|
229
|
+
opening it restores that view.
|
|
230
|
+
|
|
231
|
+
### Timeline
|
|
232
|
+
|
|
233
|
+
- **Step Forward/Backward**: `Alt` + Arrow Right / Arrow Left
|
|
234
|
+
- **Seek (and Pause)**: Click on the timeline bar
|
|
235
|
+
- **Play/Pause**: `Space` or Click the Play button
|
|
236
|
+
- **Record**: `R` or Click the Record button (select WEBM, MP4 -- if your browser supports
|
|
237
|
+
recording it -- or PNG sequence via the dropdown). Recording seeks to the start, plays
|
|
238
|
+
exactly one loop, then automatically stops and downloads the file.
|
|
239
|
+
- **Screenshot**: `S` or Click the camera button next to Record to save the current frame as a PNG.
|
|
240
|
+
- **Playback Speed**: Adjust speed (0.1x to 5x) via the dropdown next to the timeline
|
|
241
|
+
|
|
242
|
+
### Batch Selection
|
|
243
|
+
|
|
244
|
+
- **Move Selection**: `Shift` + Arrow keys
|
|
245
|
+
|
|
246
|
+
### Visualization Options
|
|
247
|
+
|
|
248
|
+
- **`B`**: Toggle Body Visualization Mode (Mesh / Wireframe / Points)
|
|
249
|
+
- **`A`**: Toggle Axes Visibility
|
|
250
|
+
- **`G`**: Toggle Trajectory Trails
|
|
251
|
+
- **`I`**: Toggle Smooth Interpolation (on by default; interpolates position/orientation between recorded states during playback and scrubbing instead of snapping to the nearest frame)
|
|
252
|
+
- **`C`**: Toggle Contact Points
|
|
253
|
+
- **`V`**: Toggle Linear Velocity
|
|
254
|
+
- **`W`**: Toggle Angular Velocity
|
|
255
|
+
- **`F`**: Toggle Linear Force
|
|
256
|
+
- **`T`**: Toggle Torque
|
|
257
|
+
- **`P`**: Toggle Terrain Data Probe (interactive tooltip on hover)
|
|
258
|
+
|
|
259
|
+
You can also customize terrain colors, colormaps, and toggle surface/wireframe/normals from the "Terrain Options" menu.
|
|
260
|
+
|
|
261
|
+
### Trajectory Trails
|
|
262
|
+
|
|
263
|
+
Toggling trails (`G`, or "Show Trails" in the Body Options panel) draws each body's
|
|
264
|
+
path from the start of the simulation up to the current playback time, one line per
|
|
265
|
+
batch in that batch's color. Useful for comparing the overall shape of two
|
|
266
|
+
trajectories (e.g. real vs. simulated) at a glance instead of scrubbing frame by frame.
|
|
267
|
+
|
|
268
|
+
### Analysis Panel
|
|
269
|
+
|
|
270
|
+
Scalars and Error Metrics share one collapsible panel at the top-center of the screen.
|
|
271
|
+
When both are available, a mode switcher lets you flip between them; if only one is
|
|
272
|
+
available (e.g. a single-batch scene has no Error Metrics), that one is shown directly
|
|
273
|
+
without the switcher.
|
|
274
|
+
|
|
275
|
+
- **Scalars**: one tab per scalar defined in the model, each plotting its value over
|
|
276
|
+
time for every batch (colored per batch, click a line to focus that batch). An
|
|
277
|
+
"Export CSV" button on the active tab downloads its full series as `time` plus one
|
|
278
|
+
column per batch, named after each batch's current display name.
|
|
279
|
+
- **Error Metrics**: shown once a scene has 2 or more batches. Pick a body and two
|
|
280
|
+
batches ("Batch A" / "Batch B") to compare — e.g. the real and simulated batches
|
|
281
|
+
produced by [merging multiple files](#comparing-multiple-runs-eg-real-world-vs-simulated)
|
|
282
|
+
— and it computes, over the full timeline, the Euclidean position error and the
|
|
283
|
+
quaternion angle (orientation) error between the two batches for that body. A live
|
|
284
|
+
readout shows the current-frame values, and the chart plots both error curves over
|
|
285
|
+
time with a marker at the current playback position. The "Per-axis" toggle swaps the
|
|
286
|
+
combined position error curve for the signed X/Y/Z error components (Batch A minus
|
|
287
|
+
Batch B), useful for spotting a directional bias instead of just overall magnitude.
|
|
288
|
+
Below the readout, a compact stats block summarizes the full timeline: position
|
|
289
|
+
RMSE, the max position error (and when it occurs), the final-frame drift, and the
|
|
290
|
+
orientation RMSE and max angle error. An "Export CSV" button downloads the current
|
|
291
|
+
selection's per-frame series (`time`, `pos_error`, `err_x`, `err_y`, `err_z`,
|
|
292
|
+
`angle_error_deg`).
|
|
293
|
+
|
|
294
|
+
### Batch Legend
|
|
295
|
+
|
|
296
|
+
When a scene has 2 or more batches, a toggleable "Batches" legend appears in the
|
|
297
|
+
bottom-right corner, listing each batch's color, index, and name. Click a row to focus
|
|
298
|
+
that batch, or click a name to rename it in place — renames persist next to the input
|
|
299
|
+
file(s), so they survive a reload or server restart.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## JSON Format Specification
|
|
304
|
+
|
|
305
|
+
If you prefer to generate data files manually or from another language, SimView uses a
|
|
306
|
+
single JSON document with two top-level keys: `model` (static data, sent once) and
|
|
307
|
+
`states` (an array of time-ordered snapshots). This is exactly what `SimulationScene.save()`
|
|
308
|
+
produces.
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{ "model": { ... }, "states": [ { ... }, { ... } ] }
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Model (Static Data)
|
|
315
|
+
|
|
316
|
+
- **`simBatches`** *(integer)* — number of parallel simulation instances (batches).
|
|
317
|
+
- **`batchNames`** *(array[string], optional)* — display name for each batch, length
|
|
318
|
+
must equal `simBatches`. Shown in the [Batch Legend](#batch-legend); falls back to
|
|
319
|
+
`"Batch <index>"` per entry if omitted, empty, or the wrong length. Renames made from
|
|
320
|
+
the Batch Legend are persisted server-side (see below) and take precedence over this
|
|
321
|
+
field on subsequent loads.
|
|
322
|
+
- **`scalarNames`** *(array[string])* — names of per-batch scalar time-series (e.g. `"energy"`).
|
|
323
|
+
- **`dt`** *(float)* — simulation timestep in seconds. Used for playback timing; if omitted or invalid the viewer infers it from consecutive state times.
|
|
324
|
+
- **`collapse`** *(boolean)* — UI hint to start with the body-state window collapsed.
|
|
325
|
+
- **`bodies`** *(array)* — dynamic bodies. Each entry:
|
|
326
|
+
- **`name`** *(string)* — unique identifier, referenced from each state.
|
|
327
|
+
- **`shape`** *(object)* — geometry, keyed by a **string** `type`:
|
|
328
|
+
- `"box"` — requires `hx`, `hy`, `hz` (half-extents).
|
|
329
|
+
- `"sphere"` — requires `radius`.
|
|
330
|
+
- `"cylinder"` — requires `radius`, `height`.
|
|
331
|
+
- `"pointcloud"` — requires `points` *(array[array[3]])* in the body's local frame.
|
|
332
|
+
- `"mesh"` — requires `vertices` *(array[array[3]])* and `faces` *(array[array[3]])*.
|
|
333
|
+
- **`availableAttributes`** *(array[string], optional)* — which optional per-state
|
|
334
|
+
fields this body provides. Any of `"contacts"`, `"velocity"`, `"angularVelocity"`,
|
|
335
|
+
`"force"`, `"torque"`.
|
|
336
|
+
- **`parent`** *(string, optional)* — name of another `model.bodies[]` entry this
|
|
337
|
+
body is attached to. When set, this body's pose is no longer absolute world
|
|
338
|
+
space; see `localTransform` below and the `bodyTransform` note under
|
|
339
|
+
[States](#states-dynamic-data).
|
|
340
|
+
- **`localTransform`** *(array[7], optional)* — `[x, y, z, w, qx, qy, qz]` constant
|
|
341
|
+
offset from `parent`, for bodies **rigidly** attached (e.g. a wheel bolted to a
|
|
342
|
+
chassis). Set only together with `parent`. A body with `localTransform` never
|
|
343
|
+
appears in any state's `bodies[]` — its world pose is derived every frame from
|
|
344
|
+
its parent's current pose plus this fixed offset, saving the cost of repeating
|
|
345
|
+
an unchanging transform every frame. For an **articulated** attachment (e.g. an
|
|
346
|
+
arm joint) instead, set only `parent` and keep providing a per-frame
|
|
347
|
+
`bodyTransform` in `states[].bodies[]` as usual — it's then interpreted as local
|
|
348
|
+
to the parent's current-frame pose rather than world space.
|
|
349
|
+
- **`staticObjects`** *(array, optional)* — non-moving geometry. Each entry has `name`,
|
|
350
|
+
`isSingleton` *(boolean)*, and either `shape` (when singleton) or `shapes`
|
|
351
|
+
*(array, one per batch)* using the same shape objects as bodies.
|
|
352
|
+
- **`terrain`** *(object)* — heightfield shared or per-batch:
|
|
353
|
+
- **`dimensions`**: `sizeX`, `sizeY` *(float)* and `resolutionX`, `resolutionY` *(int)*.
|
|
354
|
+
- **`bounds`**: `minX`, `maxX`, `minY`, `maxY`, `minZ`, `maxZ`. When friction/stiffness
|
|
355
|
+
data is present, also `minFriction`/`maxFriction` and/or `minStiffness`/`maxStiffness`,
|
|
356
|
+
which the viewer uses to normalize the color map.
|
|
357
|
+
- **`isSingleton`** *(boolean)* — `true` when one terrain is shared by all batches;
|
|
358
|
+
`false` when each batch has its own.
|
|
359
|
+
- **`heightData`** *(array[array[float]])* — one flattened `resolutionX * resolutionY`
|
|
360
|
+
grid per batch (a single flat array is also accepted and treated as one batch).
|
|
361
|
+
- **`normals`** *(array[array[array[3]]])* — per-batch surface normals, one `[x, y, z]`
|
|
362
|
+
per grid point.
|
|
363
|
+
- **`frictionData`**, **`stiffnessData`** *(array[array[float]] | null, optional)* —
|
|
364
|
+
per-batch scalar fields over the grid, selectable as terrain color modes.
|
|
365
|
+
|
|
366
|
+
### States (Dynamic Data)
|
|
367
|
+
|
|
368
|
+
`states` is an array; each element is one snapshot:
|
|
369
|
+
|
|
370
|
+
- **`time`** *(float)* — snapshot time in seconds.
|
|
371
|
+
- **`bodies`** *(array)* — per body:
|
|
372
|
+
- **`name`** *(string | array[string])* — matches a `model.bodies[].name`. May instead be
|
|
373
|
+
a list of names when several bodies move rigidly together (e.g. links welded to the same
|
|
374
|
+
parent): the single entry's `bodyTransform` and other fields below then apply identically
|
|
375
|
+
to every named body, instead of repeating identical data once per body. All named bodies
|
|
376
|
+
must exist in `model.bodies`.
|
|
377
|
+
- **`bodyTransform`** — pose. Batched: `array[array[7]]`, one `[x, y, z, w, qx, qy, qz]`
|
|
378
|
+
per batch; single: a flat `[x, y, z, w, qx, qy, qz]`. Absolute world-space, unless
|
|
379
|
+
the referenced body has a `parent` in `model.bodies` (see above), in which case
|
|
380
|
+
this is local to that parent's current-frame pose instead. A body with a constant
|
|
381
|
+
`localTransform` on the model never has a `bodyTransform` entry here at all.
|
|
382
|
+
- **`contacts`** *(array[array[int]], optional)* — per batch, indices of contacting
|
|
383
|
+
points (into the body's pointcloud `points`). Empty array means no contacts.
|
|
384
|
+
- **`velocity`**, **`angularVelocity`**, **`force`**, **`torque`**
|
|
385
|
+
*(array[array[3]], optional)* — per-batch 3-vectors.
|
|
386
|
+
- **`<scalarName>`** *(array[float])* — for each name in `model.scalarNames`, one value per batch.
|
|
387
|
+
|
|
388
|
+
> **Binary state fields.** The numeric per-body fields (`bodyTransform`, `velocity`,
|
|
389
|
+
> `angularVelocity`, `force`, `torque`) may alternatively be a string of the form
|
|
390
|
+
> `"__b64__<base64>"`, where the base64 payload is the little-endian float32 bytes of the
|
|
391
|
+
> batched array in row-major order (`bodyTransform` is width 7, the vectors width 3). Both
|
|
392
|
+
> `SimViewBodyState` (used by `add_state`) and
|
|
393
|
+
> [`SimulationScene.add_trajectory`](#authoring-whole-trajectories) emit this by default
|
|
394
|
+
> (typically ~3-4× smaller than the equivalent plain JSON floats); pass `binary=False` to
|
|
395
|
+
> either to emit plain JSON lists instead. The viewer and the file-merge decode binary
|
|
396
|
+
> fields transparently. `contacts`, scalars, and `time` are always plain JSON.
|
|
397
|
+
>
|
|
398
|
+
> **Server-side columnar repack.** This on-disk, per-frame layout never changes (and
|
|
399
|
+
> `simview merge` still reads/writes it as described above); but when serving a scene to
|
|
400
|
+
> the viewer, the server repacks `states` at load time into whole-trajectory columns --
|
|
401
|
+
> one binary blob per body per numeric field (and one per scalar), covering all `T`
|
|
402
|
+
> frames at once -- and serves a small JSON index (`{"version": 4, "times", "bodies",
|
|
403
|
+
> "scalars"}`) whose entries are `/blob/...` URLs, fetched in parallel and decoded into
|
|
404
|
+
> `Float32Array`s. This avoids materializing thousands of tiny per-frame JS objects
|
|
405
|
+
> just to play back a long trajectory. The repack requires the body set, per-body field
|
|
406
|
+
> set, and field widths to be identical across every frame (`contacts` is exempt and may
|
|
407
|
+
> come and go per frame); if a scene doesn't meet that, the server falls back to serving
|
|
408
|
+
> the legacy per-frame JSON array unchanged, which the viewer also still supports.
|
|
409
|
+
|
|
410
|
+
### Authoring whole trajectories
|
|
411
|
+
|
|
412
|
+
Building states one frame at a time (`add_state`) is fine for short scenes, but for long,
|
|
413
|
+
dense trajectories prefer `SimulationScene.add_trajectory`, which appends an entire
|
|
414
|
+
time-series in one call, converting each body's tensors once instead of per frame —
|
|
415
|
+
noticeably faster save/load than the same data built frame-by-frame. Both paths pack the
|
|
416
|
+
numeric fields as the binary blobs described above by default, so file size is comparable
|
|
417
|
+
either way:
|
|
418
|
+
|
|
419
|
+
```python
|
|
420
|
+
from simview import SimulationScene, BodyShapeType, BodyTrajectory
|
|
421
|
+
|
|
422
|
+
scene = SimulationScene(batch_size=B, scalar_names=[], dt=0.001)
|
|
423
|
+
scene.create_terrain(...)
|
|
424
|
+
scene.create_body(body_name="box", shape_type=BodyShapeType.BOX, hx=0.5, hy=0.3, hz=0.15)
|
|
425
|
+
|
|
426
|
+
# positions: (T, B, 3), orientations: (T, B, 4) as [w, x, y, z]
|
|
427
|
+
# (2-D (T, 3) / (T, 4) is accepted when batch_size == 1)
|
|
428
|
+
scene.add_trajectory(
|
|
429
|
+
times=times, # length-T sequence or tensor
|
|
430
|
+
trajectories=[BodyTrajectory("box", positions, orientations)],
|
|
431
|
+
)
|
|
432
|
+
scene.save("scene.json")
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Pass `binary=False` to emit plain JSON lists instead.
|
|
436
|
+
|
|
437
|
+
Both `BodyTrajectory.name` and `SimViewBodyState`'s `body_name` accept a list of body names
|
|
438
|
+
instead of a single string, for bodies that move rigidly together (e.g. `BodyTrajectory(["link_a", "link_b"], positions, orientations)`) — the same transform (and any optional
|
|
439
|
+
attributes) is applied to every named body, so it only needs to be written once per frame
|
|
440
|
+
instead of once per body.
|
|
441
|
+
|
|
442
|
+
For large simulations, pass `compress=True` to `save()` (or use a `.gz` filepath) to
|
|
443
|
+
gzip the output — `SimulationScene.load()`, the CLI, and the server all detect and
|
|
444
|
+
decompress it transparently regardless of extension.
|
|
445
|
+
|
|
446
|
+
### Parent-relative bodies (rigid and articulated attachments)
|
|
447
|
+
|
|
448
|
+
`create_body` accepts `parent`/`local_transform` to attach a body to another body
|
|
449
|
+
already in the model, instead of it moving in world space:
|
|
450
|
+
|
|
451
|
+
```python
|
|
452
|
+
scene.create_body(body_name="chassis", shape_type=BodyShapeType.BOX, hx=0.6, hy=0.4, hz=0.2)
|
|
453
|
+
|
|
454
|
+
# Rigid attachment (e.g. a wheel bolted to the chassis): a constant offset, defined
|
|
455
|
+
# once, never repeated per frame. Never call add_state/add_trajectory for "left_wheel".
|
|
456
|
+
scene.create_body(
|
|
457
|
+
body_name="left_wheel", shape_type=BodyShapeType.CYLINDER, radius=0.15, height=0.1,
|
|
458
|
+
parent="chassis", local_transform=[0.4, 0.52, 0.0, 1.0, 0.0, 0.0, 0.0],
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
# Articulated attachment (e.g. an arm joint): only `parent` is set, so this body's
|
|
462
|
+
# pose is still supplied every frame via add_state/add_trajectory as usual -- it's
|
|
463
|
+
# just interpreted as local to the chassis's current-frame pose instead of world.
|
|
464
|
+
scene.create_body(body_name="arm_joint", shape_type=BodyShapeType.BOX, hx=0.05, hy=0.05, hz=0.2, parent="chassis")
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
A body's `parent` must already exist in the model (added before its children), which
|
|
468
|
+
also rules out cycles. Merging files containing rigid (constant-offset) bodies works
|
|
469
|
+
the same way — `merge_simulation_files` carries the `parent`/`localTransform` through
|
|
470
|
+
in `model.bodies` and doesn't require or emit per-frame data for them.
|
|
471
|
+
|
|
472
|
+
### Example (2 batches, one box, flat terrain)
|
|
473
|
+
|
|
474
|
+
```json
|
|
475
|
+
{
|
|
476
|
+
"model": {
|
|
477
|
+
"simBatches": 2,
|
|
478
|
+
"scalarNames": ["energy"],
|
|
479
|
+
"dt": 0.1,
|
|
480
|
+
"collapse": false,
|
|
481
|
+
"bodies": [
|
|
482
|
+
{
|
|
483
|
+
"name": "Box",
|
|
484
|
+
"shape": { "type": "box", "hx": 0.5, "hy": 0.5, "hz": 0.5 },
|
|
485
|
+
"availableAttributes": ["velocity"]
|
|
486
|
+
}
|
|
487
|
+
],
|
|
488
|
+
"staticObjects": [],
|
|
489
|
+
"terrain": {
|
|
490
|
+
"dimensions": { "sizeX": 10.0, "sizeY": 10.0, "resolutionX": 2, "resolutionY": 2 },
|
|
491
|
+
"bounds": { "minX": -5.0, "maxX": 5.0, "minY": -5.0, "maxY": 5.0, "minZ": 0.0, "maxZ": 0.0 },
|
|
492
|
+
"isSingleton": true,
|
|
493
|
+
"heightData": [[0.0, 0.0, 0.0, 0.0]],
|
|
494
|
+
"normals": [[[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1]]]
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
"states": [
|
|
498
|
+
{
|
|
499
|
+
"time": 0.0,
|
|
500
|
+
"bodies": [
|
|
501
|
+
{
|
|
502
|
+
"name": "Box",
|
|
503
|
+
"bodyTransform": [
|
|
504
|
+
[0, 0, 1, 1, 0, 0, 0],
|
|
505
|
+
[2, 0, 1, 1, 0, 0, 0]
|
|
506
|
+
],
|
|
507
|
+
"velocity": [
|
|
508
|
+
[0, 0, -0.1],
|
|
509
|
+
[0, 0, 0]
|
|
510
|
+
]
|
|
511
|
+
}
|
|
512
|
+
],
|
|
513
|
+
"energy": [1.2, 0.1]
|
|
514
|
+
}
|
|
515
|
+
]
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Notes
|
|
522
|
+
|
|
523
|
+
- **Quaternion Convention**
|
|
524
|
+
Quaternions use `[w, x, y, z]` (scalar-first format), packed into `bodyTransform` after the position.
|
|
525
|
+
|
|
526
|
+
- **Terrain Consistency**
|
|
527
|
+
Each per-batch `heightData` grid and `normals` list must contain exactly
|
|
528
|
+
`resolutionX * resolutionY` elements.
|
|
529
|
+
|
|
530
|
+
- **Batch Synchronization**
|
|
531
|
+
Per-batch arrays (`bodyTransform`, `velocity`, scalar values, …) must have length `simBatches`.
|
|
532
|
+
When `terrain.isSingleton` is `true`, `heightData`/`normals` hold a single batch that is
|
|
533
|
+
reused for all instances.
|
|
534
|
+
|
|
535
|
+
- **Contact Points**
|
|
536
|
+
The `contacts` field lists point indices into a body's pointcloud `points` for each batch.
|
|
537
|
+
An empty array means no contacts.
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## License and Third-Party Notices
|
|
542
|
+
|
|
543
|
+
SimView is distributed under the [BSD 3-Clause License](LICENSE).
|
|
544
|
+
|
|
545
|
+
The web interface uses [**uPlot**](https://github.com/leeoniya/uPlot) (MIT licensed) for
|
|
546
|
+
scalar and error-metric plotting; it is vendored under `simview/static/lib/`.
|
|
547
|
+
[**three.js**](https://github.com/mrdoob/three.js) (MIT licensed) and
|
|
548
|
+
[**chroma-js**](https://github.com/gka/chroma.js) (MIT licensed) are likewise vendored
|
|
549
|
+
under `simview/static/lib/` (in version-stamped directories, e.g. `lib/three-0.174.0/`)
|
|
550
|
+
rather than loaded from a CDN, so the viewer works fully offline. All third-party
|
|
551
|
+
libraries used by SimView are permissively licensed (MIT/BSD), so there are no licensing
|
|
552
|
+
restrictions on commercial use.
|