From a68a11619651eebbd2a1d3a4210492cf567bb1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Mon, 5 Oct 2026 21:22:19 +0200 Subject: [PATCH] feat(python): expose the deterministic mode on NexusViewer and NexusState --- CHANGELOG.md | 8 ++++++++ crates/nexus_python3d/src/nexus.rs | 19 +++++++++++++++++++ crates/nexus_python3d/src/viewer.rs | 11 +++++++++++ 3 files changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d636285..e197875c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## Unreleased + +### Added + +- Python: `NexusViewer.set_deterministic` / `deterministic` (applied to the state by `sync`), + and `NexusState.set_deterministic`, `deterministic` and `steps` for states stepped without a + viewer sync. + ## v0.6.0 (4 October 2026) ### Breaking changes diff --git a/crates/nexus_python3d/src/nexus.rs b/crates/nexus_python3d/src/nexus.rs index 1361d8cb..18805e32 100644 --- a/crates/nexus_python3d/src/nexus.rs +++ b/crates/nexus_python3d/src/nexus.rs @@ -1240,6 +1240,25 @@ impl NexusState { self.0.set_rbd_implicit_coriolis(viewer.backend(), enabled); } + /// Same-machine determinism: two runs of the same scene with the same + /// inputs give bit-identical results on the same machine and build. Off by + /// default. Can be called before or after `finalize`. + /// `NexusViewer.sync` overrides it with the viewer's own setting, so a state + /// synced through a viewer is configured with `NexusViewer.set_deterministic`. + fn set_deterministic(&mut self, viewer: PyRef, enabled: bool) { + self.0.set_deterministic(viewer.backend(), enabled); + } + + /// Whether the simulation runs in deterministic mode (see `set_deterministic`). + fn deterministic(&self) -> bool { + self.0.deterministic() + } + + /// Number of `NexusPipeline.simulate` calls since the scene was created. + fn steps(&self) -> u64 { + self.0.steps() + } + /// Multibody refresh cadence. `refresh` (default `True`) rebuilds the /// robots' constraints, mass matrix and LU factors every substep; `False` /// builds them once per step and later substeps only refresh the joint diff --git a/crates/nexus_python3d/src/viewer.rs b/crates/nexus_python3d/src/viewer.rs index 8468c6bb..2a2232f7 100644 --- a/crates/nexus_python3d/src/viewer.rs +++ b/crates/nexus_python3d/src/viewer.rs @@ -97,6 +97,17 @@ impl NexusViewer { self.inner_mut().set_vsync(enabled); } + /// Same-machine determinism of the states this viewer syncs: `sync` applies + /// it to the state (see `NexusState.set_deterministic`). Off by default. + fn set_deterministic(&mut self, enabled: bool) { + self.inner_mut().ui.sim_settings.deterministic = enabled; + } + + /// Whether `sync` puts the states it syncs in deterministic mode. + fn deterministic(&self) -> bool { + self.inner().ui.sim_settings.deterministic + } + // --- backend selection (fluent) -------------------------------------- fn with_cpu(mut slf: PyRefMut) -> PyRefMut {