simview 6.1.0__tar.gz → 6.1.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-6.1.0/simview.egg-info → simview-6.1.2}/PKG-INFO +1 -1
- {simview-6.1.0 → simview-6.1.2}/pyproject.toml +1 -1
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/SimView.js +14 -2
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/components/AnimationController.js +4 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/components/BatchManager.js +14 -6
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/Controls.js +15 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/ScalarPlotter.js +21 -41
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/chartControls.js +15 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/viewState.js +29 -5
- {simview-6.1.0 → simview-6.1.2}/simview/view.py +17 -0
- {simview-6.1.0 → simview-6.1.2/simview.egg-info}/PKG-INFO +1 -1
- {simview-6.1.0 → simview-6.1.2}/tests/test_view.py +7 -0
- {simview-6.1.0 → simview-6.1.2}/LICENSE +0 -0
- {simview-6.1.0 → simview-6.1.2}/MANIFEST.in +0 -0
- {simview-6.1.0 → simview-6.1.2}/README.md +0 -0
- {simview-6.1.0 → simview-6.1.2}/setup.cfg +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/__init__.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/__main__.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/columnar.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/diff.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/info.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/launcher.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/live.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/merge.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/model.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/py.typed +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/remote.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/render.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/scene.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/server.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/state.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/css/controls.css +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/components/InteractionController.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/components/Scene.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/components/StateStore.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/components/WindowedField.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/config.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/main.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/objects/Body.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/objects/StaticObject.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/objects/Terrain.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/objects/colormap.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/objects/similarity.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/objects/utils.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/AnalysisPanel.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/BatchLegend.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/BodyStateWindow.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/ErrorMetrics.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/Legend.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/PlaybackControls.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/ui/TerrainProfile.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/batchPresets.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/batchVisibility.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/blobCodec.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/blobWindow.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/bodyTransforms.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/cameraRange.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/csv.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/episodes.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/errorMath.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/interpolate.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/keyboard.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/liveFollow.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/terrainSample.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/uplot.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/js/utils/viewerDefaults.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/lib/js-colormaps.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/lib/three-0.174.0/addons/controls/OrbitControls.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/lib/three-0.174.0/addons/libs/lil-gui.module.min.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/lib/three-0.174.0/three.core.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/lib/three-0.174.0/three.module.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/lib/uPlot.esm.js +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/lib/uPlot.min.css +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/textures/contacts/red-cross0.png +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/static/textures/points/ball1.png +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/templates/index.html +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/terrain.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview/utils.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview.egg-info/SOURCES.txt +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview.egg-info/dependency_links.txt +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview.egg-info/entry_points.txt +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview.egg-info/requires.txt +0 -0
- {simview-6.1.0 → simview-6.1.2}/simview.egg-info/top_level.txt +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_batch_selection.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_cli.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_columnar_ondisk.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_columnar_states.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_contacts.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_diff.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_embed_defaults.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_episodes.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_info.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_launcher.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_live.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_merge.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_pointcloud_features.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_remote.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_roundtrip.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_scene.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_server.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_show.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_terrain.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_terrain_query.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_trajectory.py +0 -0
- {simview-6.1.0 → simview-6.1.2}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simview
|
|
3
|
-
Version: 6.1.
|
|
3
|
+
Version: 6.1.2
|
|
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
|
|
@@ -602,6 +602,9 @@ export class SimView {
|
|
|
602
602
|
// actually flips body/terrain visuals) stay in sync instead of
|
|
603
603
|
// uiState silently drifting out from under the displayed panel.
|
|
604
604
|
if (this.uiControls) {
|
|
605
|
+
if (Number.isFinite(state.playbackSpeed) && this.animationController) {
|
|
606
|
+
this.animationController.setSpeed(state.playbackSpeed);
|
|
607
|
+
}
|
|
605
608
|
this.uiControls.applyViewState(state);
|
|
606
609
|
}
|
|
607
610
|
} catch (e) {
|
|
@@ -623,6 +626,10 @@ export class SimView {
|
|
|
623
626
|
bodyVisualizationMode: this.uiState.bodyVisualizationMode,
|
|
624
627
|
terrainColorMode: this.uiState.terrainColorMode,
|
|
625
628
|
toggles: toggleMapFromUiState(this.uiState),
|
|
629
|
+
pointCloudsVisible: this.uiState.pointCloudsVisible,
|
|
630
|
+
trackBody: this.uiState.trackBody,
|
|
631
|
+
terrainColorMap: this.uiState.terrainColorMap,
|
|
632
|
+
playbackSpeed: this.animationController ? this.animationController.playbackSpeed : undefined,
|
|
626
633
|
};
|
|
627
634
|
}
|
|
628
635
|
|
|
@@ -750,8 +757,11 @@ export class SimView {
|
|
|
750
757
|
const currentBodyPos = new THREE.Vector3(pos.x + offset.x, pos.y + offset.y, pos.z + offset.z);
|
|
751
758
|
|
|
752
759
|
try {
|
|
753
|
-
if (
|
|
754
|
-
|
|
760
|
+
if (
|
|
761
|
+
this._lastTrackedBody !== this.uiState.trackBody ||
|
|
762
|
+
this._lastTrackedBatch !== activeBatch
|
|
763
|
+
) {
|
|
764
|
+
// On first tracking or a body/batch switch, center the target on the body, preserving the viewing angle
|
|
755
765
|
const delta = currentBodyPos.clone().sub(this.scene.controls.target);
|
|
756
766
|
this.scene.camera.position.add(delta);
|
|
757
767
|
this.scene.controls.target.copy(currentBodyPos);
|
|
@@ -772,11 +782,13 @@ export class SimView {
|
|
|
772
782
|
}
|
|
773
783
|
|
|
774
784
|
this._lastTrackedBody = this.uiState.trackBody;
|
|
785
|
+
this._lastTrackedBatch = activeBatch;
|
|
775
786
|
this._lastTrackedPosition = currentBodyPos.clone();
|
|
776
787
|
}
|
|
777
788
|
}
|
|
778
789
|
} else {
|
|
779
790
|
this._lastTrackedBody = null;
|
|
791
|
+
this._lastTrackedBatch = null;
|
|
780
792
|
this._lastTrackedPosition = null;
|
|
781
793
|
}
|
|
782
794
|
this.scene.animate(now);
|
|
@@ -126,6 +126,10 @@ export class AnimationController {
|
|
|
126
126
|
|
|
127
127
|
setSpeed(speed) {
|
|
128
128
|
this.playbackSpeed = speed;
|
|
129
|
+
// Keep the playback bar's dropdown in step with programmatic callers.
|
|
130
|
+
if (this.playbackControls && this.playbackControls.speedSelect) {
|
|
131
|
+
this.playbackControls.speedSelect.value = String(speed);
|
|
132
|
+
}
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
setRecordingFormat(format) {
|
|
@@ -152,12 +152,19 @@ export class BatchManager {
|
|
|
152
152
|
return { x: 0, y: 0, z: 0 };
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
// Moves camera and target by the offset between the two batches' grid
|
|
156
|
+
// cells, so the view keeps its place relative to the scene. Snapping the
|
|
157
|
+
// target to the cell origin instead lost the view on every chart click
|
|
158
|
+
// (which re-selects the closest batch) and broke body tracking.
|
|
159
|
+
changeFocusOnBatchByIndex(batchIndex, fromBatchIndex) {
|
|
156
160
|
const { camera, controls } = this.app.scene;
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
+
const to = this.getBatchOffset(batchIndex);
|
|
162
|
+
const from = this.getBatchOffset(fromBatchIndex);
|
|
163
|
+
const shift = new THREE.Vector3(to.x - from.x, to.y - from.y, to.z - from.z);
|
|
164
|
+
if (shift.lengthSq() === 0) return;
|
|
165
|
+
camera.position.add(shift);
|
|
166
|
+
controls.target.add(shift);
|
|
167
|
+
controls.update();
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
setActiveBatch(batchIndex) {
|
|
@@ -167,8 +174,9 @@ export class BatchManager {
|
|
|
167
174
|
console.warn("Invalid batch index:", batchIndex);
|
|
168
175
|
return;
|
|
169
176
|
}
|
|
177
|
+
const previousBatch = this.currentlyActiveBatch;
|
|
170
178
|
this.currentlyActiveBatch = batchIndex;
|
|
171
|
-
this.changeFocusOnBatchByIndex(batchIndex);
|
|
179
|
+
this.changeFocusOnBatchByIndex(batchIndex, previousBatch);
|
|
172
180
|
// In focused mode the newly-active batch is (probably) not the one
|
|
173
181
|
// currently built/drawn, so refresh before the panels read from it.
|
|
174
182
|
this._recomputeVisibleBatches();
|
|
@@ -607,6 +607,21 @@ export class UIControls {
|
|
|
607
607
|
if (ctrl) ctrl.setValue(state.terrainColorMode);
|
|
608
608
|
}
|
|
609
609
|
|
|
610
|
+
if (typeof state.terrainColorMap === "string") {
|
|
611
|
+
const known = Array.isArray(colorMapOptions) ? colorMapOptions : Object.keys(colorMapOptions);
|
|
612
|
+
const ctrl = this.findController("colorMap");
|
|
613
|
+
if (ctrl && known.includes(state.terrainColorMap)) ctrl.setValue(state.terrainColorMap);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (typeof state.pointCloudsVisible === "boolean") {
|
|
617
|
+
const ctrl = this.findController("showPointClouds");
|
|
618
|
+
if (ctrl) ctrl.setValue(state.pointCloudsVisible);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (typeof state.trackBody === "string" && this.trackBodyCtrl) {
|
|
622
|
+
if (this.trackableBodyNames().includes(state.trackBody)) this.trackBodyCtrl.setValue(state.trackBody);
|
|
623
|
+
}
|
|
624
|
+
|
|
610
625
|
if (state.toggles && typeof state.toggles === "object") {
|
|
611
626
|
const propertyForKey = {
|
|
612
627
|
axesVisible: "showAxes",
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
normalizeEpisodes,
|
|
8
8
|
} from "../utils/episodes.js";
|
|
9
9
|
import { makeChart, yIncrements } from "../utils/uplot.js";
|
|
10
|
-
import { batchColumnsCsv, closestSeries, exportBar } from "./chartControls.js";
|
|
10
|
+
import { batchColumnsCsv, closestSeries, exportBar, finiteBounds } from "./chartControls.js";
|
|
11
11
|
|
|
12
12
|
export class ScalarPlotter {
|
|
13
13
|
constructor(app, scalarNames) {
|
|
@@ -35,7 +35,7 @@ export class ScalarPlotter {
|
|
|
35
35
|
// {scalarName -> per-batch array of per-episode aggregates}, rebuilt
|
|
36
36
|
// only when the episodes or the underlying series change.
|
|
37
37
|
this.episodeAggregateCache = new Map();
|
|
38
|
-
this.
|
|
38
|
+
this.markerDirty = false;
|
|
39
39
|
this.opacityRenderCallback = null;
|
|
40
40
|
this.minRenderDelay = 1000 / FREQ_CONFIG.scalarPlotter;
|
|
41
41
|
this.lastRenderTime = Number.NEGATIVE_INFINITY;
|
|
@@ -140,16 +140,7 @@ export class ScalarPlotter {
|
|
|
140
140
|
// scalar as one whole-trajectory Float32Array.
|
|
141
141
|
const series = store.getScalarSeries(scalarName, batchSize);
|
|
142
142
|
this.scalarSeries.set(scalarName, series);
|
|
143
|
-
|
|
144
|
-
let min = Number.MAX_VALUE;
|
|
145
|
-
let max = Number.MIN_VALUE;
|
|
146
|
-
for (const batchSeries of series) {
|
|
147
|
-
for (const { y } of batchSeries) {
|
|
148
|
-
min = Math.min(min, y);
|
|
149
|
-
max = Math.max(max, y);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
this.scalarBounds.set(scalarName, [min, max]);
|
|
143
|
+
this.scalarBounds.set(scalarName, finiteBounds(series));
|
|
153
144
|
}
|
|
154
145
|
|
|
155
146
|
this._initializePlots();
|
|
@@ -314,6 +305,7 @@ export class ScalarPlotter {
|
|
|
314
305
|
},
|
|
315
306
|
],
|
|
316
307
|
tooltip: (u, idx) => this._tooltipHtml(u, idx, name),
|
|
308
|
+
markerTime: () => this.times[this.currentEndIndex] ?? null,
|
|
317
309
|
hooks: {
|
|
318
310
|
draw: [
|
|
319
311
|
(u) => {
|
|
@@ -331,7 +323,16 @@ export class ScalarPlotter {
|
|
|
331
323
|
}
|
|
332
324
|
},
|
|
333
325
|
},
|
|
334
|
-
|
|
326
|
+
// The whole timeline, plotted once; playback only moves the
|
|
327
|
+
// marker. NaN (a gap in the source) becomes null, uPlot's gap.
|
|
328
|
+
[
|
|
329
|
+
this.times,
|
|
330
|
+
...this.scalarSeries
|
|
331
|
+
.get(name)
|
|
332
|
+
.map((batchSeries) =>
|
|
333
|
+
batchSeries.map(({ y }) => (Number.isFinite(y) ? y : null))
|
|
334
|
+
),
|
|
335
|
+
],
|
|
335
336
|
this.app
|
|
336
337
|
);
|
|
337
338
|
|
|
@@ -354,6 +355,7 @@ export class ScalarPlotter {
|
|
|
354
355
|
}
|
|
355
356
|
|
|
356
357
|
setEndIndex(newEndIndex, force = false) {
|
|
358
|
+
if (this.times.length === 0) return; // store not loaded yet (initFromStore pending)
|
|
357
359
|
if (newEndIndex < 0 || newEndIndex >= this.times.length) {
|
|
358
360
|
console.warn(
|
|
359
361
|
"Invalid end index. Must be within the range of time values."
|
|
@@ -364,27 +366,7 @@ export class ScalarPlotter {
|
|
|
364
366
|
return;
|
|
365
367
|
}
|
|
366
368
|
this.currentEndIndex = newEndIndex;
|
|
367
|
-
|
|
368
|
-
if (!activeChart) {
|
|
369
|
-
console.warn(`No chart found for scalar "${this.activeScalar}".`);
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const scalarData = this.scalarSeries.get(this.activeScalar);
|
|
374
|
-
if (!scalarData) {
|
|
375
|
-
console.warn(`No data points found for scalar "${this.activeScalar}".`);
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
this.seriesRenderCallback = () => {
|
|
380
|
-
const numPoints = this.currentEndIndex + 1;
|
|
381
|
-
const xValues = this.times.slice(0, numPoints);
|
|
382
|
-
const data = [xValues];
|
|
383
|
-
for (let i = 0; i < this.app.batchManager.simBatches; i++) {
|
|
384
|
-
data.push(scalarData[i].slice(0, numPoints).map((p) => p.y));
|
|
385
|
-
}
|
|
386
|
-
activeChart.setData(data, false);
|
|
387
|
-
};
|
|
369
|
+
this.markerDirty = true;
|
|
388
370
|
}
|
|
389
371
|
|
|
390
372
|
setFocusedBatch(batchIndex, force = false) {
|
|
@@ -436,9 +418,8 @@ export class ScalarPlotter {
|
|
|
436
418
|
}
|
|
437
419
|
|
|
438
420
|
var needRender = false;
|
|
439
|
-
if (this.
|
|
440
|
-
this.
|
|
441
|
-
this.seriesRenderCallback = null;
|
|
421
|
+
if (this.markerDirty) {
|
|
422
|
+
this.markerDirty = false;
|
|
442
423
|
needRender = true;
|
|
443
424
|
}
|
|
444
425
|
if (this.opacityRenderCallback) {
|
|
@@ -448,10 +429,9 @@ export class ScalarPlotter {
|
|
|
448
429
|
}
|
|
449
430
|
|
|
450
431
|
if (!needRender) return;
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
activeChart.redraw(true, true);
|
|
432
|
+
// The data never changes after init, so the cached paths stay valid;
|
|
433
|
+
// strokes (focus opacity) are re-read on every draw anyway.
|
|
434
|
+
activeChart.redraw(false);
|
|
455
435
|
}
|
|
456
436
|
|
|
457
437
|
animate(now) {
|
|
@@ -20,6 +20,21 @@ export function closestSeries(u, dataIdx, yVal) {
|
|
|
20
20
|
return best;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// [min, max] over the finite y of every batch's {x, y} points; [0, 1] when
|
|
24
|
+
// there are none, so a chart never gets NaN/Infinity bounds.
|
|
25
|
+
export function finiteBounds(seriesPerBatch) {
|
|
26
|
+
let min = Infinity;
|
|
27
|
+
let max = -Infinity;
|
|
28
|
+
for (const batchSeries of seriesPerBatch) {
|
|
29
|
+
for (const { y } of batchSeries) {
|
|
30
|
+
if (!Number.isFinite(y)) continue;
|
|
31
|
+
if (y < min) min = y;
|
|
32
|
+
if (y > max) max = y;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return min <= max ? [min, max] : [0, 1];
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
// CSV with a time column, then one column per batch (named after the batch's
|
|
24
39
|
// display name); `seriesPerBatch[b][i]` is an {x, y} point or missing.
|
|
25
40
|
export function batchColumnsCsv(batchManager, times, seriesPerBatch) {
|
|
@@ -30,11 +30,9 @@ const BOOLEAN_FLAG_KEYS = [
|
|
|
30
30
|
"terrainVisualizationModes.normals",
|
|
31
31
|
];
|
|
32
32
|
|
|
33
|
-
// Deliberately NOT in the list above: `pointCloudsVisible`.
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// switched off and hide point clouds on every old link. Adding it needs a
|
|
37
|
-
// format version that can tell "absent" from "false".
|
|
33
|
+
// Deliberately NOT in the list above: `pointCloudsVisible`. It defaults on,
|
|
34
|
+
// so a bit would decode old links (bit absent) as off; it travels as the
|
|
35
|
+
// named `pc=0|1` key instead, where absent means "leave alone".
|
|
38
36
|
|
|
39
37
|
function getPath(obj, path) {
|
|
40
38
|
return path.split(".").reduce((o, k) => (o && typeof o === "object" ? o[k] : undefined), obj);
|
|
@@ -87,6 +85,11 @@ function decodeFlags(mask) {
|
|
|
87
85
|
// bodyVisualizationMode: string,
|
|
88
86
|
// terrainColorMode: string,
|
|
89
87
|
// toggles: { [key in BOOLEAN_FLAG_KEYS]?: boolean },
|
|
88
|
+
// pointCloudsVisible: boolean, // `pc=0|1` -- a named key, not a flag bit,
|
|
89
|
+
// // so "absent" (leave alone) != false
|
|
90
|
+
// trackBody: string, // `track=`
|
|
91
|
+
// terrainColorMap: string, // `cmap=`
|
|
92
|
+
// playbackSpeed: number, // `speed=`
|
|
90
93
|
// }
|
|
91
94
|
export function serializeViewState(state) {
|
|
92
95
|
if (!state || typeof state !== "object") return "";
|
|
@@ -116,6 +119,18 @@ export function serializeViewState(state) {
|
|
|
116
119
|
if (state.toggles && typeof state.toggles === "object") {
|
|
117
120
|
params.push(`flags=${encodeFlags(state.toggles)}`);
|
|
118
121
|
}
|
|
122
|
+
if (typeof state.pointCloudsVisible === "boolean") {
|
|
123
|
+
params.push(`pc=${state.pointCloudsVisible ? 1 : 0}`);
|
|
124
|
+
}
|
|
125
|
+
if (typeof state.trackBody === "string" && state.trackBody) {
|
|
126
|
+
params.push(`track=${encodeURIComponent(state.trackBody)}`);
|
|
127
|
+
}
|
|
128
|
+
if (typeof state.terrainColorMap === "string" && state.terrainColorMap) {
|
|
129
|
+
params.push(`cmap=${encodeURIComponent(state.terrainColorMap)}`);
|
|
130
|
+
}
|
|
131
|
+
if (Number.isFinite(state.playbackSpeed) && state.playbackSpeed > 0) {
|
|
132
|
+
params.push(`speed=${fmtNum(state.playbackSpeed)}`);
|
|
133
|
+
}
|
|
119
134
|
|
|
120
135
|
return `#${params.join("&")}`;
|
|
121
136
|
}
|
|
@@ -155,6 +170,15 @@ export function parseViewState(hash) {
|
|
|
155
170
|
const mask = parseInt(params.get("flags"), 10);
|
|
156
171
|
if (Number.isInteger(mask)) state.toggles = decodeFlags(mask);
|
|
157
172
|
|
|
173
|
+
const pc = params.get("pc");
|
|
174
|
+
if (pc === "0" || pc === "1") state.pointCloudsVisible = pc === "1";
|
|
175
|
+
const track = params.get("track");
|
|
176
|
+
if (track) state.trackBody = track;
|
|
177
|
+
const cmap = params.get("cmap");
|
|
178
|
+
if (cmap) state.terrainColorMap = cmap;
|
|
179
|
+
const speed = num("speed");
|
|
180
|
+
if (Number.isFinite(speed) && speed > 0) state.playbackSpeed = speed;
|
|
181
|
+
|
|
158
182
|
return state;
|
|
159
183
|
}
|
|
160
184
|
|
|
@@ -47,6 +47,10 @@ def view_hash(
|
|
|
47
47
|
body_mode: str | None = None,
|
|
48
48
|
terrain_color_mode: str | None = None,
|
|
49
49
|
flags: Mapping[str, bool] | None = None,
|
|
50
|
+
point_clouds: bool | None = None,
|
|
51
|
+
track: str | None = None,
|
|
52
|
+
color_map: str | None = None,
|
|
53
|
+
speed: float | None = None,
|
|
50
54
|
data: str | None = None,
|
|
51
55
|
ui: bool | None = None,
|
|
52
56
|
) -> str:
|
|
@@ -55,6 +59,9 @@ def view_hash(
|
|
|
55
59
|
`t` is the playback time in seconds, `cam`/`tgt` the camera position and
|
|
56
60
|
orbit target, `batch` the focused batch index, `flags` a map of the dotted
|
|
57
61
|
toggle names in `BOOLEAN_FLAG_KEYS` (unlisted ones default to off).
|
|
62
|
+
`point_clouds`, `track` (a body name, or ``"None"``), `color_map` and
|
|
63
|
+
`speed` are carried as named keys; leaving one ``None`` leaves the
|
|
64
|
+
viewer's current setting alone.
|
|
58
65
|
`data` (base URL of a static bundle, see `SimulationScene.save_static`) and
|
|
59
66
|
`ui=False` (hide all panels) are startup options, not view state.
|
|
60
67
|
"""
|
|
@@ -81,6 +88,16 @@ def view_hash(
|
|
|
81
88
|
)
|
|
82
89
|
mask = sum(1 << i for i, k in enumerate(BOOLEAN_FLAG_KEYS) if flags.get(k))
|
|
83
90
|
params.append(f"flags={mask}")
|
|
91
|
+
if point_clouds is not None:
|
|
92
|
+
params.append(f"pc={int(bool(point_clouds))}")
|
|
93
|
+
if track:
|
|
94
|
+
params.append(f"track={quote(track, safe='')}")
|
|
95
|
+
if color_map:
|
|
96
|
+
params.append(f"cmap={quote(color_map, safe='')}")
|
|
97
|
+
if speed is not None:
|
|
98
|
+
if speed <= 0:
|
|
99
|
+
raise ValueError(f"speed must be positive; got {speed!r}")
|
|
100
|
+
params.append(f"speed={_num(speed)}")
|
|
84
101
|
if params:
|
|
85
102
|
params.insert(0, "v=1")
|
|
86
103
|
if data is not None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simview
|
|
3
|
-
Version: 6.1.
|
|
3
|
+
Version: 6.1.2
|
|
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
|
|
@@ -35,6 +35,13 @@ def test_view_hash():
|
|
|
35
35
|
assert view_hash(ui=False, data="http://h/x") == "#data=http%3A%2F%2Fh%2Fx&ui=0"
|
|
36
36
|
with pytest.raises(ValueError):
|
|
37
37
|
view_hash(flags={"nope": True})
|
|
38
|
+
# Named carry-over keys, in the JS serializer's order; absent means "leave alone".
|
|
39
|
+
assert (
|
|
40
|
+
view_hash(point_clouds=False, track="robot 1", color_map="viridis", speed=0.25)
|
|
41
|
+
== "#v=1&pc=0&track=robot%201&cmap=viridis&speed=0.25"
|
|
42
|
+
)
|
|
43
|
+
with pytest.raises(ValueError):
|
|
44
|
+
view_hash(speed=0)
|
|
38
45
|
|
|
39
46
|
|
|
40
47
|
def _urls(o):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simview-6.1.0 → simview-6.1.2}/simview/static/lib/three-0.174.0/addons/controls/OrbitControls.js
RENAMED
|
File without changes
|
{simview-6.1.0 → simview-6.1.2}/simview/static/lib/three-0.174.0/addons/libs/lil-gui.module.min.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|