simview 3.3__tar.gz → 3.4__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.3/simview.egg-info → simview-3.4}/PKG-INFO +76 -3
- {simview-3.3 → simview-3.4}/README.md +75 -2
- {simview-3.3 → simview-3.4}/pyproject.toml +1 -1
- simview-3.4/simview/__main__.py +463 -0
- simview-3.4/simview/diff.py +360 -0
- simview-3.4/simview/info.py +561 -0
- simview-3.4/simview/terrain.py +558 -0
- {simview-3.3 → simview-3.4/simview.egg-info}/PKG-INFO +76 -3
- {simview-3.3 → simview-3.4}/simview.egg-info/SOURCES.txt +6 -0
- simview-3.4/tests/test_cli.py +621 -0
- simview-3.4/tests/test_diff.py +248 -0
- simview-3.4/tests/test_info.py +255 -0
- simview-3.4/tests/test_terrain_query.py +333 -0
- simview-3.3/simview/__main__.py +0 -172
- simview-3.3/tests/test_cli.py +0 -169
- {simview-3.3 → simview-3.4}/LICENSE +0 -0
- {simview-3.3 → simview-3.4}/MANIFEST.in +0 -0
- {simview-3.3 → simview-3.4}/setup.cfg +0 -0
- {simview-3.3 → simview-3.4}/simview/__init__.py +0 -0
- {simview-3.3 → simview-3.4}/simview/launcher.py +0 -0
- {simview-3.3 → simview-3.4}/simview/live.py +0 -0
- {simview-3.3 → simview-3.4}/simview/merge.py +0 -0
- {simview-3.3 → simview-3.4}/simview/model.py +0 -0
- {simview-3.3 → simview-3.4}/simview/py.typed +0 -0
- {simview-3.3 → simview-3.4}/simview/scene.py +0 -0
- {simview-3.3 → simview-3.4}/simview/server.py +0 -0
- {simview-3.3 → simview-3.4}/simview/state.py +0 -0
- {simview-3.3 → simview-3.4}/simview/static/css/controls.css +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/SimView.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/components/AnimationController.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/components/BatchManager.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/components/InteractionController.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/components/InteractionControls.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/components/Scene.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/components/StateStore.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/config.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/main.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/objects/Body.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/objects/StaticObject.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/objects/Terrain.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/objects/utils.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/ui/AnalysisPanel.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/ui/BatchLegend.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/ui/BodyStateWindow.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/ui/Controls.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/ui/ErrorMetrics.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/ui/Legend.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/ui/PlaybackControls.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/ui/ScalarPlotter.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/utils/blobCodec.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/utils/bodyTransforms.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/utils/csv.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/utils/errorMath.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/utils/injectStyles.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/utils/interpolate.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/utils/liveFollow.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/utils/loadRecordingLibs.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/js/utils/viewState.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/index.min.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/Color.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/chroma.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/colors/colorbrewer.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/colors/w3cx11.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/generator/average.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/generator/bezier.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/generator/blend.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/generator/cubehelix.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/generator/mix.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/generator/random.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/generator/scale.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/_hsx.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/hcg.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/hsi.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/hsl.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/hsv.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/lab.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/lch.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/lrgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/num.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/oklab.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/oklch.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/interpolator/rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/cmyk/cmyk2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/cmyk/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/cmyk/rgb2cmyk.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/css/css2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/css/hsl2css.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/css/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/css/lab2css.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/css/lch2css.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/css/oklab2css.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/css/oklch2css.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/css/rgb2css.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/gl/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hcg/hcg2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hcg/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hcg/rgb2hcg.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hex/hex2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hex/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hex/rgb2hex.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hsi/hsi2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hsi/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hsi/rgb2hsi.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hsl/hsl2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hsl/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hsl/rgb2hsl.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hsv/hsv2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hsv/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/hsv/rgb2hsv.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/input.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lab/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lab/lab-constants.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lab/lab2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lab/rgb2lab.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lch/hcl2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lch/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lch/lab2lch.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lch/lch2lab.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lch/lch2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/lch/rgb2lch.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/named/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/num/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/num/num2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/num/rgb2num.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/oklab/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/oklab/oklab2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/oklab/rgb2oklab.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/oklch/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/oklch/oklch2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/oklch/rgb2oklch.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/rgb/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/temp/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/temp/rgb2temperature.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/io/temp/temperature2rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/alpha.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/clipped.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/darken.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/get.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/luminance.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/mix.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/premultiply.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/saturate.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/set.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/ops/shade.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/analyze.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/clip_rgb.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/contrast.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/contrastAPCA.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/delta-e.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/distance.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/index.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/last.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/limit.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/multiply-matrices.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/scales.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/type.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/unpack.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/utils/valid.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/chroma-js-3.1.2/src/version.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/download.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/js-colormaps.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/tar.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/three-0.174.0/addons/controls/OrbitControls.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/three-0.174.0/addons/libs/lil-gui.module.min.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/three-0.174.0/three.core.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/three-0.174.0/three.module.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/uPlot.esm.js +0 -0
- {simview-3.3 → simview-3.4}/simview/static/lib/uPlot.min.css +0 -0
- {simview-3.3 → simview-3.4}/simview/static/textures/contacts/red-cross0.png +0 -0
- {simview-3.3 → simview-3.4}/simview/static/textures/points/ball0.png +0 -0
- {simview-3.3 → simview-3.4}/simview/static/textures/points/ball1.png +0 -0
- {simview-3.3 → simview-3.4}/simview/templates/index.html +0 -0
- {simview-3.3 → simview-3.4}/simview/utils.py +0 -0
- {simview-3.3 → simview-3.4}/simview.egg-info/dependency_links.txt +0 -0
- {simview-3.3 → simview-3.4}/simview.egg-info/entry_points.txt +0 -0
- {simview-3.3 → simview-3.4}/simview.egg-info/requires.txt +0 -0
- {simview-3.3 → simview-3.4}/simview.egg-info/top_level.txt +0 -0
- {simview-3.3 → simview-3.4}/tests/test_columnar_states.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_contacts.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_launcher.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_live.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_merge.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_roundtrip.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_scene.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_server.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_show.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_terrain.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_trajectory.py +0 -0
- {simview-3.3 → simview-3.4}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simview
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.4
|
|
4
4
|
Summary: THREE.js-based simulation visualization tool
|
|
5
5
|
Author-email: Jan Vlk <vlkjan6@fel.cvut.cz>, Ales Kucera <kuceral4@fel.cvut.cz>, David Korcak <david.korcak@gmail.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -68,16 +68,28 @@ require `torch`.
|
|
|
68
68
|
|
|
69
69
|
Requires **Python 3.12+**.
|
|
70
70
|
|
|
71
|
+
### From PyPI
|
|
72
|
+
|
|
71
73
|
To only view existing simulation JSON files:
|
|
72
74
|
|
|
73
75
|
```bash
|
|
74
|
-
pip install
|
|
76
|
+
pip install simview
|
|
75
77
|
```
|
|
76
78
|
|
|
77
79
|
To also author simulations from Python (installs `torch` and `einops`):
|
|
78
80
|
|
|
79
81
|
```bash
|
|
80
|
-
pip install
|
|
82
|
+
pip install "simview[authoring]"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### From source
|
|
86
|
+
|
|
87
|
+
For working on SimView itself (or to track an unreleased commit), install an
|
|
88
|
+
editable checkout instead:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install -e . # viewing only
|
|
92
|
+
pip install -e ".[authoring]" # viewing + authoring
|
|
81
93
|
```
|
|
82
94
|
|
|
83
95
|
For independent use of this repository, use `venv` or `uv`:
|
|
@@ -108,6 +120,67 @@ all of this using the following command:
|
|
|
108
120
|
simview clear
|
|
109
121
|
```
|
|
110
122
|
|
|
123
|
+
### Inspecting a scene file
|
|
124
|
+
|
|
125
|
+
To print a quick structural summary of a scene JSON file (body/terrain/state
|
|
126
|
+
breakdown, plus consistency warnings) without opening the viewer:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
simview info scene.json # human-readable text
|
|
130
|
+
simview info scene.json --json # machine-readable JSON (for scripts/agents)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Works on gzip-compressed files automatically, and does not require the
|
|
134
|
+
`authoring` extra.
|
|
135
|
+
|
|
136
|
+
### Querying terrain data
|
|
137
|
+
|
|
138
|
+
To read raw numeric terrain values (height, and friction/stiffness if present)
|
|
139
|
+
at a single point or over an area, without opening the viewer:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
simview terrain scene.json --point 1.5 -2.0 # bilinear-interpolated value(s) at (x, y)
|
|
143
|
+
simview terrain scene.json --area # whole terrain extent
|
|
144
|
+
simview terrain scene.json --area -5 5 -5 0 # xmin xmax ymin ymax sub-box
|
|
145
|
+
simview terrain scene.json --area --json # machine-readable JSON (for scripts/agents)
|
|
146
|
+
simview terrain scene.json --area --csv # CSV (for pandas/spreadsheets)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Add `--layer height|friction|stiffness` to restrict to one layer, `--batch N`
|
|
150
|
+
to pick a batch (only matters when the terrain isn't a singleton), and
|
|
151
|
+
`--stride N` to subsample an `--area` query. Like `simview info`, this works
|
|
152
|
+
on gzip-compressed files and doesn't require the `authoring` extra.
|
|
153
|
+
|
|
154
|
+
Pass `--batches A B` instead of `--batch N` to compare two batches directly
|
|
155
|
+
(e.g. a ground-truth terrain vs. a recovered friction/stiffness estimate)
|
|
156
|
+
instead of querying a single one — each layer then reports `value_a`,
|
|
157
|
+
`value_b`, and their `delta`:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
simview terrain scene.json --area --batches 0 1 --json # ground truth vs. estimate, whole extent
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Comparing two batches' trajectories
|
|
164
|
+
|
|
165
|
+
To check how far apart two batches' trajectories are — e.g. ground truth vs.
|
|
166
|
+
a model's prediction, or baseline vs. post-adaptation — without opening the
|
|
167
|
+
viewer:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
simview diff scene.json --batches 0 1 # every body, human-readable text
|
|
171
|
+
simview diff scene.json --batches 0 1 --json # machine-readable JSON (for scripts/agents)
|
|
172
|
+
simview diff scene.json --batches 0 1 --csv # CSV, one row per (body, frame)
|
|
173
|
+
simview diff scene.json --batches 0 1 --body Box # restrict to one body
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
For each body, this reports per-frame position error (meters) and
|
|
177
|
+
orientation error (degrees, quaternion angular distance) between the two
|
|
178
|
+
batches, plus mean/max/final summaries. Add `--every N` to subsample frames,
|
|
179
|
+
and `--pos-threshold METERS`/`--rot-threshold-deg DEGREES` to report the
|
|
180
|
+
first frame where a batch's trajectory diverges past a given tolerance. Like
|
|
181
|
+
`simview info`/`simview terrain`, this works on gzip-compressed files and
|
|
182
|
+
doesn't require the `authoring` extra.
|
|
183
|
+
|
|
111
184
|
### Visualization of exported simulations
|
|
112
185
|
|
|
113
186
|
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:
|
|
@@ -37,16 +37,28 @@ require `torch`.
|
|
|
37
37
|
|
|
38
38
|
Requires **Python 3.12+**.
|
|
39
39
|
|
|
40
|
+
### From PyPI
|
|
41
|
+
|
|
40
42
|
To only view existing simulation JSON files:
|
|
41
43
|
|
|
42
44
|
```bash
|
|
43
|
-
pip install
|
|
45
|
+
pip install simview
|
|
44
46
|
```
|
|
45
47
|
|
|
46
48
|
To also author simulations from Python (installs `torch` and `einops`):
|
|
47
49
|
|
|
48
50
|
```bash
|
|
49
|
-
pip install
|
|
51
|
+
pip install "simview[authoring]"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### From source
|
|
55
|
+
|
|
56
|
+
For working on SimView itself (or to track an unreleased commit), install an
|
|
57
|
+
editable checkout instead:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install -e . # viewing only
|
|
61
|
+
pip install -e ".[authoring]" # viewing + authoring
|
|
50
62
|
```
|
|
51
63
|
|
|
52
64
|
For independent use of this repository, use `venv` or `uv`:
|
|
@@ -77,6 +89,67 @@ all of this using the following command:
|
|
|
77
89
|
simview clear
|
|
78
90
|
```
|
|
79
91
|
|
|
92
|
+
### Inspecting a scene file
|
|
93
|
+
|
|
94
|
+
To print a quick structural summary of a scene JSON file (body/terrain/state
|
|
95
|
+
breakdown, plus consistency warnings) without opening the viewer:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
simview info scene.json # human-readable text
|
|
99
|
+
simview info scene.json --json # machine-readable JSON (for scripts/agents)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Works on gzip-compressed files automatically, and does not require the
|
|
103
|
+
`authoring` extra.
|
|
104
|
+
|
|
105
|
+
### Querying terrain data
|
|
106
|
+
|
|
107
|
+
To read raw numeric terrain values (height, and friction/stiffness if present)
|
|
108
|
+
at a single point or over an area, without opening the viewer:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
simview terrain scene.json --point 1.5 -2.0 # bilinear-interpolated value(s) at (x, y)
|
|
112
|
+
simview terrain scene.json --area # whole terrain extent
|
|
113
|
+
simview terrain scene.json --area -5 5 -5 0 # xmin xmax ymin ymax sub-box
|
|
114
|
+
simview terrain scene.json --area --json # machine-readable JSON (for scripts/agents)
|
|
115
|
+
simview terrain scene.json --area --csv # CSV (for pandas/spreadsheets)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Add `--layer height|friction|stiffness` to restrict to one layer, `--batch N`
|
|
119
|
+
to pick a batch (only matters when the terrain isn't a singleton), and
|
|
120
|
+
`--stride N` to subsample an `--area` query. Like `simview info`, this works
|
|
121
|
+
on gzip-compressed files and doesn't require the `authoring` extra.
|
|
122
|
+
|
|
123
|
+
Pass `--batches A B` instead of `--batch N` to compare two batches directly
|
|
124
|
+
(e.g. a ground-truth terrain vs. a recovered friction/stiffness estimate)
|
|
125
|
+
instead of querying a single one — each layer then reports `value_a`,
|
|
126
|
+
`value_b`, and their `delta`:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
simview terrain scene.json --area --batches 0 1 --json # ground truth vs. estimate, whole extent
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Comparing two batches' trajectories
|
|
133
|
+
|
|
134
|
+
To check how far apart two batches' trajectories are — e.g. ground truth vs.
|
|
135
|
+
a model's prediction, or baseline vs. post-adaptation — without opening the
|
|
136
|
+
viewer:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
simview diff scene.json --batches 0 1 # every body, human-readable text
|
|
140
|
+
simview diff scene.json --batches 0 1 --json # machine-readable JSON (for scripts/agents)
|
|
141
|
+
simview diff scene.json --batches 0 1 --csv # CSV, one row per (body, frame)
|
|
142
|
+
simview diff scene.json --batches 0 1 --body Box # restrict to one body
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
For each body, this reports per-frame position error (meters) and
|
|
146
|
+
orientation error (degrees, quaternion angular distance) between the two
|
|
147
|
+
batches, plus mean/max/final summaries. Add `--every N` to subsample frames,
|
|
148
|
+
and `--pos-threshold METERS`/`--rot-threshold-deg DEGREES` to report the
|
|
149
|
+
first frame where a batch's trajectory diverges past a given tolerance. Like
|
|
150
|
+
`simview info`/`simview terrain`, this works on gzip-compressed files and
|
|
151
|
+
doesn't require the `authoring` extra.
|
|
152
|
+
|
|
80
153
|
### Visualization of exported simulations
|
|
81
154
|
|
|
82
155
|
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:
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import gzip
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import shutil
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from simview import CACHE_DIR, __version__
|
|
11
|
+
from simview.server import SimViewServer
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger("simview.cli")
|
|
14
|
+
|
|
15
|
+
_logging_configured = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _configure_logging() -> None:
|
|
19
|
+
"""CLI entry point default: INFO-level, message-only output on stderr.
|
|
20
|
+
|
|
21
|
+
Kept minimal (no timestamps/level names) since this is user-facing CLI
|
|
22
|
+
chatter, not application log output meant for parsing. Idempotent, so
|
|
23
|
+
calling `main()` more than once in the same process (e.g. across tests)
|
|
24
|
+
doesn't stack up duplicate handlers.
|
|
25
|
+
"""
|
|
26
|
+
global _logging_configured
|
|
27
|
+
if _logging_configured:
|
|
28
|
+
return
|
|
29
|
+
handler = logging.StreamHandler(sys.stderr)
|
|
30
|
+
handler.setFormatter(logging.Formatter("%(message)s"))
|
|
31
|
+
root = logging.getLogger("simview")
|
|
32
|
+
root.addHandler(handler)
|
|
33
|
+
root.setLevel(logging.INFO)
|
|
34
|
+
_logging_configured = True
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _package_version() -> str:
|
|
38
|
+
"""The installed simview version, with a sensible fallback for editable
|
|
39
|
+
checkouts run without an installed distribution (e.g. `python -m simview`
|
|
40
|
+
from a source tree that was never `pip install -e`'d)."""
|
|
41
|
+
if __version__ == "0.0.0.dev0":
|
|
42
|
+
return "unknown (not installed)"
|
|
43
|
+
return __version__
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def clear_cache():
|
|
47
|
+
# Legacy cache directories (kept for cleanup of older installs).
|
|
48
|
+
for cache_dir in (Path("/tmp") / CACHE_DIR, Path.home() / ".cache" / CACHE_DIR):
|
|
49
|
+
if cache_dir.exists():
|
|
50
|
+
logger.info("Removing %s", cache_dir)
|
|
51
|
+
shutil.rmtree(cache_dir, ignore_errors=True)
|
|
52
|
+
|
|
53
|
+
# Temp scenes written by SimViewLauncher (tempfile.mkstemp with this prefix);
|
|
54
|
+
# these leak if a launched viewer is killed before cleanup runs.
|
|
55
|
+
removed = 0
|
|
56
|
+
for leftover in Path(tempfile.gettempdir()).glob("simview_viz_*.json"):
|
|
57
|
+
try:
|
|
58
|
+
leftover.unlink()
|
|
59
|
+
removed += 1
|
|
60
|
+
except OSError as e:
|
|
61
|
+
logger.warning("Could not remove %s: %s", leftover, e)
|
|
62
|
+
if removed:
|
|
63
|
+
logger.info("Removed %d leftover temporary scene file(s).", removed)
|
|
64
|
+
|
|
65
|
+
logger.info("Cache cleared.")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def run_info(path: Path, as_json: bool) -> None:
|
|
69
|
+
"""Print a structural summary of the scene JSON at `path` to stdout."""
|
|
70
|
+
from simview.info import format_text, summarize_scene
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
summary = summarize_scene(path)
|
|
74
|
+
except (json.JSONDecodeError, ValueError, KeyError) as e:
|
|
75
|
+
logger.error("Error: Could not parse '%s' as a scene file: %s", path, e)
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
|
|
78
|
+
if as_json:
|
|
79
|
+
print(json.dumps(summary, indent=2))
|
|
80
|
+
else:
|
|
81
|
+
print(format_text(summary))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def run_terrain(path: Path, args: argparse.Namespace) -> None:
|
|
85
|
+
"""Print terrain value(s) at a point or over an area, from the scene JSON
|
|
86
|
+
at `path`, to stdout. With `--batches A B`, compares two batches instead
|
|
87
|
+
of querying a single one."""
|
|
88
|
+
from simview import terrain as terrain_mod
|
|
89
|
+
|
|
90
|
+
if (args.point is None) == (args.area is None):
|
|
91
|
+
logger.error(
|
|
92
|
+
"Error: 'simview terrain' requires exactly one of --point or --area."
|
|
93
|
+
)
|
|
94
|
+
sys.exit(1)
|
|
95
|
+
if args.area is not None and len(args.area) not in (0, 4):
|
|
96
|
+
logger.error(
|
|
97
|
+
"Error: --area requires 0 values (whole terrain extent) or 4 values "
|
|
98
|
+
"(xmin xmax ymin ymax); got %d.",
|
|
99
|
+
len(args.area),
|
|
100
|
+
)
|
|
101
|
+
sys.exit(1)
|
|
102
|
+
if args.json and args.csv:
|
|
103
|
+
logger.error("Error: --json and --csv are mutually exclusive.")
|
|
104
|
+
sys.exit(1)
|
|
105
|
+
|
|
106
|
+
diff_mode = args.batches is not None
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
model_data = terrain_mod.load_scene_model(path)
|
|
110
|
+
if diff_mode:
|
|
111
|
+
batch_a, batch_b = args.batches
|
|
112
|
+
if args.point is not None:
|
|
113
|
+
result = terrain_mod.query_point_diff(
|
|
114
|
+
model_data,
|
|
115
|
+
args.point[0],
|
|
116
|
+
args.point[1],
|
|
117
|
+
batch_a,
|
|
118
|
+
batch_b,
|
|
119
|
+
args.layer,
|
|
120
|
+
)
|
|
121
|
+
text = terrain_mod.format_point_diff_text(result)
|
|
122
|
+
csv_text = terrain_mod.format_point_diff_csv(result)
|
|
123
|
+
else:
|
|
124
|
+
bounds = tuple(args.area) if args.area else None
|
|
125
|
+
result = terrain_mod.query_area_diff(
|
|
126
|
+
model_data, bounds, batch_a, batch_b, args.layer, args.stride
|
|
127
|
+
)
|
|
128
|
+
text = terrain_mod.format_area_diff_text(result)
|
|
129
|
+
csv_text = terrain_mod.format_area_diff_csv(result)
|
|
130
|
+
elif args.point is not None:
|
|
131
|
+
result = terrain_mod.query_point(
|
|
132
|
+
model_data, args.point[0], args.point[1], args.layer, args.batch
|
|
133
|
+
)
|
|
134
|
+
text = terrain_mod.format_point_text(result)
|
|
135
|
+
csv_text = terrain_mod.format_point_csv(result)
|
|
136
|
+
else:
|
|
137
|
+
bounds = tuple(args.area) if args.area else None
|
|
138
|
+
result = terrain_mod.query_area(
|
|
139
|
+
model_data, bounds, args.layer, args.batch, args.stride
|
|
140
|
+
)
|
|
141
|
+
text = terrain_mod.format_area_text(result)
|
|
142
|
+
csv_text = terrain_mod.format_area_csv(result)
|
|
143
|
+
except (json.JSONDecodeError, ValueError, KeyError) as e:
|
|
144
|
+
logger.error("Error: %s", e)
|
|
145
|
+
sys.exit(1)
|
|
146
|
+
|
|
147
|
+
if args.csv:
|
|
148
|
+
print(csv_text, end="")
|
|
149
|
+
elif args.json:
|
|
150
|
+
print(json.dumps(result, indent=2))
|
|
151
|
+
else:
|
|
152
|
+
print(text)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def run_diff(path: Path, args: argparse.Namespace) -> None:
|
|
156
|
+
"""Print a trajectory divergence report between two batches of the scene
|
|
157
|
+
JSON at `path`, to stdout."""
|
|
158
|
+
from simview import diff as diff_mod
|
|
159
|
+
|
|
160
|
+
if args.batches is None:
|
|
161
|
+
logger.error(
|
|
162
|
+
"Error: 'simview diff' requires --batches A B, e.g. 'simview diff "
|
|
163
|
+
"scene.json --batches 0 1'."
|
|
164
|
+
)
|
|
165
|
+
sys.exit(1)
|
|
166
|
+
if args.json and args.csv:
|
|
167
|
+
logger.error("Error: --json and --csv are mutually exclusive.")
|
|
168
|
+
sys.exit(1)
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
model_data, states_data = diff_mod.load_scene(path)
|
|
172
|
+
result = diff_mod.compute_trajectory_diff(
|
|
173
|
+
model_data,
|
|
174
|
+
states_data,
|
|
175
|
+
args.batches[0],
|
|
176
|
+
args.batches[1],
|
|
177
|
+
body=args.body,
|
|
178
|
+
every=args.every,
|
|
179
|
+
pos_threshold=args.pos_threshold,
|
|
180
|
+
rot_threshold_deg=args.rot_threshold_deg,
|
|
181
|
+
)
|
|
182
|
+
text = diff_mod.format_diff_text(result)
|
|
183
|
+
csv_text = diff_mod.format_diff_csv(result)
|
|
184
|
+
except (json.JSONDecodeError, ValueError, KeyError) as e:
|
|
185
|
+
logger.error("Error: %s", e)
|
|
186
|
+
sys.exit(1)
|
|
187
|
+
|
|
188
|
+
if args.csv:
|
|
189
|
+
print(csv_text, end="")
|
|
190
|
+
elif args.json:
|
|
191
|
+
print(json.dumps(result, indent=2))
|
|
192
|
+
else:
|
|
193
|
+
print(text)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def save_merged(paths: list[Path], out_path: Path) -> None:
|
|
197
|
+
"""Merge `paths` (must be >= 2) and write the result to `out_path`, gzipped
|
|
198
|
+
if it ends in .gz, without starting the server."""
|
|
199
|
+
if len(paths) < 2:
|
|
200
|
+
logger.error("Error: --save-merged requires at least 2 input files to merge.")
|
|
201
|
+
sys.exit(1)
|
|
202
|
+
|
|
203
|
+
from simview.merge import merge_simulation_files
|
|
204
|
+
|
|
205
|
+
merged = merge_simulation_files(paths)
|
|
206
|
+
payload = json.dumps(merged).encode("utf-8")
|
|
207
|
+
if out_path.suffix == ".gz":
|
|
208
|
+
payload = gzip.compress(payload, compresslevel=1)
|
|
209
|
+
out_path.write_bytes(payload)
|
|
210
|
+
logger.info("Merged scene written to %s", out_path)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
214
|
+
parser = argparse.ArgumentParser(description="SimView CLI")
|
|
215
|
+
parser.add_argument(
|
|
216
|
+
"inputs",
|
|
217
|
+
nargs="*",
|
|
218
|
+
help=(
|
|
219
|
+
"Path(s) to simulation JSON file(s) to visualize, 'clear' to clear "
|
|
220
|
+
"cache, 'info <file>' to print a structural summary of one scene "
|
|
221
|
+
"file, 'terrain <file>' to query terrain values at a point or over "
|
|
222
|
+
"an area, or 'diff <file>' to compare two batches' trajectories. "
|
|
223
|
+
"Multiple visualize-mode files are merged into one scene, each "
|
|
224
|
+
"file's batches appended as extra batches (e.g. a real-world "
|
|
225
|
+
"recording plus a simulated rerun)."
|
|
226
|
+
),
|
|
227
|
+
)
|
|
228
|
+
parser.add_argument(
|
|
229
|
+
"--version",
|
|
230
|
+
action="store_true",
|
|
231
|
+
help="Print the installed simview version and exit.",
|
|
232
|
+
)
|
|
233
|
+
parser.add_argument(
|
|
234
|
+
"--json",
|
|
235
|
+
action="store_true",
|
|
236
|
+
help=(
|
|
237
|
+
"With 'simview info <file>', 'simview terrain <file>', or "
|
|
238
|
+
"'simview diff <file>', print machine-readable JSON instead of "
|
|
239
|
+
"text."
|
|
240
|
+
),
|
|
241
|
+
)
|
|
242
|
+
parser.add_argument(
|
|
243
|
+
"--csv",
|
|
244
|
+
action="store_true",
|
|
245
|
+
help=(
|
|
246
|
+
"With 'simview terrain <file>' or 'simview diff <file>', print "
|
|
247
|
+
"CSV instead of text. Mutually exclusive with --json."
|
|
248
|
+
),
|
|
249
|
+
)
|
|
250
|
+
parser.add_argument(
|
|
251
|
+
"--point",
|
|
252
|
+
nargs=2,
|
|
253
|
+
type=float,
|
|
254
|
+
metavar=("X", "Y"),
|
|
255
|
+
help=(
|
|
256
|
+
"With 'simview terrain <file>', query terrain value(s) at a single "
|
|
257
|
+
"(x, y) point (bilinear-interpolated). Mutually exclusive with "
|
|
258
|
+
"--area."
|
|
259
|
+
),
|
|
260
|
+
)
|
|
261
|
+
parser.add_argument(
|
|
262
|
+
"--area",
|
|
263
|
+
nargs="*",
|
|
264
|
+
type=float,
|
|
265
|
+
metavar="BOUND",
|
|
266
|
+
help=(
|
|
267
|
+
"With 'simview terrain <file>', query terrain values over a "
|
|
268
|
+
"rectangular area: pass no values for the whole terrain extent, "
|
|
269
|
+
"or exactly 4 values 'XMIN XMAX YMIN YMAX' for a sub-box. Mutually "
|
|
270
|
+
"exclusive with --point."
|
|
271
|
+
),
|
|
272
|
+
)
|
|
273
|
+
parser.add_argument(
|
|
274
|
+
"--layer",
|
|
275
|
+
choices=["height", "friction", "stiffness", "all"],
|
|
276
|
+
default="all",
|
|
277
|
+
help=(
|
|
278
|
+
"With 'simview terrain <file>', which terrain layer(s) to query "
|
|
279
|
+
"(default: all present)."
|
|
280
|
+
),
|
|
281
|
+
)
|
|
282
|
+
parser.add_argument(
|
|
283
|
+
"--batch",
|
|
284
|
+
type=int,
|
|
285
|
+
default=0,
|
|
286
|
+
help="With 'simview terrain <file>', batch index to query (default: 0).",
|
|
287
|
+
)
|
|
288
|
+
parser.add_argument(
|
|
289
|
+
"--stride",
|
|
290
|
+
type=int,
|
|
291
|
+
default=1,
|
|
292
|
+
help=(
|
|
293
|
+
"With 'simview terrain <file> --area', subsample every Nth grid "
|
|
294
|
+
"point in both directions (default: 1)."
|
|
295
|
+
),
|
|
296
|
+
)
|
|
297
|
+
parser.add_argument(
|
|
298
|
+
"--batches",
|
|
299
|
+
nargs=2,
|
|
300
|
+
type=int,
|
|
301
|
+
metavar=("A", "B"),
|
|
302
|
+
help=(
|
|
303
|
+
"Two batch indices to compare. Required by 'simview diff <file>'. "
|
|
304
|
+
"With 'simview terrain <file> --point|--area', switches into "
|
|
305
|
+
"cross-batch diff mode (value_a/value_b/delta per layer) instead "
|
|
306
|
+
"of a single-batch query, and takes precedence over --batch if "
|
|
307
|
+
"both are given."
|
|
308
|
+
),
|
|
309
|
+
)
|
|
310
|
+
parser.add_argument(
|
|
311
|
+
"--body",
|
|
312
|
+
type=str,
|
|
313
|
+
default=None,
|
|
314
|
+
metavar="NAME",
|
|
315
|
+
help=(
|
|
316
|
+
"With 'simview diff <file>', restrict the trajectory diff to one "
|
|
317
|
+
"body (matched by its full label, e.g. 'wheel_fl+wheel_fr', or by "
|
|
318
|
+
"any single name inside a rigidly-grouped body). Default: diff "
|
|
319
|
+
"every body present in states."
|
|
320
|
+
),
|
|
321
|
+
)
|
|
322
|
+
parser.add_argument(
|
|
323
|
+
"--every",
|
|
324
|
+
type=int,
|
|
325
|
+
default=1,
|
|
326
|
+
help="With 'simview diff <file>', sample every Nth frame (default: 1).",
|
|
327
|
+
)
|
|
328
|
+
parser.add_argument(
|
|
329
|
+
"--pos-threshold",
|
|
330
|
+
type=float,
|
|
331
|
+
default=None,
|
|
332
|
+
metavar="METERS",
|
|
333
|
+
help=(
|
|
334
|
+
"With 'simview diff <file>', report the first sampled frame whose "
|
|
335
|
+
"position error exceeds this many meters."
|
|
336
|
+
),
|
|
337
|
+
)
|
|
338
|
+
parser.add_argument(
|
|
339
|
+
"--rot-threshold-deg",
|
|
340
|
+
type=float,
|
|
341
|
+
default=None,
|
|
342
|
+
metavar="DEGREES",
|
|
343
|
+
help=(
|
|
344
|
+
"With 'simview diff <file>', report the first sampled frame whose "
|
|
345
|
+
"orientation error exceeds this many degrees."
|
|
346
|
+
),
|
|
347
|
+
)
|
|
348
|
+
parser.add_argument(
|
|
349
|
+
"--host",
|
|
350
|
+
type=str,
|
|
351
|
+
default="127.0.0.1",
|
|
352
|
+
help="Host/interface for the server to bind to (default: 127.0.0.1).",
|
|
353
|
+
)
|
|
354
|
+
parser.add_argument(
|
|
355
|
+
"--port",
|
|
356
|
+
type=int,
|
|
357
|
+
default=5420,
|
|
358
|
+
help=(
|
|
359
|
+
"Port for the server to use (default: 5420). If it's already taken, "
|
|
360
|
+
"the next free port is used instead."
|
|
361
|
+
),
|
|
362
|
+
)
|
|
363
|
+
parser.add_argument(
|
|
364
|
+
"--no-browser",
|
|
365
|
+
action="store_true",
|
|
366
|
+
help="Don't automatically open a browser tab once the server starts.",
|
|
367
|
+
)
|
|
368
|
+
parser.add_argument(
|
|
369
|
+
"--save-merged",
|
|
370
|
+
type=str,
|
|
371
|
+
default=None,
|
|
372
|
+
metavar="PATH",
|
|
373
|
+
help=(
|
|
374
|
+
"Merge the given input files and write the result to PATH instead of "
|
|
375
|
+
"launching the viewer. Requires at least 2 input files. Gzips the "
|
|
376
|
+
"output if PATH ends in .gz."
|
|
377
|
+
),
|
|
378
|
+
)
|
|
379
|
+
return parser
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def main():
|
|
383
|
+
_configure_logging()
|
|
384
|
+
parser = build_parser()
|
|
385
|
+
args = parser.parse_args()
|
|
386
|
+
|
|
387
|
+
if args.version:
|
|
388
|
+
print(_package_version())
|
|
389
|
+
return
|
|
390
|
+
|
|
391
|
+
if not args.inputs:
|
|
392
|
+
parser.print_help()
|
|
393
|
+
sys.exit(1)
|
|
394
|
+
|
|
395
|
+
if args.inputs and args.inputs[0] == "info":
|
|
396
|
+
info_args = args.inputs[1:]
|
|
397
|
+
if len(info_args) != 1:
|
|
398
|
+
logger.error(
|
|
399
|
+
"Error: 'simview info' requires exactly one file argument, e.g. "
|
|
400
|
+
"'simview info scene.json'."
|
|
401
|
+
)
|
|
402
|
+
sys.exit(1)
|
|
403
|
+
info_path = Path(info_args[0])
|
|
404
|
+
if not (info_path.exists() and info_path.is_file()):
|
|
405
|
+
logger.error("Error: File '%s' not found or is not a file.", info_path)
|
|
406
|
+
sys.exit(1)
|
|
407
|
+
run_info(info_path, as_json=args.json)
|
|
408
|
+
return
|
|
409
|
+
|
|
410
|
+
if args.inputs and args.inputs[0] == "terrain":
|
|
411
|
+
terrain_args = args.inputs[1:]
|
|
412
|
+
if len(terrain_args) != 1:
|
|
413
|
+
logger.error(
|
|
414
|
+
"Error: 'simview terrain' requires exactly one file argument, "
|
|
415
|
+
"e.g. 'simview terrain scene.json --point 0 0'."
|
|
416
|
+
)
|
|
417
|
+
sys.exit(1)
|
|
418
|
+
terrain_path = Path(terrain_args[0])
|
|
419
|
+
if not (terrain_path.exists() and terrain_path.is_file()):
|
|
420
|
+
logger.error("Error: File '%s' not found or is not a file.", terrain_path)
|
|
421
|
+
sys.exit(1)
|
|
422
|
+
run_terrain(terrain_path, args)
|
|
423
|
+
return
|
|
424
|
+
|
|
425
|
+
if args.inputs and args.inputs[0] == "diff":
|
|
426
|
+
diff_args = args.inputs[1:]
|
|
427
|
+
if len(diff_args) != 1:
|
|
428
|
+
logger.error(
|
|
429
|
+
"Error: 'simview diff' requires exactly one file argument, "
|
|
430
|
+
"e.g. 'simview diff scene.json --batches 0 1'."
|
|
431
|
+
)
|
|
432
|
+
sys.exit(1)
|
|
433
|
+
diff_path = Path(diff_args[0])
|
|
434
|
+
if not (diff_path.exists() and diff_path.is_file()):
|
|
435
|
+
logger.error("Error: File '%s' not found or is not a file.", diff_path)
|
|
436
|
+
sys.exit(1)
|
|
437
|
+
run_diff(diff_path, args)
|
|
438
|
+
return
|
|
439
|
+
|
|
440
|
+
if args.inputs == ["clear"]:
|
|
441
|
+
clear_cache()
|
|
442
|
+
return
|
|
443
|
+
|
|
444
|
+
paths = [Path(p) for p in args.inputs]
|
|
445
|
+
for path in paths:
|
|
446
|
+
if not (path.exists() and path.is_file()):
|
|
447
|
+
logger.error("Error: File '%s' not found or is not a file.", path)
|
|
448
|
+
sys.exit(1)
|
|
449
|
+
|
|
450
|
+
if args.save_merged:
|
|
451
|
+
save_merged(paths, Path(args.save_merged))
|
|
452
|
+
return
|
|
453
|
+
|
|
454
|
+
SimViewServer.start(
|
|
455
|
+
sim_path=paths if len(paths) > 1 else paths[0],
|
|
456
|
+
host=args.host,
|
|
457
|
+
preferred_port=args.port,
|
|
458
|
+
open_browser=not args.no_browser,
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
if __name__ == "__main__":
|
|
463
|
+
main()
|