simview 3.4__tar.gz → 3.5__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.5/PKG-INFO +136 -0
- simview-3.5/README.md +103 -0
- {simview-3.4 → simview-3.5}/pyproject.toml +7 -2
- {simview-3.4 → simview-3.5}/simview/__main__.py +91 -4
- {simview-3.4 → simview-3.5}/simview/diff.py +58 -24
- simview-3.5/simview/render.py +141 -0
- {simview-3.4 → simview-3.5}/simview/static/js/components/InteractionController.js +77 -6
- {simview-3.4 → simview-3.5}/simview/static/js/objects/Terrain.js +123 -11
- {simview-3.4 → simview-3.5}/simview/static/js/ui/Controls.js +76 -0
- {simview-3.4 → simview-3.5}/simview/static/js/ui/ErrorMetrics.js +8 -2
- {simview-3.4 → simview-3.5}/simview/static/js/ui/Legend.js +18 -2
- simview-3.5/simview/static/js/utils/batchPresets.js +28 -0
- {simview-3.4 → simview-3.5}/simview/terrain.py +13 -2
- simview-3.5/simview.egg-info/PKG-INFO +136 -0
- {simview-3.4 → simview-3.5}/simview.egg-info/SOURCES.txt +2 -0
- {simview-3.4 → simview-3.5}/simview.egg-info/requires.txt +3 -0
- {simview-3.4 → simview-3.5}/tests/test_cli.py +118 -0
- {simview-3.4 → simview-3.5}/tests/test_diff.py +55 -0
- {simview-3.4 → simview-3.5}/tests/test_terrain_query.py +33 -0
- simview-3.4/PKG-INFO +0 -626
- simview-3.4/README.md +0 -595
- simview-3.4/simview.egg-info/PKG-INFO +0 -626
- {simview-3.4 → simview-3.5}/LICENSE +0 -0
- {simview-3.4 → simview-3.5}/MANIFEST.in +0 -0
- {simview-3.4 → simview-3.5}/setup.cfg +0 -0
- {simview-3.4 → simview-3.5}/simview/__init__.py +0 -0
- {simview-3.4 → simview-3.5}/simview/info.py +0 -0
- {simview-3.4 → simview-3.5}/simview/launcher.py +0 -0
- {simview-3.4 → simview-3.5}/simview/live.py +0 -0
- {simview-3.4 → simview-3.5}/simview/merge.py +0 -0
- {simview-3.4 → simview-3.5}/simview/model.py +0 -0
- {simview-3.4 → simview-3.5}/simview/py.typed +0 -0
- {simview-3.4 → simview-3.5}/simview/scene.py +0 -0
- {simview-3.4 → simview-3.5}/simview/server.py +0 -0
- {simview-3.4 → simview-3.5}/simview/state.py +0 -0
- {simview-3.4 → simview-3.5}/simview/static/css/controls.css +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/SimView.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/components/AnimationController.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/components/BatchManager.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/components/InteractionControls.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/components/Scene.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/components/StateStore.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/config.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/main.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/objects/Body.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/objects/StaticObject.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/objects/utils.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/ui/AnalysisPanel.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/ui/BatchLegend.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/ui/BodyStateWindow.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/ui/PlaybackControls.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/ui/ScalarPlotter.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/utils/blobCodec.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/utils/bodyTransforms.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/utils/csv.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/utils/errorMath.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/utils/injectStyles.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/utils/interpolate.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/utils/liveFollow.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/utils/loadRecordingLibs.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/js/utils/viewState.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/index.min.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/Color.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/chroma.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/colors/colorbrewer.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/colors/w3cx11.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/generator/average.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/generator/bezier.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/generator/blend.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/generator/cubehelix.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/generator/mix.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/generator/random.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/generator/scale.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/_hsx.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/hcg.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/hsi.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/hsl.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/hsv.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/lab.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/lch.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/lrgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/num.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/oklab.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/oklch.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/interpolator/rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/cmyk/cmyk2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/cmyk/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/cmyk/rgb2cmyk.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/css/css2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/css/hsl2css.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/css/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/css/lab2css.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/css/lch2css.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/css/oklab2css.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/css/oklch2css.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/css/rgb2css.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/gl/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hcg/hcg2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hcg/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hcg/rgb2hcg.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hex/hex2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hex/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hex/rgb2hex.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hsi/hsi2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hsi/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hsi/rgb2hsi.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hsl/hsl2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hsl/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hsl/rgb2hsl.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hsv/hsv2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hsv/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/hsv/rgb2hsv.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/input.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lab/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lab/lab-constants.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lab/lab2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lab/rgb2lab.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lch/hcl2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lch/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lch/lab2lch.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lch/lch2lab.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lch/lch2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/lch/rgb2lch.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/named/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/num/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/num/num2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/num/rgb2num.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/oklab/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/oklab/oklab2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/oklab/rgb2oklab.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/oklch/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/oklch/oklch2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/oklch/rgb2oklch.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/rgb/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/temp/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/temp/rgb2temperature.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/io/temp/temperature2rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/alpha.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/clipped.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/darken.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/get.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/luminance.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/mix.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/premultiply.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/saturate.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/set.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/ops/shade.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/analyze.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/clip_rgb.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/contrast.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/contrastAPCA.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/delta-e.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/distance.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/index.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/last.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/limit.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/multiply-matrices.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/scales.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/type.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/unpack.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/utils/valid.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/chroma-js-3.1.2/src/version.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/download.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/js-colormaps.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/tar.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/three-0.174.0/addons/controls/OrbitControls.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/three-0.174.0/addons/libs/lil-gui.module.min.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/three-0.174.0/three.core.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/three-0.174.0/three.module.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/uPlot.esm.js +0 -0
- {simview-3.4 → simview-3.5}/simview/static/lib/uPlot.min.css +0 -0
- {simview-3.4 → simview-3.5}/simview/static/textures/contacts/red-cross0.png +0 -0
- {simview-3.4 → simview-3.5}/simview/static/textures/points/ball0.png +0 -0
- {simview-3.4 → simview-3.5}/simview/static/textures/points/ball1.png +0 -0
- {simview-3.4 → simview-3.5}/simview/templates/index.html +0 -0
- {simview-3.4 → simview-3.5}/simview/utils.py +0 -0
- {simview-3.4 → simview-3.5}/simview.egg-info/dependency_links.txt +0 -0
- {simview-3.4 → simview-3.5}/simview.egg-info/entry_points.txt +0 -0
- {simview-3.4 → simview-3.5}/simview.egg-info/top_level.txt +0 -0
- {simview-3.4 → simview-3.5}/tests/test_columnar_states.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_contacts.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_info.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_launcher.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_live.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_merge.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_roundtrip.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_scene.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_server.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_show.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_terrain.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_trajectory.py +0 -0
- {simview-3.4 → simview-3.5}/tests/test_utils.py +0 -0
simview-3.5/PKG-INFO
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simview
|
|
3
|
+
Version: 3.5
|
|
4
|
+
Summary: THREE.js-based simulation visualization tool
|
|
5
|
+
Author-email: Jan Vlk <vlkjan6@fel.cvut.cz>, Ales Kucera <kuceral4@fel.cvut.cz>, David Korcak <david.korcak@gmail.com>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://vlk-jan.github.io/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 :: 5 - Production/Stable
|
|
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: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: fastapi>=0.135.3
|
|
21
|
+
Requires-Dist: httptools>=0.7.1; sys_platform != "win32"
|
|
22
|
+
Requires-Dist: jinja2>=3.1.6
|
|
23
|
+
Requires-Dist: orjson>=3.11.8
|
|
24
|
+
Requires-Dist: uvicorn>=0.44.0
|
|
25
|
+
Requires-Dist: uvloop>=0.22.1; sys_platform != "win32"
|
|
26
|
+
Provides-Extra: authoring
|
|
27
|
+
Requires-Dist: torch; extra == "authoring"
|
|
28
|
+
Requires-Dist: einops; extra == "authoring"
|
|
29
|
+
Requires-Dist: numpy; extra == "authoring"
|
|
30
|
+
Provides-Extra: render
|
|
31
|
+
Requires-Dist: playwright>=1.40; extra == "render"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# SimView Visualizer
|
|
35
|
+
|
|
36
|
+
[](https://vlk-jan.github.io/simview/)
|
|
37
|
+
[](https://vlk-jan.github.io/assets/demos/simview/)
|
|
38
|
+
|
|
39
|
+
**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.
|
|
40
|
+
|
|
41
|
+
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.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Documentation
|
|
46
|
+
|
|
47
|
+
Full documentation — usage guides, CLI reference, the JSON format specification,
|
|
48
|
+
the API reference, and developer/architecture docs — is available at:
|
|
49
|
+
|
|
50
|
+
**[https://vlk-jan.github.io/simview/](https://vlk-jan.github.io/simview/)**
|
|
51
|
+
|
|
52
|
+
Want to try it without installing anything? There's a
|
|
53
|
+
[live browser demo](https://vlk-jan.github.io/assets/demos/simview/)
|
|
54
|
+
(static snapshot, no backend needed).
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Features
|
|
59
|
+
|
|
60
|
+
- **Batched Simulations**: Visualize multiple simulation instances side-by-side.
|
|
61
|
+
- **Shared Terrain**: Efficient rendering with shared terrain across all batches.
|
|
62
|
+
- **Interactive UI**: Web-based controls for playback, camera, and data inspection.
|
|
63
|
+
- **Python API**: Easy-to-use API for generating scenes and launching the visualizer directly from your code.
|
|
64
|
+
- **JSON Support**: Load and save simulation data using a portable JSON format.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
The easiest way to get started is to run the provided example script:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
python example.py
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This script demonstrates how to use the Python API to create a simulation with wavy terrain, dynamic bodies, and time-series data.
|
|
77
|
+
|
|
78
|
+
The Python authoring API (`simview.scene`, `simview.state`, `simview.model`) depends on
|
|
79
|
+
`torch` and `einops`. Install them with the optional `authoring` extra shown below.
|
|
80
|
+
Only these are needed to *build* simulations; *viewing* an existing JSON file does not
|
|
81
|
+
require `torch`.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Installation
|
|
86
|
+
|
|
87
|
+
Requires **Python 3.12+**.
|
|
88
|
+
|
|
89
|
+
### From PyPI
|
|
90
|
+
|
|
91
|
+
To only view existing simulation JSON files:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install simview
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
To also author simulations from Python (installs `torch` and `einops`):
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install "simview[authoring]"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### From source
|
|
104
|
+
|
|
105
|
+
For working on SimView itself (or to track an unreleased commit), install an
|
|
106
|
+
editable checkout instead:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install -e . # viewing only
|
|
110
|
+
pip install -e ".[authoring]" # viewing + authoring
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
For independent use of this repository, use `venv` or `uv`:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
python -m venv .venv
|
|
117
|
+
source .venv/bin/activate
|
|
118
|
+
pip install -e ".[authoring]"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
uv sync --extra authoring
|
|
123
|
+
source .venv/bin/activate
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
SimView is distributed under the [BSD 3-Clause License](LICENSE). The web interface
|
|
131
|
+
vendors [uPlot](https://github.com/leeoniya/uPlot), [three.js](https://github.com/mrdoob/three.js),
|
|
132
|
+
and [chroma-js](https://github.com/gka/chroma.js) (all MIT licensed) under
|
|
133
|
+
`simview/static/lib/` so the viewer works fully offline — see the
|
|
134
|
+
[documentation](https://vlk-jan.github.io/simview/dev/architecture/#vendored-third-party-libraries)
|
|
135
|
+
for details. All third-party libraries used by SimView are permissively licensed
|
|
136
|
+
(MIT/BSD), so there are no licensing restrictions on commercial use.
|
simview-3.5/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# SimView Visualizer
|
|
2
|
+
|
|
3
|
+
[](https://vlk-jan.github.io/simview/)
|
|
4
|
+
[](https://vlk-jan.github.io/assets/demos/simview/)
|
|
5
|
+
|
|
6
|
+
**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.
|
|
7
|
+
|
|
8
|
+
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.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Documentation
|
|
13
|
+
|
|
14
|
+
Full documentation — usage guides, CLI reference, the JSON format specification,
|
|
15
|
+
the API reference, and developer/architecture docs — is available at:
|
|
16
|
+
|
|
17
|
+
**[https://vlk-jan.github.io/simview/](https://vlk-jan.github.io/simview/)**
|
|
18
|
+
|
|
19
|
+
Want to try it without installing anything? There's a
|
|
20
|
+
[live browser demo](https://vlk-jan.github.io/assets/demos/simview/)
|
|
21
|
+
(static snapshot, no backend needed).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **Batched Simulations**: Visualize multiple simulation instances side-by-side.
|
|
28
|
+
- **Shared Terrain**: Efficient rendering with shared terrain across all batches.
|
|
29
|
+
- **Interactive UI**: Web-based controls for playback, camera, and data inspection.
|
|
30
|
+
- **Python API**: Easy-to-use API for generating scenes and launching the visualizer directly from your code.
|
|
31
|
+
- **JSON Support**: Load and save simulation data using a portable JSON format.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
The easiest way to get started is to run the provided example script:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python example.py
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This script demonstrates how to use the Python API to create a simulation with wavy terrain, dynamic bodies, and time-series data.
|
|
44
|
+
|
|
45
|
+
The Python authoring API (`simview.scene`, `simview.state`, `simview.model`) depends on
|
|
46
|
+
`torch` and `einops`. Install them with the optional `authoring` extra shown below.
|
|
47
|
+
Only these are needed to *build* simulations; *viewing* an existing JSON file does not
|
|
48
|
+
require `torch`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
Requires **Python 3.12+**.
|
|
55
|
+
|
|
56
|
+
### From PyPI
|
|
57
|
+
|
|
58
|
+
To only view existing simulation JSON files:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install simview
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
To also author simulations from Python (installs `torch` and `einops`):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install "simview[authoring]"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### From source
|
|
71
|
+
|
|
72
|
+
For working on SimView itself (or to track an unreleased commit), install an
|
|
73
|
+
editable checkout instead:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install -e . # viewing only
|
|
77
|
+
pip install -e ".[authoring]" # viewing + authoring
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For independent use of this repository, use `venv` or `uv`:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
python -m venv .venv
|
|
84
|
+
source .venv/bin/activate
|
|
85
|
+
pip install -e ".[authoring]"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
uv sync --extra authoring
|
|
90
|
+
source .venv/bin/activate
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
SimView is distributed under the [BSD 3-Clause License](LICENSE). The web interface
|
|
98
|
+
vendors [uPlot](https://github.com/leeoniya/uPlot), [three.js](https://github.com/mrdoob/three.js),
|
|
99
|
+
and [chroma-js](https://github.com/gka/chroma.js) (all MIT licensed) under
|
|
100
|
+
`simview/static/lib/` so the viewer works fully offline — see the
|
|
101
|
+
[documentation](https://vlk-jan.github.io/simview/dev/architecture/#vendored-third-party-libraries)
|
|
102
|
+
for details. All third-party libraries used by SimView are permissively licensed
|
|
103
|
+
(MIT/BSD), so there are no licensing restrictions on commercial use.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simview"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.5"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -34,7 +34,7 @@ dependencies = [
|
|
|
34
34
|
]
|
|
35
35
|
|
|
36
36
|
[project.urls]
|
|
37
|
-
Homepage = "https://
|
|
37
|
+
Homepage = "https://vlk-jan.github.io/simview/"
|
|
38
38
|
Repository = "https://github.com/vlk-jan/simview"
|
|
39
39
|
Issues = "https://github.com/vlk-jan/simview/issues"
|
|
40
40
|
|
|
@@ -42,6 +42,10 @@ Issues = "https://github.com/vlk-jan/simview/issues"
|
|
|
42
42
|
# Needed only to author simulations via the Python API (simview.scene / state / model).
|
|
43
43
|
# Viewing an existing JSON file does not require these.
|
|
44
44
|
authoring = ["torch", "einops", "numpy"]
|
|
45
|
+
# Needed only for `simview render` (headless PNG screenshots, e.g. from a
|
|
46
|
+
# SLURM job with no display). Also requires a one-time `playwright install
|
|
47
|
+
# chromium` to fetch the browser binary itself.
|
|
48
|
+
render = ["playwright>=1.40"]
|
|
45
49
|
|
|
46
50
|
[project.scripts]
|
|
47
51
|
simview = "simview.__main__:main"
|
|
@@ -61,6 +65,7 @@ dev = [
|
|
|
61
65
|
"pyright>=1.1.411",
|
|
62
66
|
"websockets>=16.1",
|
|
63
67
|
]
|
|
68
|
+
docs = ["mkdocs>=1.6", "mkdocs-material>=9.5", "mkdocstrings[python]>=0.27"]
|
|
64
69
|
|
|
65
70
|
[tool.ruff]
|
|
66
71
|
target-version = "py312"
|
|
@@ -178,6 +178,7 @@ def run_diff(path: Path, args: argparse.Namespace) -> None:
|
|
|
178
178
|
every=args.every,
|
|
179
179
|
pos_threshold=args.pos_threshold,
|
|
180
180
|
rot_threshold_deg=args.rot_threshold_deg,
|
|
181
|
+
per_axis=args.per_axis,
|
|
181
182
|
)
|
|
182
183
|
text = diff_mod.format_diff_text(result)
|
|
183
184
|
csv_text = diff_mod.format_diff_csv(result)
|
|
@@ -193,6 +194,36 @@ def run_diff(path: Path, args: argparse.Namespace) -> None:
|
|
|
193
194
|
print(text)
|
|
194
195
|
|
|
195
196
|
|
|
197
|
+
def run_render(path: Path, args: argparse.Namespace) -> None:
|
|
198
|
+
"""Headlessly render one PNG screenshot of the scene JSON at `path`, for
|
|
199
|
+
generating figures from a display-less environment (e.g. a SLURM job)."""
|
|
200
|
+
from simview.render import render_screenshot
|
|
201
|
+
|
|
202
|
+
if args.output is None:
|
|
203
|
+
logger.error(
|
|
204
|
+
"Error: 'simview render' requires --output, e.g. 'simview render "
|
|
205
|
+
"scene.json --output frame.png'."
|
|
206
|
+
)
|
|
207
|
+
sys.exit(1)
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
render_screenshot(
|
|
211
|
+
path,
|
|
212
|
+
args.output,
|
|
213
|
+
host=args.host,
|
|
214
|
+
port=args.port,
|
|
215
|
+
view=args.view,
|
|
216
|
+
width=args.width,
|
|
217
|
+
height=args.height,
|
|
218
|
+
)
|
|
219
|
+
except ImportError as e:
|
|
220
|
+
logger.error("Error: %s", e)
|
|
221
|
+
sys.exit(1)
|
|
222
|
+
except (RuntimeError, TimeoutError) as e:
|
|
223
|
+
logger.error("Error: 'simview render' failed: %s", e)
|
|
224
|
+
sys.exit(1)
|
|
225
|
+
|
|
226
|
+
|
|
196
227
|
def save_merged(paths: list[Path], out_path: Path) -> None:
|
|
197
228
|
"""Merge `paths` (must be >= 2) and write the result to `out_path`, gzipped
|
|
198
229
|
if it ends in .gz, without starting the server."""
|
|
@@ -219,10 +250,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
219
250
|
"Path(s) to simulation JSON file(s) to visualize, 'clear' to clear "
|
|
220
251
|
"cache, 'info <file>' to print a structural summary of one scene "
|
|
221
252
|
"file, 'terrain <file>' to query terrain values at a point or over "
|
|
222
|
-
"an area,
|
|
223
|
-
"
|
|
224
|
-
"
|
|
225
|
-
"
|
|
253
|
+
"an area, 'diff <file>' to compare two batches' trajectories, or "
|
|
254
|
+
"'render <file>' to headlessly save a PNG screenshot (needs the "
|
|
255
|
+
"'render' extra). Multiple visualize-mode files are merged into "
|
|
256
|
+
"one scene, each file's batches appended as extra batches (e.g. a "
|
|
257
|
+
"real-world recording plus a simulated rerun)."
|
|
226
258
|
),
|
|
227
259
|
)
|
|
228
260
|
parser.add_argument(
|
|
@@ -345,6 +377,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
345
377
|
"orientation error exceeds this many degrees."
|
|
346
378
|
),
|
|
347
379
|
)
|
|
380
|
+
parser.add_argument(
|
|
381
|
+
"--per-axis",
|
|
382
|
+
action="store_true",
|
|
383
|
+
help=(
|
|
384
|
+
"With 'simview diff <file>', also report signed per-axis "
|
|
385
|
+
"(err_x/err_y/err_z = batch_a - batch_b) position error, matching "
|
|
386
|
+
"the browser Error Metrics panel's per-axis toggle."
|
|
387
|
+
),
|
|
388
|
+
)
|
|
348
389
|
parser.add_argument(
|
|
349
390
|
"--host",
|
|
350
391
|
type=str,
|
|
@@ -365,6 +406,37 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
365
406
|
action="store_true",
|
|
366
407
|
help="Don't automatically open a browser tab once the server starts.",
|
|
367
408
|
)
|
|
409
|
+
parser.add_argument(
|
|
410
|
+
"--output",
|
|
411
|
+
type=str,
|
|
412
|
+
default=None,
|
|
413
|
+
metavar="PATH",
|
|
414
|
+
help="With 'simview render <file>', PNG file path to save the screenshot to.",
|
|
415
|
+
)
|
|
416
|
+
parser.add_argument(
|
|
417
|
+
"--view",
|
|
418
|
+
type=str,
|
|
419
|
+
default=None,
|
|
420
|
+
metavar="HASH",
|
|
421
|
+
help=(
|
|
422
|
+
"With 'simview render <file>', a shareable view-link hash (from "
|
|
423
|
+
"the viewer's 'Copy view link' button, with or without the "
|
|
424
|
+
"leading '#') to set the camera/playback/terrain state before "
|
|
425
|
+
"capturing. Default: the viewer's startup state."
|
|
426
|
+
),
|
|
427
|
+
)
|
|
428
|
+
parser.add_argument(
|
|
429
|
+
"--width",
|
|
430
|
+
type=int,
|
|
431
|
+
default=1280,
|
|
432
|
+
help="With 'simview render <file>', screenshot width in pixels (default: 1280).",
|
|
433
|
+
)
|
|
434
|
+
parser.add_argument(
|
|
435
|
+
"--height",
|
|
436
|
+
type=int,
|
|
437
|
+
default=720,
|
|
438
|
+
help="With 'simview render <file>', screenshot height in pixels (default: 720).",
|
|
439
|
+
)
|
|
368
440
|
parser.add_argument(
|
|
369
441
|
"--save-merged",
|
|
370
442
|
type=str,
|
|
@@ -437,6 +509,21 @@ def main():
|
|
|
437
509
|
run_diff(diff_path, args)
|
|
438
510
|
return
|
|
439
511
|
|
|
512
|
+
if args.inputs and args.inputs[0] == "render":
|
|
513
|
+
render_args = args.inputs[1:]
|
|
514
|
+
if len(render_args) != 1:
|
|
515
|
+
logger.error(
|
|
516
|
+
"Error: 'simview render' requires exactly one file argument, "
|
|
517
|
+
"e.g. 'simview render scene.json --output frame.png'."
|
|
518
|
+
)
|
|
519
|
+
sys.exit(1)
|
|
520
|
+
render_path = Path(render_args[0])
|
|
521
|
+
if not (render_path.exists() and render_path.is_file()):
|
|
522
|
+
logger.error("Error: File '%s' not found or is not a file.", render_path)
|
|
523
|
+
sys.exit(1)
|
|
524
|
+
run_render(render_path, args)
|
|
525
|
+
return
|
|
526
|
+
|
|
440
527
|
if args.inputs == ["clear"]:
|
|
441
528
|
clear_cache()
|
|
442
529
|
return
|
|
@@ -180,19 +180,25 @@ def compute_trajectory_diff(
|
|
|
180
180
|
every: int = 1,
|
|
181
181
|
pos_threshold: float | None = None,
|
|
182
182
|
rot_threshold_deg: float | None = None,
|
|
183
|
+
per_axis: bool = False,
|
|
183
184
|
) -> dict:
|
|
184
185
|
"""Per-frame positional/orientation divergence between batch `batch_a`
|
|
185
186
|
and batch `batch_b`'s trajectories in `states_data`, for every body
|
|
186
187
|
present in the states (or just `body` if given).
|
|
187
188
|
|
|
188
189
|
Returns a JSON-serializable dict: `{"batch_a", "batch_b", "every",
|
|
189
|
-
"pos_threshold", "rot_threshold_deg", "bodies": {label:
|
|
190
|
-
"times", "position_error", "orientation_error_deg",
|
|
191
|
-
"frame_count", "position_error": {"mean","max","final"},
|
|
190
|
+
"pos_threshold", "rot_threshold_deg", "per_axis", "bodies": {label:
|
|
191
|
+
{"frame_indices", "times", "position_error", "orientation_error_deg",
|
|
192
|
+
"summary": {"frame_count", "position_error": {"mean","max","final"},
|
|
192
193
|
"orientation_error_deg": {...}, "first_frame_exceeding_pos_threshold",
|
|
193
|
-
"first_frame_exceeding_rot_threshold"}}}}`.
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
"first_frame_exceeding_rot_threshold"}}}}`. When `per_axis` is set, each
|
|
195
|
+
body also gets `"err_x"`/`"err_y"`/`"err_z"` per-frame series (signed
|
|
196
|
+
`batch_a - batch_b`, matching the browser Error Metrics panel's
|
|
197
|
+
per-axis toggle -- see `static/js/utils/errorMath.js`'s
|
|
198
|
+
`positionAxisError`) and matching `"err_x"`/`"err_y"`/`"err_z"` entries
|
|
199
|
+
in `summary` (mean/max/final of the *signed* value, so directional bias
|
|
200
|
+
is visible). Raises `ValueError` on invalid batch indices, `every < 1`,
|
|
201
|
+
an unmatched/ambiguous `body`, or a scene with no diffable bodies.
|
|
196
202
|
"""
|
|
197
203
|
batch_size = _resolve_batches(model_data, batch_a, batch_b)
|
|
198
204
|
if every < 1:
|
|
@@ -223,6 +229,9 @@ def compute_trajectory_diff(
|
|
|
223
229
|
times: list = []
|
|
224
230
|
position_error: list[float] = []
|
|
225
231
|
orientation_error_deg: list[float] = []
|
|
232
|
+
err_x: list[float] = []
|
|
233
|
+
err_y: list[float] = []
|
|
234
|
+
err_z: list[float] = []
|
|
226
235
|
|
|
227
236
|
for idx, state in enumerate(states_data):
|
|
228
237
|
if idx % every != 0:
|
|
@@ -245,24 +254,36 @@ def compute_trajectory_diff(
|
|
|
245
254
|
times.append(state.get("time"))
|
|
246
255
|
position_error.append(math.dist(row_a[:3], row_b[:3]))
|
|
247
256
|
orientation_error_deg.append(_quat_angle_deg(row_a[3:], row_b[3:]))
|
|
248
|
-
|
|
257
|
+
if per_axis:
|
|
258
|
+
err_x.append(row_a[0] - row_b[0])
|
|
259
|
+
err_y.append(row_a[1] - row_b[1])
|
|
260
|
+
err_z.append(row_a[2] - row_b[2])
|
|
261
|
+
|
|
262
|
+
summary = {
|
|
263
|
+
"frame_count": len(frame_indices),
|
|
264
|
+
"position_error": _stats(position_error),
|
|
265
|
+
"orientation_error_deg": _stats(orientation_error_deg),
|
|
266
|
+
"first_frame_exceeding_pos_threshold": _first_exceeding(
|
|
267
|
+
frame_indices, position_error, pos_threshold
|
|
268
|
+
),
|
|
269
|
+
"first_frame_exceeding_rot_threshold": _first_exceeding(
|
|
270
|
+
frame_indices, orientation_error_deg, rot_threshold_deg
|
|
271
|
+
),
|
|
272
|
+
}
|
|
249
273
|
bodies_out[label] = {
|
|
250
274
|
"frame_indices": frame_indices,
|
|
251
275
|
"times": times,
|
|
252
276
|
"position_error": position_error,
|
|
253
277
|
"orientation_error_deg": orientation_error_deg,
|
|
254
|
-
"summary":
|
|
255
|
-
"frame_count": len(frame_indices),
|
|
256
|
-
"position_error": _stats(position_error),
|
|
257
|
-
"orientation_error_deg": _stats(orientation_error_deg),
|
|
258
|
-
"first_frame_exceeding_pos_threshold": _first_exceeding(
|
|
259
|
-
frame_indices, position_error, pos_threshold
|
|
260
|
-
),
|
|
261
|
-
"first_frame_exceeding_rot_threshold": _first_exceeding(
|
|
262
|
-
frame_indices, orientation_error_deg, rot_threshold_deg
|
|
263
|
-
),
|
|
264
|
-
},
|
|
278
|
+
"summary": summary,
|
|
265
279
|
}
|
|
280
|
+
if per_axis:
|
|
281
|
+
bodies_out[label]["err_x"] = err_x
|
|
282
|
+
bodies_out[label]["err_y"] = err_y
|
|
283
|
+
bodies_out[label]["err_z"] = err_z
|
|
284
|
+
summary["err_x"] = _stats(err_x)
|
|
285
|
+
summary["err_y"] = _stats(err_y)
|
|
286
|
+
summary["err_z"] = _stats(err_z)
|
|
266
287
|
|
|
267
288
|
return {
|
|
268
289
|
"batch_a": batch_a,
|
|
@@ -270,6 +291,7 @@ def compute_trajectory_diff(
|
|
|
270
291
|
"every": every,
|
|
271
292
|
"pos_threshold": pos_threshold,
|
|
272
293
|
"rot_threshold_deg": rot_threshold_deg,
|
|
294
|
+
"per_axis": per_axis,
|
|
273
295
|
"bodies": bodies_out,
|
|
274
296
|
}
|
|
275
297
|
|
|
@@ -306,6 +328,13 @@ def format_diff_text(result: dict) -> str:
|
|
|
306
328
|
f" rot_err (deg): mean={rot['mean']:.6g} max={rot['max']:.6g} "
|
|
307
329
|
f"final={rot['final']:.6g}"
|
|
308
330
|
)
|
|
331
|
+
if result.get("per_axis"):
|
|
332
|
+
for axis in ("err_x", "err_y", "err_z"):
|
|
333
|
+
a = summary[axis]
|
|
334
|
+
lines.append(
|
|
335
|
+
f" {axis} (m): mean={a['mean']:.6g} max={a['max']:.6g} "
|
|
336
|
+
f"final={a['final']:.6g}"
|
|
337
|
+
)
|
|
309
338
|
if summary["first_frame_exceeding_pos_threshold"] is not None:
|
|
310
339
|
lines.append(
|
|
311
340
|
" first frame exceeding pos_threshold: "
|
|
@@ -344,17 +373,22 @@ def format_diff_csv(result: dict) -> str:
|
|
|
344
373
|
(body, frame), full series (no truncation) -- CSV output is for scripts,
|
|
345
374
|
matching the "just the numbers, in full" philosophy `--json` already
|
|
346
375
|
uses everywhere in this CLI."""
|
|
376
|
+
per_axis = result.get("per_axis")
|
|
347
377
|
buf = io.StringIO()
|
|
348
378
|
writer = csv.writer(buf)
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
379
|
+
header = ["body", "frame", "time", "position_error", "orientation_error_deg"]
|
|
380
|
+
if per_axis:
|
|
381
|
+
header += ["err_x", "err_y", "err_z"]
|
|
382
|
+
writer.writerow(header)
|
|
352
383
|
for label, body in result["bodies"].items():
|
|
353
|
-
|
|
384
|
+
columns = [
|
|
354
385
|
body["frame_indices"],
|
|
355
386
|
body["times"],
|
|
356
387
|
body["position_error"],
|
|
357
388
|
body["orientation_error_deg"],
|
|
358
|
-
|
|
359
|
-
|
|
389
|
+
]
|
|
390
|
+
if per_axis:
|
|
391
|
+
columns += [body["err_x"], body["err_y"], body["err_z"]]
|
|
392
|
+
for row in zip(*columns):
|
|
393
|
+
writer.writerow([label, *row])
|
|
360
394
|
return buf.getvalue()
|