Skip to main content

alice_physics/
lib.rs

1//! ALICE-Physics: Deterministic 128-bit Fixed-Point Physics Engine
2//!
3//! A high-precision physics engine designed for deterministic simulation
4//! across different platforms and hardware. Uses 128-bit fixed-point arithmetic
5//! to ensure bit-exact results regardless of CPU or compiler.
6//!
7//! # Features
8//!
9//! - **Deterministic**: the Fix128 core (rigid bodies, constraints, joints,
10//!   BVH, CCD) is bit-exact across all platforms — pure integer arithmetic.
11//!   <!-- claim-test: test_multi_step_bit_exact -->
12//!   Grid / engineering modules on `f32` / `f64` are bit-exact via `det_math`
13//!   (correctly-rounded software transcendental functions); see README
14//!   "Determinism" for the per-module table.
15//!   <!-- claim-test: determinism_freefall -->
16//! - **128-bit Fixed-Point**: I64F64 format with CORDIC trigonometry
17//! - **XPBD Solver**: Extended Position Based Dynamics for stable constraints
18//! - **GJK/EPA**: Robust collision detection for convex shapes
19//! - **Linear BVH**: Morton code-based spatial acceleration (used for broad-phase and raycast)
20//! - **Rollback Support**: State serialization for netcode
21//!
22//! # Feature Flags
23//!
24//! <!-- readme-sync: features -->
25//! | Feature | Description |
26//! |---------|-------------|
27//! | `std` (default) | Standard library support. Disable for `no_std` (requires `alloc`). |
28//! | `simd` | SSE2 acceleration for `Vec3Fix` math ops (`dot`, `add`, `sub`). Results remain bit-exact. Solver hot paths use scalar arithmetic to guarantee cross-platform determinism. |
29//! | `parallel` | Parallel constraint solving via Rayon with graph-colored batches. |
30//! | `neural` | Deterministic neural controller integration (ALICE-ML). |
31//! | `python` | `PyO3` + `NumPy` zero-copy Python bindings. |
32//! | `replay` | Replay recording/playback via ALICE-DB. |
33//! | `ffi` | C FFI for Unity/UE5/game engine integration. |
34//! | `wasm` | WebAssembly bindings via wasm-bindgen. |
35//! | `analytics` | ALICE-Analytics simulation profiling (`DDSketch`, `HyperLogLog`). |
36//! | `gpu-solver-bridge` | `GpuSolverBridge` trait for external GPU solver backends. No cost when off. |
37//!
38//! # Example
39//!
40//! ```rust
41//! use alice_physics::{PhysicsWorld, PhysicsConfig, RigidBody, Fix128, Vec3Fix};
42//!
43//! // Create physics world
44//! let config = PhysicsConfig::default();
45//! let mut world = PhysicsWorld::new(config);
46//!
47//! // Add a dynamic body
48//! let body = RigidBody::new_dynamic(
49//!     Vec3Fix::from_int(0, 10, 0),  // position
50//!     Fix128::ONE,                   // mass = 1
51//! );
52//! let body_id = world.add_body(body);
53//!
54//! // Step simulation (60 frames at 1/60 second)
55//! let dt = Fix128::from_ratio(1, 60);
56//! for _ in 0..60 {
57//!     world.step(dt);
58//! }
59//!
60//! // Body should have fallen under gravity
61//! let pos = world.bodies[body_id].position;
62//! assert!(pos.y < Fix128::from_int(10), "Body fell under gravity");
63//! ```
64//!
65//! # Modules
66//!
67//! ## Core
68//! - [`math`]: Fixed-point math primitives (Fix128, `Vec3Fix`, `QuatFix`, `Mat3Fix`, CORDIC)
69//! - [`collider`]: Collision shapes (Sphere, Capsule, `ConvexHull`, AABB) and GJK/EPA detection
70//! - [`solver`]: XPBD physics solver, rigid body dynamics, constraint batching
71//! - [`bvh`]: Linear BVH for broad-phase collision detection (Morton codes, stackless traversal)
72//! - [`error`]: Unified physics error type (`PhysicsError`)
73//!
74//! ## Collision Shapes
75//! - [`box_collider`]: Oriented Bounding Box (OBB) with GJK support
76//! - [`compound`]: Multi-shape compound collider with local transforms
77//! - [`cone`]: Cone collider with GJK support (apex +Y, base -Y)
78//! - [`convex_mesh_builder`]: Incremental convex hull builder from point sets
79//! - [`cylinder`]: Cylinder collider with GJK support
80//! - [`ellipsoid`]: Ellipsoid collider with three independent semi-axes
81//! - [`plane_collider`]: Infinite plane collider (sphere/AABB intersection)
82//! - [`sdf_collider`]: SDF-based collision shapes (distance field surfaces)
83//! - [`spherical_terrain`]: Sphere-world ground as a radial height field, and central gravity
84//! - [`torus`]: Torus collider with major/minor radii
85//! - [`wedge`]: Wedge (triangular prism) collider with 6 vertices
86//!
87//! ## Spatial Acceleration
88//! - [`dynamic_bvh`]: Incremental AABB tree with O(log n) insert/remove/update
89//! - [`spatial`]: Hash grid for neighbor queries (shared across fluid, cloth)
90//! - [`contact_cache`]: Persistent contact manifold cache with `HashMap` O(1) lookup
91//!
92//! ## Constraints & Dynamics
93//! - [`joint`]: Joint constraints (Ball, Hinge, Fixed, Slider, Spring, D6, `ConeTwist`)
94//! - [`joint_extra`]: Advanced joints (Pulley, Gear, Weld, Rack-and-Pinion, Mouse)
95//! - [`motor`]: PD controllers and joint motors
96//! - [`articulation`]: Articulated bodies (ragdolls, robotic arms, Featherstone)
97//! - [`force`]: Custom force fields (wind, gravity, buoyancy, vortex, explosion, magnetic)
98//! - [`sleeping`]: Sleep/wake and island management (Union-Find)
99//! - [`filter`]: Collision filtering with layer/mask bitmasks (see [`filter::layers`])
100//! - [`material`]: Material pair table (friction, restitution, combine rules)
101//!
102//! ## Queries
103//! - [`raycast`]: Ray casting against spheres, AABBs, capsules, planes
104//! - [`shape_raycast`]: World ray queries against the geometry bodies collide as (shapes, compounds, static colliders, SDF)
105//! - [`sensors`]: Simulated lidar, contact sensor and IMU with deterministic Gaussian noise
106//! - [`query`]: Shape cast (sphere, capsule) and overlap queries (sphere, AABB)
107//! - [`ccd`]: Continuous collision detection (TOI, conservative advancement, speculative)
108//!
109//! ## Soft Body & Simulation
110//! - [`rope`]: XPBD distance chain rope and cable simulation
111//! - [`cloth`]: XPBD triangle mesh cloth with self-collision
112//! - [`fluid`]: Position-Based Fluids (PBF) with spatial hash grid
113//! - [`deformable`]: FEM-XPBD deformable body simulation
114//! - [`vehicle`]: Vehicle physics (wheel, suspension, engine, steering)
115//! - [`vehicle_dynamics`]: Per-wheel vehicle dynamics (tyre models, wheel spin, brakes / ABS, road surfaces and weather, powertrain, scenario metrics and lossless replay)
116//! - [`character`]: Kinematic capsule-based character controller (move-and-slide)
117//! - [`world_character`]: `PhysicsWorld::move_character`, move-and-slide of a character against the world's collided geometry
118//! - [`trimesh`]: Triangle mesh collision with BVH acceleration (Moller-Trumbore)
119//! - [`heightfield`]: Height field terrain collision (bilinear interpolation)
120//! - [`cloth_fluid`]: Cloth-fluid coupling (drag, buoyancy, boundary repulsion)
121//! - [`rope_attach`]: Rope attachment to rigid bodies with compliance and break force
122//! - [`soft_body_cut`]: Plane-based cutting of deformable bodies and cloth
123//! - [`particle`]: General-purpose particle system with emitters and force fields
124//!
125//! ## 2D Physics
126//! - [`physics2d`]: Complete 2D engine (SAT collision, XPBD solver, joints)
127//!
128//! ## Mesh & I/O Utilities
129//! - [`mass_properties`]: Mass and inertia tensor computation for standard shapes
130//! - [`collision_mesh_gen`]: Marching cubes SDF-to-mesh with simplification
131//! - [`scene_io`]: Binary (.aphys) and JSON scene serialization (`std`)
132//! - [`multi_world`]: Multiple independent physics worlds with body transfer
133//!
134//! ## Visualization
135//! - [`heatmap`]: Stress/temperature heatmap with viridis colormap
136//! - [`flow_viz`]: Fluid flow arrows and streamline generation
137//! - [`contact_viz`]: Contact force arrows and friction cone visualization
138//!
139//! ## SDF Integration
140//! - [`sdf_manifold`]: Multi-point contact manifold from SDF surfaces
141//! - [`sdf_ccd`]: Sphere tracing continuous collision detection for SDF
142//! - [`sdf_force`]: SDF-driven force fields (attract, repel, contain, flow)
143//! - [`sdf_destruction`]: Real-time CSG boolean destruction (`std`)
144//! - [`sdf_adaptive`]: Adaptive SDF evaluation with distance-based LOD (`std`)
145//! - [`convex_decompose`]: Convex decomposition from SDF voxel grid (`std`)
146//! - [`gpu_sdf`]: GPU compute shader interface for batch SDF evaluation (`std`)
147//!
148//! ## SDF Simulation Modifiers (`std`)
149//! - [`sim_field`]: 3D scalar/vector fields with trilinear interpolation and diffusion
150//! - [`sim_modifier`]: Physics modifier chain for SDF surfaces
151//! - [`thermal`]: Heat diffusion, melt, thermal expansion, freeze
152//! - [`pressure`]: Contact-force deformation (crush, bulge, dent)
153//! - [`erosion`]: Wind, water, chemical, ablation erosion
154//! - [`fracture`]: Stress-driven crack propagation with CSG subtraction
155//! - [`phase_change`]: Solid/liquid/gas transitions driven by temperature
156//! - [`coupled_field`]: Deterministic `Fix128` channel for sharing one field between physics
157//!
158//! ## Game Systems
159//! - [`animation_blend`]: Ragdoll animation blending with SLERP
160//! - [`audio_physics`]: Physics-based audio parameter generation
161//! - [`netcode`]: Deterministic simulation with frame input, checksum, rollback
162//! - [`fluid_netcode`]: Deterministic fluid netcode with delta compression (`std`)
163//! - [`interpolation`]: Substep interpolation with SLERP quaternion blending
164//! - [`debug_render`]: Wireframe visualization API (bodies, contacts, joints, BVH)
165//! - [`profiling`]: Per-stage timer and per-frame statistics
166//!
167//! ## Analytics & Privacy (`std`)
168//! - [`sketch`]: Probabilistic sketches (`HyperLogLog`, `DDSketch`, Count-Min)
169//! - [`anomaly`]: Streaming anomaly detection (MAD, EWMA, Z-score)
170//! - [`privacy`]: Local differential privacy (Laplace, RAPPOR)
171//! - [`pipeline`]: Lock-free metric aggregation pipeline
172//!
173//! ## Utility
174//! - [`rng`]: Deterministic pseudo-random number generator (PCG-XSH-RR)
175//! - [`event`]: Contact and trigger event tracking
176//!
177//! ## Feature-Gated
178//! - `neural`: Deterministic neural controller (ALICE-ML, `--features neural`)
179//! - `ffi`: C FFI for Unity/UE5 game engines (`--features ffi`)
180//! - `replay`: Replay recording/playback (`--features replay`)
181//! - `db_bridge`: Physics state persistence bridge (`--features replay`)
182//! - `analytics_bridge`: Simulation profiling bridge (`--features analytics`)
183//!
184//! # Determinism
185//!
186//! This engine guarantees bit-exact results by:
187//!
188//! 1. Using 128-bit fixed-point instead of IEEE 754 floating-point
189//! 2. CORDIC algorithm for trigonometry (no FPU instructions)
190//! 3. Fixed iteration counts in all algorithms
191//! 4. Deterministic sorting (stable sort with explicit comparators)
192//!
193//! This makes it suitable for:
194//! - Lockstep multiplayer games
195//! - Rollback netcode
196//! - Replay systems
197//! - Distributed physics simulation
198
199// TGS solver family (`solver_tgs*`) modules opt out of missing_docs
200// locally with `#![allow(missing_docs)]` while their per-item `///`
201// docs are being backfilled (Turn E follow-up). The rest of the crate
202// is now under `deny` — verified 0 warnings via
203// `cargo doc --no-deps --all-features` at v0.14.0-preview.3 landing,
204// escalated from `warn` on 2026-09-13 (v1.0 roadmap Item D).
205#![deny(missing_docs)]
206// Pedantic: suppress lints inherent to physics/math code.
207// - Cast lints: Fixed-point ↔ float conversions and index casts are pervasive.
208// - Naming: coordinate variables (px, py, pz), vertex indices (v0, v1, v2).
209// - must_use: pure functions returning computed values are the norm; annotating
210//   every constructor and accessor adds noise without improving safety.
211// - doc_markdown: technical identifiers in docs are context-obvious.
212// - unreadable_literal: hash constants and bit patterns are more readable without separators.
213// - large_stack_arrays: physics lookup tables and inline data are intentional.
214#![allow(
215    clippy::cast_possible_truncation,
216    clippy::cast_precision_loss,
217    clippy::cast_sign_loss,
218    clippy::cast_lossless,
219    clippy::cast_possible_wrap,
220    clippy::similar_names,
221    clippy::many_single_char_names,
222    clippy::module_name_repetitions,
223    clippy::unreadable_literal,
224    clippy::large_stack_arrays,
225    clippy::doc_markdown
226)]
227#![cfg_attr(not(feature = "std"), no_std)]
228
229#[cfg(all(feature = "wasm", feature = "ffi"))]
230compile_error!("Features `wasm` and `ffi` are mutually exclusive. Enable only one.");
231
232#[cfg(all(feature = "wasm", not(feature = "std")))]
233compile_error!("Feature `wasm` requires `std`.");
234
235#[cfg(not(feature = "std"))]
236extern crate alloc;
237
238// Test builds only: lets a module compile an integration test file (which
239// names the crate `alice_physics`) as part of the lib tests, so its lines count
240// in the `--lib` coverage.
241#[cfg(test)]
242extern crate self as alice_physics;
243
244pub mod acoustic_wave;
245pub mod aeroelasticity;
246#[cfg(feature = "analytics")]
247pub mod analytics_bridge;
248pub mod animation_blend;
249pub mod anisotropic;
250pub mod anisotropic_friction;
251#[cfg(feature = "std")]
252pub mod anomaly;
253pub mod articulation;
254pub mod atmosphere;
255pub mod audio_physics;
256pub mod beam_stress;
257pub mod bimaterial;
258pub mod box_collider;
259#[cfg(feature = "std")]
260pub mod bridging;
261pub mod buckling;
262pub mod buoyancy_zone;
263pub mod bvh;
264pub mod ccd;
265#[cfg(feature = "std")]
266pub mod cfd_solver;
267pub mod character;
268pub mod character_state;
269pub mod cloth;
270pub mod cloth_fluid;
271pub mod collider;
272pub mod collision_mesh_gen;
273pub mod compound;
274pub mod compressible;
275pub mod cone;
276pub mod contact_cache;
277pub mod contact_viz;
278#[cfg(feature = "std")]
279pub mod convex_decompose;
280pub mod convex_mesh_builder;
281pub mod coupled_field;
282pub mod coupled_iteration;
283pub mod coupling_medium;
284pub mod creep_longterm;
285pub mod crowd_force;
286pub mod cubic_elastic_fem;
287pub mod cylinder;
288pub mod damping_rayleigh;
289#[cfg(feature = "replay")]
290pub mod db_bridge;
291pub mod debug_render;
292pub mod deformable;
293/// Deterministic `f32` / `f64` transcendentals (cross-platform bit-exact).
294///
295/// Since 1.3.0 the implementation lives in the [`alice_det_math`] crate;
296/// every function here is an `#[inline]` delegation to it, so
297/// `alice_physics::det_math::sin` and `alice_det_math::sin` are the same law
298/// — ALICE-SDF's evaluator uses the same crate, which is what makes the
299/// `SdfField` boundary bit-exact across the two. Available without `std`
300/// (software `sqrt`; same bits).
301///
302/// The functions are delegations rather than a `pub use` because
303/// `cargo-semver-checks` cannot see items re-exported from another crate and
304/// would report the 1.2.0 API as removed.
305pub mod det_math {
306    pub use alice_det_math::{acos64, asin64, round, round64, sin_cos, sqrt, sqrt64};
307
308    macro_rules! delegate {
309        ($(fn $name:ident($($arg:ident: $ty:ty),*) -> $ret:ty;)*) => {
310            $(
311                #[doc = concat!("Delegates to [`alice_det_math::", stringify!($name), "`].")]
312                #[inline]
313                #[must_use]
314                pub fn $name($($arg: $ty),*) -> $ret {
315                    alice_det_math::$name($($arg),*)
316                }
317            )*
318        };
319    }
320
321    delegate! {
322        fn sin(x: f32) -> f32;
323        fn cos(x: f32) -> f32;
324        fn exp(x: f32) -> f32;
325        fn ln(x: f32) -> f32;
326        fn powf(x: f32, y: f32) -> f32;
327        fn powi(x: f32, n: i32) -> f32;
328        fn cbrt(x: f32) -> f32;
329        fn hypot(x: f32, y: f32) -> f32;
330        fn atan(x: f32) -> f32;
331        fn atan2(y: f32, x: f32) -> f32;
332        fn asin(x: f32) -> f32;
333        fn acos(x: f32) -> f32;
334        fn tan(x: f32) -> f32;
335        fn tanh(x: f32) -> f32;
336        fn atan64(x: f64) -> f64;
337        fn atan2_64(y: f64, x: f64) -> f64;
338        fn exp64(x: f64) -> f64;
339        fn ln64(x: f64) -> f64;
340        fn powf64(x: f64, y: f64) -> f64;
341    }
342}
343#[cfg(any(feature = "ffi", feature = "wasm", feature = "python"))]
344mod binding_api;
345pub mod dynamic_bvh;
346#[cfg(feature = "std")]
347pub mod dynamic_fem;
348pub mod electromagnetic;
349pub mod ellipsoid;
350#[cfg(feature = "std")]
351pub mod erosion;
352pub mod error;
353pub mod euler_fv;
354pub mod eulerian_grid;
355pub mod event;
356pub mod fatigue;
357#[cfg(feature = "ffi")]
358pub mod ffi;
359pub mod filament_db;
360
361pub(crate) mod body_collider;
362pub mod fillet_stress;
363pub mod filter;
364#[cfg(feature = "std")]
365pub mod flow_viz;
366pub mod fluid;
367#[cfg(feature = "std")]
368pub mod fluid_netcode;
369pub mod force;
370#[cfg(feature = "std")]
371pub mod fracture;
372#[cfg(feature = "std")]
373pub mod fsi_advanced;
374#[cfg(feature = "std")]
375pub mod gpu_sdf;
376pub mod heatmap;
377pub mod heightfield;
378pub mod hyperelastic;
379#[cfg(feature = "std")]
380pub mod ik_physics_bridge;
381pub mod interface_capture;
382pub mod interpolation;
383pub mod joint;
384pub mod joint_extra;
385pub mod kepler;
386pub mod kinematic_loop;
387#[cfg(feature = "std")]
388pub mod laminate;
389pub mod laminate_failure;
390// Content identifier of the rules a world is configured with.
391pub mod law_id;
392pub mod layer_adhesion;
393pub mod lift_drag;
394/// Small-strain linear elastic FEM on tetrahedra (P1), producing a Cauchy
395/// stress tensor per element from an [`sdf_fem_mesh::SdfTetMesh`].
396///
397/// `std` only, because its input is: [`sdf_fem_mesh`] deduplicates vertices
398/// through a `HashMap`, which `alloc` does not provide, so it carries the same
399/// gate. Adding `alloc` imports here would not help — the module cannot be
400/// built at all without the type it consumes.
401#[cfg(feature = "std")]
402pub mod linear_elastic_fem;
403pub mod linear_solver;
404pub mod mass_properties;
405pub mod material;
406pub mod math;
407pub mod math_util;
408pub mod maxwell_fdtd;
409pub mod metric;
410pub mod modal;
411pub mod molecular_dynamics;
412pub mod motor;
413pub mod multi_world;
414pub mod multiphase;
415pub mod nbody;
416pub mod netcode;
417#[cfg(feature = "std")]
418pub mod netcode_prediction;
419#[cfg(feature = "neural")]
420pub mod neural;
421pub mod non_newtonian;
422pub mod pair_potential;
423pub mod particle;
424#[cfg(feature = "std")]
425pub mod phase_change;
426#[cfg(feature = "std")]
427pub mod piezoelectric;
428#[cfg(feature = "std")]
429pub mod pipeline;
430pub mod plane_collider;
431pub mod plastic;
432#[cfg(feature = "std")]
433pub mod pressure;
434pub mod prestressed;
435pub mod print_orientation;
436#[cfg(feature = "std")]
437pub mod print_pipeline_solver;
438#[cfg(feature = "std")]
439pub mod privacy;
440pub mod profiling;
441#[cfg(feature = "python")]
442mod python;
443pub mod quadratic_elastic_fem;
444pub mod query;
445pub mod ragdoll;
446pub mod raycast;
447#[cfg(feature = "replay")]
448pub mod replay;
449pub mod rng;
450#[cfg(feature = "std")]
451pub mod rolling_contact;
452pub mod rope;
453pub mod rope_attach;
454pub mod rotor;
455#[cfg(feature = "std")]
456pub mod scene_io;
457#[cfg(feature = "std")]
458pub mod sdf_adaptive;
459pub mod sdf_ccd;
460pub mod sdf_character;
461pub mod sdf_collider;
462#[cfg(feature = "std")]
463pub mod sdf_destruction;
464#[cfg(feature = "std")]
465pub mod sdf_fem_mesh;
466pub mod sdf_force;
467pub mod sdf_manifold;
468#[cfg(feature = "std")]
469pub mod sdf_sph;
470pub mod sdf_wind_field;
471// Content hash of the stepping semantics; no `cfg`: the same value in every build.
472pub mod semantics;
473pub mod sensors;
474pub mod shape;
475pub mod shape_raycast;
476#[cfg(feature = "std")]
477pub mod sim_field;
478#[cfg(feature = "std")]
479pub mod sim_modifier;
480pub mod static_collider;
481pub mod structural_solver;
482#[cfg(feature = "std")]
483pub mod support_volume;
484pub mod surface_tension_csf;
485#[cfg(feature = "std")]
486pub mod thin_wall;
487pub mod vibration_wall;
488pub mod warp_risk;
489pub mod wave_ship;
490pub mod wind_zone;
491pub mod world_character;
492pub mod world_participant;
493pub mod world_shape_query;
494
495pub mod physics2d;
496
497#[cfg(feature = "gpu-solver-bridge")]
498pub mod gpu_bridge;
499#[cfg(feature = "std")]
500pub mod sketch;
501pub mod sleeping;
502pub mod smoke_fire;
503pub mod soft_body_cut;
504pub mod solver;
505// Gyroscopic term `ω × Iω` by symplectic splitting, shared by XPBD and TGS.
506pub(crate) mod gyroscopic;
507// solver_tgs* family: pub(crate) since v0.14.0-preview.8 (v1.0 Item B
508// Option C — TGS extension mechanism with zero downstream adoption is
509// hidden from the public surface; re-expose via semver-minor bump if
510// concrete demand emerges).
511#[cfg(feature = "std")]
512pub(crate) mod solver_tgs;
513#[cfg(feature = "std")]
514pub(crate) mod solver_tgs_hooks_6dof_oriented;
515#[cfg(feature = "std")]
516pub(crate) mod solver_tgs_hooks_6dof_oriented_scoped;
517// Adapter from `solver::RigidBody` / `ContactConstraint` to the solver_tgs*
518// family's oriented body/contact types — the production entry point for
519// `SolverBackend::Tgs` (`PhysicsWorld::step_tgs`). std-gated for the same
520// reason as the rest of `solver_tgs*` (the family uses `std::collections::HashMap`).
521#[cfg(feature = "std")]
522pub(crate) mod solver_tgs_backend;
523pub mod spatial;
524pub mod spherical_terrain;
525#[cfg(feature = "std")]
526pub mod thermal;
527pub mod thermal_stress;
528pub mod torus;
529#[cfg(feature = "std")]
530pub mod transient_thermal;
531pub mod trimesh;
532pub mod turbulence;
533pub mod vehicle;
534pub mod vehicle_dynamics;
535#[cfg(feature = "wasm")]
536mod wasm;
537pub mod wedge;
538
539// Re-export commonly used types
540pub use animation_blend::{AnimationBlender, AnimationClip, BlendMode, SkeletonPose};
541#[cfg(feature = "std")]
542pub use anomaly::{
543    AnomalyCallback, AnomalyEvent, CompositeDetector, EwmaDetector, MadDetector, StreamingMedian,
544    ZScoreDetector,
545};
546pub use articulation::{ArticulatedBody, FeatherstoneSolver};
547pub use audio_physics::{AudioConfig, AudioEvent, AudioGenerator, AudioMaterial};
548pub use box_collider::OrientedBox;
549pub use bvh::{BvhNode, BvhPrimitive, LinearBvh};
550pub use ccd::{
551    aabb_plane_toi, capsule_plane_toi, speculative_contact, sphere_capsule_toi, CcdConfig,
552};
553pub use character::{CharacterConfig, CharacterController, MoveResult, PushImpulse};
554pub use cloth::{Cloth, ClothConfig};
555pub use cloth_fluid::{
556    apply_cloth_boundary_to_fluid, apply_fluid_forces_to_cloth, ClothFluidCoupling,
557};
558pub use collider::{Capsule, CollisionResult, ConvexHull, ScaledShape, Sphere, Support, AABB};
559pub use collision_mesh_gen::{
560    compute_mesh_aabb, generate_collision_mesh, simplify_collision_mesh, CollisionMesh,
561    CollisionMeshConfig,
562};
563#[cfg(feature = "std")]
564pub use compound::{CompoundShape, ShapeRef};
565pub use cone::Cone;
566pub use contact_cache::{BodyPairKey, ContactCache, ContactManifold};
567pub use contact_viz::{
568    generate_contact_arrows, generate_friction_cones, ContactArrow, FrictionCone,
569};
570#[cfg(feature = "std")]
571pub use convex_decompose::{DecomposeConfig, DecompositionResult};
572pub use convex_mesh_builder::{build_convex_hull, compute_centroid};
573pub use cylinder::Cylinder;
574pub use debug_render::{debug_draw_world, DebugColor, DebugDrawData, DebugDrawFlags};
575pub use deformable::{DeformableBody, DeformableConfig};
576pub use dynamic_bvh::DynamicAabbTree;
577pub use ellipsoid::Ellipsoid;
578#[cfg(feature = "std")]
579pub use erosion::{ErosionConfig, ErosionModifier, ErosionType};
580pub use error::PhysicsError;
581pub use event::{ContactEvent, ContactEventType, EventCollector, JointBreakEvent, TriggerEvent};
582/// Re-export predefined collision layer constants for convenience.
583pub use filter::layers;
584pub use filter::CollisionFilter;
585#[cfg(feature = "std")]
586pub use flow_viz::{
587    generate_flow_arrows, generate_streamlines, FlowArrow, FlowVizConfig, Streamlines,
588};
589pub use fluid::{Fluid, FluidConfig};
590#[cfg(feature = "std")]
591pub use fluid_netcode::{FluidDelta, FluidSnapshot};
592pub use force::{apply_force_fields, ForceField, ForceFieldInstance};
593#[cfg(feature = "std")]
594pub use fracture::{Crack, FractureConfig, FractureModifier};
595#[cfg(feature = "std")]
596pub use gpu_sdf::{
597    batch_size, GpuDispatchConfig, GpuSdfBatch, GpuSdfInstancedBatch, GpuSdfMultiDispatch,
598    GpuSdfQuery, GpuSdfResult,
599};
600pub use heatmap::{
601    generate_stress_heatmap, generate_temperature_heatmap, heatmap_to_rgba, Heatmap, HeatmapConfig,
602    SliceAxis,
603};
604pub use heightfield::HeightField;
605pub use interpolation::{BodySnapshot, InterpolationState, WorldSnapshot};
606#[allow(deprecated)] // re-exported for compatibility until 3.0
607pub use joint::{
608    solve_joints, solve_joints_breakable, solve_joints_breaking_on_force,
609    solve_joints_with_reaction_forces,
610};
611pub use joint::{
612    BallJoint, ConeTwistJoint, D6Joint, D6Motion, FixedJoint, HingeJoint, Joint, JointType,
613    SliderJoint, SpringJoint,
614};
615pub use joint_extra::{
616    solve_extra_joints, ExtraJoint, GearJoint, MouseJoint, PulleyJoint, RackAndPinionJoint,
617    WeldJoint,
618};
619pub use mass_properties::{
620    box_mass_properties, capsule_mass_properties, convex_hull_mass_properties,
621    cylinder_mass_properties, sphere_mass_properties, translate_inertia, MassProperties,
622};
623pub use material::{CombineRule, CombinedMaterial, MaterialId, MaterialTable, PhysicsMaterial};
624pub use math::{simd_width, Fix128, Mat3Fix, QuatFix, Vec3Fix, SIMD_WIDTH};
625pub use motor::{JointMotor, MotorMode, PdController};
626pub use multi_world::{MultiWorld, Portal};
627pub use netcode::{
628    DeterministicSimulation, FrameInput, InputApplicator, NetcodeConfig, SimulationChecksum,
629    SimulationSnapshot,
630};
631pub use particle::{LandingEvent, LandingTarget, Particle, ParticleEmitter, ParticleSystem};
632#[cfg(feature = "std")]
633pub use phase_change::{Phase, PhaseChangeConfig, PhaseChangeModifier};
634pub use physics2d::{
635    BodyType2D, Contact2D, Joint2D, PhysicsConfig2D, PhysicsWorld2D, RigidBody2D, Shape2D, Vec2Fix,
636};
637#[cfg(feature = "std")]
638pub use pipeline::{
639    MetricEvent, MetricPipeline, MetricRegistry, MetricSnapshot, MetricType, RingBuffer,
640};
641pub use plane_collider::PlaneCollider;
642#[cfg(feature = "std")]
643pub use pressure::{PressureConfig, PressureModifier};
644#[cfg(feature = "std")]
645pub use privacy::{
646    LaplaceNoise, PrivacyBudget, PrivateAggregator, RandomizedResponse, Rappor, XorShift64,
647};
648pub use profiling::{PhysicsProfiler, ProfileEntry, StepStats};
649pub use query::{
650    batch_raycast, batch_sphere_cast, capsule_cast, overlap_aabb, overlap_aabb_bvh, overlap_sphere,
651    overlap_sphere_bvh, sphere_cast, BatchRayQuery, OverlapResult, ShapeCastHit,
652};
653pub use raycast::{
654    raycast_all_aabbs, raycast_all_spheres, raycast_any_aabbs, raycast_any_spheres, Ray, RayHit,
655};
656pub use rng::DeterministicRng;
657pub use rope::{Rope, RopeConfig};
658pub use rope_attach::{solve_rope_attachments, RopeAttachment};
659#[cfg(feature = "std")]
660pub use scene_io::{
661    load_scene, load_scene_json, save_scene, save_scene_json, PhysicsScene, SerializedBody,
662    SerializedJoint,
663};
664#[cfg(feature = "std")]
665pub use sdf_adaptive::{AdaptiveConfig, AdaptiveSdfEvaluator};
666pub use sdf_ccd::SdfCcdConfig;
667pub use sdf_collider::{ClosureSdf, SdfCollider, SdfContact, SdfField};
668#[cfg(feature = "std")]
669pub use sdf_destruction::{DestructibleSdf, DestructionShape};
670pub use sdf_force::{SdfForceField, SdfForceType};
671pub use sdf_manifold::ManifoldConfig;
672#[cfg(feature = "std")]
673pub use sdf_manifold::SdfManifold;
674pub use semantics::{PHYSICS_SEMANTICS_ID, PHYSICS_SEMANTICS_PINS};
675pub use shape::{Shape, ShapeError};
676#[cfg(feature = "std")]
677pub use sim_field::{ScalarField3D, VectorField3D};
678#[cfg(feature = "std")]
679pub use sim_modifier::{ModifiedSdf, PhysicsModifier, SingleModifiedSdf};
680#[cfg(feature = "std")]
681pub use sketch::{CountMinSketch, DDSketch, FnvHasher, HeavyHitters, HyperLogLog, Mergeable};
682pub use sleeping::{Island, IslandManager, SleepConfig, SleepData, SleepState};
683pub use soft_body_cut::{cut_cloth, cut_deformable, CutPlane, CutResult};
684#[cfg(feature = "std")]
685pub use solver::ContactModifier;
686#[cfg(feature = "std")]
687pub use solver::TgsCacheStats;
688pub use solver::{
689    BodyType, ContactConstraint, DistanceConstraint, LawCheck, PhysicsConfig, PhysicsWorld,
690    RigidBody, SolverBackend, StageWork, WorldCcdConfig, WorldSnapshotError,
691};
692pub use spatial::SpatialGrid;
693#[cfg(feature = "std")]
694pub use thermal::{HeatSource, ThermalConfig, ThermalModifier};
695pub use torus::Torus;
696pub use trimesh::{TriMesh, Triangle};
697pub use vehicle::{Vehicle, VehicleConfig};
698pub use wedge::Wedge;
699
700/// Prelude module for convenient imports
701pub mod prelude {
702    pub use crate::animation_blend::{AnimationBlender, AnimationClip, BlendMode, SkeletonPose};
703    #[cfg(feature = "std")]
704    pub use crate::anomaly::{
705        AnomalyCallback, AnomalyEvent, CompositeDetector, EwmaDetector, MadDetector,
706        StreamingMedian, ZScoreDetector,
707    };
708    pub use crate::articulation::{ArticulatedBody, FeatherstoneSolver};
709    pub use crate::audio_physics::{AudioConfig, AudioEvent, AudioGenerator, AudioMaterial};
710    pub use crate::box_collider::OrientedBox;
711    pub use crate::bvh::{BvhNode, BvhPrimitive, LinearBvh};
712    pub use crate::ccd::{speculative_contact, CcdConfig};
713    pub use crate::character::{CharacterConfig, CharacterController, MoveResult, PushImpulse};
714    pub use crate::cloth::{Cloth, ClothConfig};
715    pub use crate::cloth_fluid::{
716        apply_cloth_boundary_to_fluid, apply_fluid_forces_to_cloth, ClothFluidCoupling,
717    };
718    pub use crate::collider::{
719        Capsule, CollisionResult, ConvexHull, ScaledShape, Sphere, Support, AABB,
720    };
721    pub use crate::collision_mesh_gen::{
722        compute_mesh_aabb, generate_collision_mesh, simplify_collision_mesh, CollisionMesh,
723        CollisionMeshConfig,
724    };
725    #[cfg(feature = "std")]
726    pub use crate::compound::{CompoundShape, ShapeRef};
727    pub use crate::cone::Cone;
728    pub use crate::contact_cache::{BodyPairKey, ContactCache, ContactManifold};
729    pub use crate::contact_viz::{
730        generate_contact_arrows, generate_friction_cones, ContactArrow, FrictionCone,
731    };
732    #[cfg(feature = "std")]
733    pub use crate::convex_decompose::{DecomposeConfig, DecompositionResult};
734    pub use crate::convex_mesh_builder::{build_convex_hull, compute_centroid};
735    pub use crate::cylinder::Cylinder;
736    pub use crate::debug_render::{debug_draw_world, DebugColor, DebugDrawData, DebugDrawFlags};
737    pub use crate::deformable::{DeformableBody, DeformableConfig};
738    pub use crate::dynamic_bvh::DynamicAabbTree;
739    pub use crate::ellipsoid::Ellipsoid;
740    #[cfg(feature = "std")]
741    pub use crate::erosion::{ErosionConfig, ErosionModifier, ErosionType};
742    pub use crate::error::PhysicsError;
743    pub use crate::event::{
744        ContactEvent, ContactEventType, EventCollector, JointBreakEvent, TriggerEvent,
745    };
746    pub use crate::filter::layers;
747    pub use crate::filter::CollisionFilter;
748    #[cfg(feature = "std")]
749    pub use crate::flow_viz::{
750        generate_flow_arrows, generate_streamlines, FlowArrow, FlowVizConfig, Streamlines,
751    };
752    pub use crate::fluid::{Fluid, FluidConfig};
753    #[cfg(feature = "std")]
754    pub use crate::fluid_netcode::{FluidDelta, FluidSnapshot};
755    pub use crate::force::{apply_force_fields, ForceField, ForceFieldInstance};
756    #[cfg(feature = "std")]
757    pub use crate::fracture::{Crack, FractureConfig, FractureModifier};
758    #[cfg(feature = "std")]
759    pub use crate::gpu_sdf::{
760        batch_size, GpuDispatchConfig, GpuSdfBatch, GpuSdfInstancedBatch, GpuSdfMultiDispatch,
761        GpuSdfQuery, GpuSdfResult,
762    };
763    pub use crate::heatmap::{
764        generate_stress_heatmap, generate_temperature_heatmap, heatmap_to_rgba, Heatmap,
765        HeatmapConfig, SliceAxis,
766    };
767    pub use crate::heightfield::HeightField;
768    pub use crate::interpolation::{BodySnapshot, InterpolationState, WorldSnapshot};
769    #[allow(deprecated)] // re-exported for compatibility until 3.0
770    pub use crate::joint::{
771        solve_joints, solve_joints_breakable, solve_joints_breaking_on_force,
772        solve_joints_with_reaction_forces,
773    };
774    pub use crate::joint::{
775        BallJoint, ConeTwistJoint, D6Joint, D6Motion, FixedJoint, HingeJoint, Joint, JointType,
776        SliderJoint, SpringJoint,
777    };
778    pub use crate::joint_extra::{
779        solve_extra_joints, ExtraJoint, GearJoint, MouseJoint, PulleyJoint, RackAndPinionJoint,
780        WeldJoint,
781    };
782    pub use crate::mass_properties::{
783        box_mass_properties, capsule_mass_properties, convex_hull_mass_properties,
784        cylinder_mass_properties, sphere_mass_properties, translate_inertia, MassProperties,
785    };
786    pub use crate::material::{
787        CombineRule, CombinedMaterial, MaterialId, MaterialTable, PhysicsMaterial,
788    };
789    pub use crate::math::{simd_width, Fix128, Mat3Fix, QuatFix, Vec3Fix, SIMD_WIDTH};
790    pub use crate::motor::{JointMotor, MotorMode, PdController};
791    pub use crate::multi_world::{MultiWorld, Portal};
792    pub use crate::netcode::{
793        DeterministicSimulation, FrameInput, InputApplicator, NetcodeConfig, SimulationChecksum,
794        SimulationSnapshot,
795    };
796    pub use crate::particle::{
797        LandingEvent, LandingTarget, Particle, ParticleEmitter, ParticleSystem,
798    };
799    #[cfg(feature = "std")]
800    pub use crate::phase_change::{Phase, PhaseChangeConfig, PhaseChangeModifier};
801    #[cfg(feature = "std")]
802    pub use crate::pipeline::{
803        MetricEvent, MetricPipeline, MetricRegistry, MetricSnapshot, MetricType, RingBuffer,
804    };
805    pub use crate::plane_collider::PlaneCollider;
806    #[cfg(feature = "std")]
807    pub use crate::pressure::{PressureConfig, PressureModifier};
808    #[cfg(feature = "std")]
809    pub use crate::privacy::{
810        LaplaceNoise, PrivacyBudget, PrivateAggregator, RandomizedResponse, Rappor, XorShift64,
811    };
812    pub use crate::profiling::{PhysicsProfiler, ProfileEntry, StepStats};
813    pub use crate::query::{
814        batch_raycast, batch_sphere_cast, capsule_cast, overlap_aabb, overlap_aabb_bvh,
815        overlap_sphere, overlap_sphere_bvh, sphere_cast, BatchRayQuery, OverlapResult,
816        ShapeCastHit,
817    };
818    pub use crate::raycast::{
819        raycast_all_aabbs, raycast_all_spheres, raycast_any_aabbs, raycast_any_spheres, Ray, RayHit,
820    };
821    pub use crate::rng::DeterministicRng;
822    pub use crate::rope::{Rope, RopeConfig};
823    pub use crate::rope_attach::{solve_rope_attachments, RopeAttachment};
824    #[cfg(feature = "std")]
825    pub use crate::scene_io::{
826        load_scene, load_scene_json, save_scene, save_scene_json, PhysicsScene, SerializedBody,
827        SerializedJoint,
828    };
829    #[cfg(feature = "std")]
830    pub use crate::sdf_adaptive::{AdaptiveConfig, AdaptiveSdfEvaluator};
831    pub use crate::sdf_ccd::SdfCcdConfig;
832    pub use crate::sdf_collider::{ClosureSdf, SdfCollider, SdfContact, SdfField};
833    #[cfg(feature = "std")]
834    pub use crate::sdf_destruction::{DestructibleSdf, DestructionShape};
835    pub use crate::sdf_force::{SdfForceField, SdfForceType};
836    pub use crate::sdf_manifold::ManifoldConfig;
837    #[cfg(feature = "std")]
838    pub use crate::sdf_manifold::SdfManifold;
839    #[cfg(feature = "std")]
840    pub use crate::sim_field::{ScalarField3D, VectorField3D};
841    #[cfg(feature = "std")]
842    pub use crate::sim_modifier::{ModifiedSdf, PhysicsModifier, SingleModifiedSdf};
843    #[cfg(feature = "std")]
844    pub use crate::sketch::{
845        CountMinSketch, DDSketch, FnvHasher, HeavyHitters, HyperLogLog, Mergeable,
846    };
847    pub use crate::sleeping::{Island, IslandManager, SleepConfig, SleepData, SleepState};
848    pub use crate::soft_body_cut::{cut_cloth, cut_deformable, CutPlane, CutResult};
849    #[cfg(feature = "std")]
850    pub use crate::solver::ContactModifier;
851    #[cfg(feature = "std")]
852    pub use crate::solver::TgsCacheStats;
853    pub use crate::solver::{
854        BodyType, ContactConstraint, DistanceConstraint, LawCheck, PhysicsConfig, PhysicsWorld,
855        RigidBody, SolverBackend, StageWork, WorldCcdConfig, WorldSnapshotError,
856    };
857    pub use crate::spatial::SpatialGrid;
858    #[cfg(feature = "std")]
859    pub use crate::thermal::{HeatSource, ThermalConfig, ThermalModifier};
860    pub use crate::torus::Torus;
861    pub use crate::trimesh::{TriMesh, Triangle};
862    pub use crate::vehicle::{Vehicle, VehicleConfig};
863    pub use crate::wedge::Wedge;
864}
865
866#[cfg(all(test, feature = "std"))]
867mod compile_smoke_tests {
868    //! Verify that core types from the prelude and key modules are accessible.
869    //! These tests catch accidental breakage of public re-exports.
870
871    use super::*;
872
873    #[test]
874    fn test_prelude_types_accessible() {
875        let _ = Fix128::ZERO;
876        let _ = Vec3Fix::ZERO;
877        let _ = QuatFix::IDENTITY;
878        let _ = Mat3Fix::IDENTITY;
879        let _ = PhysicsConfig::default();
880        let _ = CollisionFilter::default();
881        let _ = SleepConfig::default();
882    }
883
884    #[test]
885    fn test_error_type_accessible() {
886        let e = PhysicsError::InvalidBodyIndex { index: 0, count: 0 };
887        let _ = format!("{e}");
888    }
889
890    #[test]
891    fn test_spatial_grid_accessible() {
892        let mut grid = SpatialGrid::new(Fix128::ONE, 16);
893        grid.insert(0, Vec3Fix::ZERO);
894        grid.build();
895        let mut neighbors = Vec::new();
896        grid.query_neighbors_into(Vec3Fix::ZERO, Fix128::ONE, &mut neighbors);
897        assert!(neighbors.contains(&0));
898    }
899
900    #[test]
901    fn test_wasm_ffi_mutual_exclusion() {
902        // The compile_error! macros in lib.rs guarantee that `wasm` + `ffi`
903        // cannot be enabled together. This test documents the invariant.
904        // If this crate compiles at all, the exclusion is enforced.
905        // The compile_error! macros above enforce this at compile time.
906        // If this test runs, the invariant holds.
907    }
908}
909
910#[cfg(all(test, feature = "std"))]
911mod tests {
912    use super::*;
913
914    #[test]
915    fn test_basic_simulation() {
916        // Create world with default config
917        let config = PhysicsConfig::default();
918        let mut world = PhysicsWorld::new(config);
919
920        // Add a falling body
921        let mut body = RigidBody::new_dynamic(Vec3Fix::from_int(0, 100, 0), Fix128::ONE);
922        body.restitution = Fix128::from_ratio(5, 10); // 0.5 bounce
923
924        let body_id = world.add_body(body);
925
926        // Add ground (static body)
927        let ground = RigidBody::new_static(Vec3Fix::ZERO);
928        world.add_body(ground);
929
930        // Step simulation
931        let dt = Fix128::from_ratio(1, 60);
932        for _ in 0..60 {
933            world.step(dt);
934        }
935
936        // Body should have fallen
937        let pos = world.bodies[body_id].position;
938        assert!(pos.y < Fix128::from_int(100), "Body should have fallen");
939    }
940
941    #[test]
942    fn test_determinism() {
943        // Run simulation twice with same initial conditions
944        fn run_simulation() -> Vec3Fix {
945            let config = PhysicsConfig::default();
946            let mut world = PhysicsWorld::new(config);
947
948            let body = RigidBody::new_dynamic(
949                Vec3Fix::from_int(5, 50, 3),
950                Fix128::from_ratio(3, 2), // mass = 1.5
951            );
952            world.add_body(body);
953
954            let dt = Fix128::from_ratio(1, 60);
955            for _ in 0..120 {
956                world.step(dt);
957            }
958
959            world.bodies[0].position
960        }
961
962        let result1 = run_simulation();
963        let result2 = run_simulation();
964
965        // Results must be bit-exact
966        assert_eq!(result1.x.hi, result2.x.hi);
967        assert_eq!(result1.x.lo, result2.x.lo);
968        assert_eq!(result1.y.hi, result2.y.hi);
969        assert_eq!(result1.y.lo, result2.y.lo);
970        assert_eq!(result1.z.hi, result2.z.hi);
971        assert_eq!(result1.z.lo, result2.z.lo);
972    }
973
974    #[test]
975    fn test_state_serialization() {
976        let config = PhysicsConfig::default();
977        let mut world = PhysicsWorld::new(config);
978
979        // Add body and simulate
980        let body = RigidBody::new_dynamic(Vec3Fix::from_int(10, 20, 30), Fix128::ONE);
981        world.add_body(body);
982
983        let dt = Fix128::from_ratio(1, 60);
984        for _ in 0..30 {
985            world.step(dt);
986        }
987
988        // Serialize state
989        let state = world.serialize_state();
990
991        // Continue simulation
992        for _ in 0..30 {
993            world.step(dt);
994        }
995        let pos_after = world.bodies[0].position;
996
997        // Restore state
998        world.deserialize_state(&state);
999
1000        // Continue simulation from restored state
1001        for _ in 0..30 {
1002            world.step(dt);
1003        }
1004        let pos_restored = world.bodies[0].position;
1005
1006        // Results must be identical
1007        assert_eq!(pos_after.x.hi, pos_restored.x.hi);
1008        assert_eq!(pos_after.y.hi, pos_restored.y.hi);
1009        assert_eq!(pos_after.z.hi, pos_restored.z.hi);
1010    }
1011
1012    #[test]
1013    fn test_distance_constraint() {
1014        let config = PhysicsConfig::default();
1015        let mut world = PhysicsWorld::new(config);
1016
1017        // Two bodies connected by distance constraint
1018        let body_a = RigidBody::new_dynamic(Vec3Fix::from_int(0, 10, 0), Fix128::ONE);
1019        let body_b = RigidBody::new_dynamic(Vec3Fix::from_int(5, 10, 0), Fix128::ONE);
1020        let id_a = world.add_body(body_a);
1021        let id_b = world.add_body(body_b);
1022
1023        // Target distance = 5 units
1024        let constraint = DistanceConstraint {
1025            body_a: id_a,
1026            body_b: id_b,
1027            local_anchor_a: Vec3Fix::ZERO,
1028            local_anchor_b: Vec3Fix::ZERO,
1029            target_distance: Fix128::from_int(5),
1030            compliance: Fix128::ZERO,
1031            cached_lambda: Fix128::ZERO,
1032        };
1033        world.add_distance_constraint(constraint);
1034
1035        // Simulate at 120 Hz for stricter convergence
1036        let dt = Fix128::from_ratio(1, 120);
1037        for _ in 0..120 {
1038            world.step(dt);
1039        }
1040
1041        // Distance should remain approximately 5
1042        let pos_a = world.bodies[id_a].position;
1043        let pos_b = world.bodies[id_b].position;
1044        let dist = (pos_b - pos_a).length();
1045
1046        let target = Fix128::from_int(5);
1047        let error = if dist > target {
1048            dist - target
1049        } else {
1050            target - dist
1051        };
1052
1053        // Allow at most 2% error (target=5, tolerance=0.1) under gravity
1054        let tolerance = Fix128::from_ratio(1, 10);
1055        assert!(
1056            error < tolerance,
1057            "Distance constraint violated: error = {error:?}, tolerance = {tolerance:?}"
1058        );
1059    }
1060}