1#![deny(missing_docs)]
207#![allow(
216 clippy::cast_possible_truncation,
217 clippy::cast_precision_loss,
218 clippy::cast_sign_loss,
219 clippy::cast_lossless,
220 clippy::cast_possible_wrap,
221 clippy::similar_names,
222 clippy::many_single_char_names,
223 clippy::module_name_repetitions,
224 clippy::unreadable_literal,
225 clippy::large_stack_arrays,
226 clippy::doc_markdown
227)]
228#![cfg_attr(not(feature = "std"), no_std)]
229
230#[cfg(all(feature = "wasm", feature = "ffi"))]
231compile_error!("Features `wasm` and `ffi` are mutually exclusive. Enable only one.");
232
233#[cfg(all(feature = "wasm", not(feature = "std")))]
234compile_error!("Feature `wasm` requires `std`.");
235
236#[cfg(not(feature = "std"))]
237extern crate alloc;
238
239#[cfg(test)]
243extern crate self as alice_physics;
244
245pub mod acoustic_wave;
246pub mod aeroelasticity;
247#[cfg(feature = "analytics")]
248pub mod analytics_bridge;
249pub mod animation_blend;
250pub mod anisotropic;
251pub mod anisotropic_friction;
252#[cfg(feature = "std")]
253pub mod anomaly;
254pub mod articulation;
255pub mod atmosphere;
256pub mod audio_physics;
257pub mod beam_stress;
258pub mod bimaterial;
259pub mod box_collider;
260#[cfg(feature = "std")]
261pub mod bridging;
262pub mod buckling;
263pub mod buoyancy_zone;
264pub mod bvh;
265pub mod ccd;
266#[cfg(feature = "std")]
267pub mod cfd_solver;
268pub mod character;
269pub mod character_state;
270pub mod cloth;
271pub mod cloth_fluid;
272pub mod collider;
273pub mod collision_mesh_gen;
274pub mod compound;
275pub mod compressible;
276pub mod cone;
277pub mod contact_cache;
278pub mod contact_viz;
279#[cfg(feature = "std")]
280pub mod convex_decompose;
281pub mod convex_mesh_builder;
282pub mod coupled_field;
283pub mod coupled_iteration;
284pub mod coupling_medium;
285pub mod creep_longterm;
286pub mod crowd_force;
287pub mod cubic_elastic_fem;
288pub mod cylinder;
289pub mod damping_rayleigh;
290#[cfg(feature = "replay")]
291pub mod db_bridge;
292pub mod debug_render;
293pub mod deformable;
294pub mod det_math {
307 pub use alice_det_math::{acos64, asin64, round, round64, sin_cos, sqrt, sqrt64};
308
309 macro_rules! delegate {
310 ($(fn $name:ident($($arg:ident: $ty:ty),*) -> $ret:ty;)*) => {
311 $(
312 #[doc = concat!("Delegates to [`alice_det_math::", stringify!($name), "`].")]
313 #[inline]
314 #[must_use]
315 pub fn $name($($arg: $ty),*) -> $ret {
316 alice_det_math::$name($($arg),*)
317 }
318 )*
319 };
320 }
321
322 delegate! {
323 fn sin(x: f32) -> f32;
324 fn cos(x: f32) -> f32;
325 fn exp(x: f32) -> f32;
326 fn ln(x: f32) -> f32;
327 fn powf(x: f32, y: f32) -> f32;
328 fn powi(x: f32, n: i32) -> f32;
329 fn cbrt(x: f32) -> f32;
330 fn hypot(x: f32, y: f32) -> f32;
331 fn atan(x: f32) -> f32;
332 fn atan2(y: f32, x: f32) -> f32;
333 fn asin(x: f32) -> f32;
334 fn acos(x: f32) -> f32;
335 fn tan(x: f32) -> f32;
336 fn tanh(x: f32) -> f32;
337 fn atan64(x: f64) -> f64;
338 fn atan2_64(y: f64, x: f64) -> f64;
339 fn exp64(x: f64) -> f64;
340 fn ln64(x: f64) -> f64;
341 fn powf64(x: f64, y: f64) -> f64;
342 }
343
344 #[inline]
360 #[must_use]
361 pub(crate) fn max(a: f32, b: f32) -> f32 {
362 if a.is_nan() || b > a {
363 b
364 } else if b.is_nan() || a > b || !a.is_sign_negative() {
365 a
366 } else {
367 b
368 }
369 }
370
371 #[inline]
374 #[must_use]
375 pub(crate) fn min(a: f32, b: f32) -> f32 {
376 if a.is_nan() || b < a {
377 b
378 } else if b.is_nan() || a < b || a.is_sign_negative() {
379 a
380 } else {
381 b
382 }
383 }
384
385 #[inline]
387 #[must_use]
388 pub(crate) fn max64(a: f64, b: f64) -> f64 {
389 if a.is_nan() || b > a {
390 b
391 } else if b.is_nan() || a > b || !a.is_sign_negative() {
392 a
393 } else {
394 b
395 }
396 }
397
398 #[inline]
400 #[must_use]
401 pub(crate) fn min64(a: f64, b: f64) -> f64 {
402 if a.is_nan() || b < a {
403 b
404 } else if b.is_nan() || a < b || a.is_sign_negative() {
405 a
406 } else {
407 b
408 }
409 }
410
411 #[cfg(test)]
412 mod max_min_tests {
413 use super::{max, max64, min, min64};
414
415 fn expected_max(a: f64, b: f64) -> f64 {
418 match (a.is_nan(), b.is_nan()) {
419 (true, _) => b,
420 (_, true) => a,
421 _ if a > b => a,
422 _ if b > a => b,
423 _ => {
425 if a != 0.0 {
426 a
427 } else if a.is_sign_negative() && b.is_sign_negative() {
428 -0.0
429 } else {
430 0.0
431 }
432 }
433 }
434 }
435
436 fn expected_min(a: f64, b: f64) -> f64 {
437 match (a.is_nan(), b.is_nan()) {
438 (true, _) => b,
439 (_, true) => a,
440 _ if a < b => a,
441 _ if b < a => b,
442 _ => {
443 if a == 0.0 && (a.is_sign_negative() || b.is_sign_negative()) {
444 -0.0
445 } else {
446 a
447 }
448 }
449 }
450 }
451
452 const LATTICE: [f64; 7] = [
453 f64::NEG_INFINITY,
454 -1.0,
455 -0.0,
456 0.0,
457 1.0,
458 f64::INFINITY,
459 f64::NAN,
460 ];
461
462 fn same(x: f64, y: f64) -> bool {
463 (x.is_nan() && y.is_nan()) || x.to_bits() == y.to_bits()
464 }
465
466 #[test]
467 fn max_and_min_follow_the_rule_on_the_signed_zero_lattice() {
468 let mut compared = 0;
469 for &a in &LATTICE {
470 for &b in &LATTICE {
471 let (x, y) = (core::hint::black_box(a), core::hint::black_box(b));
474 let (xf, yf) = (x as f32, y as f32);
475 assert!(
476 same(max64(x, y), expected_max(a, b)),
477 "max64({a:?}, {b:?}) = {:?}",
478 max64(x, y)
479 );
480 assert!(
481 same(min64(x, y), expected_min(a, b)),
482 "min64({a:?}, {b:?}) = {:?}",
483 min64(x, y)
484 );
485 assert!(
486 same(f64::from(max(xf, yf)), expected_max(a, b)),
487 "max({a:?}, {b:?}) = {:?}",
488 max(xf, yf)
489 );
490 assert!(
491 same(f64::from(min(xf, yf)), expected_min(a, b)),
492 "min({a:?}, {b:?}) = {:?}",
493 min(xf, yf)
494 );
495 compared += 4;
496 }
497 }
498 assert_eq!(compared, 4 * 49);
499 }
500
501 #[test]
502 fn the_zero_pair_has_the_documented_sign_in_both_orders() {
503 let (p, n) = (
504 core::hint::black_box(0.0f32),
505 core::hint::black_box(-0.0f32),
506 );
507 assert_eq!(max(p, n).to_bits(), 0x0000_0000);
508 assert_eq!(max(n, p).to_bits(), 0x0000_0000);
509 assert_eq!(min(p, n).to_bits(), 0x8000_0000);
510 assert_eq!(min(n, p).to_bits(), 0x8000_0000);
511 let (p, n) = (
512 core::hint::black_box(0.0f64),
513 core::hint::black_box(-0.0f64),
514 );
515 assert_eq!(max64(p, n).to_bits(), 0);
516 assert_eq!(max64(n, p).to_bits(), 0);
517 assert_eq!(min64(p, n).to_bits(), 0x8000_0000_0000_0000);
518 assert_eq!(min64(n, p).to_bits(), 0x8000_0000_0000_0000);
519 }
520 }
521}
522#[cfg(any(feature = "ffi", feature = "wasm", feature = "python"))]
523mod binding_api;
524pub mod dynamic_bvh;
525#[cfg(feature = "std")]
526pub mod dynamic_fem;
527pub mod electromagnetic;
528pub mod ellipsoid;
529#[cfg(feature = "std")]
530pub mod erosion;
531pub mod error;
532pub mod euler_fv;
533pub mod eulerian_grid;
534pub mod event;
535pub mod fatigue;
536#[cfg(feature = "ffi")]
537pub mod ffi;
538pub mod filament_db;
539
540pub(crate) mod body_collider;
541pub mod fillet_stress;
542pub mod filter;
543#[cfg(feature = "std")]
544pub mod flow_viz;
545pub mod fluid;
546#[cfg(feature = "std")]
547pub mod fluid_netcode;
548pub mod force;
549#[cfg(feature = "std")]
550pub mod fracture;
551#[cfg(feature = "std")]
552pub mod fsi_advanced;
553#[cfg(feature = "std")]
554pub mod gpu_sdf;
555pub mod heatmap;
556pub mod heightfield;
557pub mod hyperelastic;
558#[cfg(feature = "std")]
559pub mod ik_physics_bridge;
560pub mod interface_capture;
561pub mod interpolation;
562pub mod joint;
563pub mod joint_extra;
564pub mod kepler;
565pub mod kinematic_loop;
566#[cfg(feature = "std")]
567pub mod laminate;
568pub mod laminate_failure;
569pub mod law_id;
571pub mod layer_adhesion;
572pub mod lift_drag;
573#[cfg(feature = "std")]
581pub mod linear_elastic_fem;
582pub mod linear_solver;
583pub mod mass_properties;
584pub mod material;
585pub mod math;
586pub mod math_util;
587pub mod maxwell_fdtd;
588pub mod metric;
589pub mod modal;
590pub mod molecular_dynamics;
591pub mod motor;
592pub mod multi_world;
593pub mod multiphase;
594pub mod nbody;
595pub mod netcode;
596#[cfg(feature = "std")]
597pub mod netcode_prediction;
598#[cfg(feature = "neural")]
599pub mod neural;
600pub mod non_newtonian;
601pub mod pair_potential;
602pub mod particle;
603#[cfg(feature = "std")]
604pub mod phase_change;
605#[cfg(feature = "std")]
606pub mod piezoelectric;
607#[cfg(feature = "std")]
608pub mod pipeline;
609pub mod plane_collider;
610pub mod plastic;
611#[cfg(feature = "std")]
612pub mod pressure;
613pub mod prestressed;
614pub mod print_orientation;
615#[cfg(feature = "std")]
616pub mod print_pipeline_solver;
617#[cfg(feature = "std")]
618pub mod privacy;
619pub mod profiling;
620#[cfg(feature = "python")]
621mod python;
622pub mod quadratic_elastic_fem;
623pub mod query;
624pub mod ragdoll;
625pub mod raycast;
626#[cfg(feature = "replay")]
627pub mod replay;
628pub mod rng;
629#[cfg(feature = "std")]
630pub mod rolling_contact;
631pub mod rope;
632pub mod rope_attach;
633pub mod rotor;
634#[cfg(feature = "std")]
635pub mod scene_io;
636#[cfg(feature = "std")]
637pub mod sdf_adaptive;
638pub mod sdf_ccd;
639pub mod sdf_character;
640pub mod sdf_collider;
641#[cfg(feature = "std")]
642pub mod sdf_destruction;
643#[cfg(feature = "std")]
644pub mod sdf_fem_mesh;
645pub mod sdf_force;
646pub mod sdf_manifold;
647#[cfg(feature = "std")]
648pub mod sdf_sph;
649pub mod sdf_wind_field;
650pub mod semantics;
652pub mod sensors;
653pub mod shape;
654pub mod shape_raycast;
655#[cfg(feature = "std")]
656pub mod sim_field;
657#[cfg(feature = "std")]
658pub mod sim_modifier;
659pub mod static_collider;
660pub mod structural_solver;
661#[cfg(feature = "std")]
662pub mod support_volume;
663pub mod surface_tension_csf;
664#[cfg(feature = "std")]
665pub mod thin_wall;
666pub mod vibration_wall;
667pub mod warp_risk;
668pub mod wave_ship;
669pub mod wind_zone;
670pub mod world_character;
671pub mod world_participant;
672pub mod world_shape_query;
673
674pub mod physics2d;
675
676#[cfg(feature = "gpu-solver-bridge")]
677pub mod gpu_bridge;
678#[cfg(feature = "std")]
679pub mod sketch;
680pub mod sleeping;
681pub mod smoke_fire;
682pub mod soft_body_cut;
683pub mod solver;
684pub(crate) mod gyroscopic;
686#[cfg(feature = "std")]
691pub(crate) mod det_hash;
692#[cfg(feature = "std")]
693pub(crate) mod solver_tgs;
694#[cfg(feature = "std")]
695pub(crate) mod solver_tgs_hooks_6dof_oriented;
696#[cfg(feature = "std")]
697pub(crate) mod solver_tgs_hooks_6dof_oriented_scoped;
698#[cfg(feature = "std")]
703pub(crate) mod solver_tgs_backend;
704pub mod spatial;
705pub mod spherical_terrain;
706#[cfg(feature = "std")]
707pub mod thermal;
708pub mod thermal_stress;
709pub mod torus;
710#[cfg(feature = "std")]
711pub mod transient_thermal;
712pub mod trimesh;
713pub mod turbulence;
714pub mod vehicle;
715pub mod vehicle_dynamics;
716#[cfg(feature = "wasm")]
717mod wasm;
718pub mod wedge;
719
720pub use animation_blend::{AnimationBlender, AnimationClip, BlendMode, SkeletonPose};
722#[cfg(feature = "std")]
723pub use anomaly::{
724 AnomalyCallback, AnomalyEvent, CompositeDetector, EwmaDetector, MadDetector, StreamingMedian,
725 ZScoreDetector,
726};
727pub use articulation::{ArticulatedBody, FeatherstoneSolver};
728pub use audio_physics::{AudioConfig, AudioEvent, AudioGenerator, AudioMaterial};
729pub use box_collider::OrientedBox;
730pub use bvh::{BvhNode, BvhPrimitive, LinearBvh};
731pub use ccd::{
732 aabb_plane_toi, capsule_plane_toi, speculative_contact, sphere_capsule_toi, CcdConfig,
733};
734pub use character::{CharacterConfig, CharacterController, MoveResult, PushImpulse};
735pub use cloth::{Cloth, ClothConfig};
736pub use cloth_fluid::{
737 apply_cloth_boundary_to_fluid, apply_fluid_forces_to_cloth, ClothFluidCoupling,
738};
739pub use collider::{Capsule, CollisionResult, ConvexHull, ScaledShape, Sphere, Support, AABB};
740pub use collision_mesh_gen::{
741 compute_mesh_aabb, generate_collision_mesh, simplify_collision_mesh, CollisionMesh,
742 CollisionMeshConfig,
743};
744#[cfg(feature = "std")]
745pub use compound::{CompoundShape, ShapeRef};
746pub use cone::Cone;
747pub use contact_cache::{BodyPairKey, ContactCache, ContactManifold};
748pub use contact_viz::{
749 generate_contact_arrows, generate_friction_cones, ContactArrow, FrictionCone,
750};
751#[cfg(feature = "std")]
752pub use convex_decompose::{DecomposeConfig, DecompositionResult};
753pub use convex_mesh_builder::{build_convex_hull, compute_centroid};
754pub use cylinder::Cylinder;
755pub use debug_render::{debug_draw_world, DebugColor, DebugDrawData, DebugDrawFlags};
756pub use deformable::{DeformableBody, DeformableConfig};
757pub use dynamic_bvh::DynamicAabbTree;
758pub use ellipsoid::Ellipsoid;
759#[cfg(feature = "std")]
760pub use erosion::{ErosionConfig, ErosionModifier, ErosionType};
761pub use error::PhysicsError;
762pub use event::{ContactEvent, ContactEventType, EventCollector, JointBreakEvent, TriggerEvent};
763pub use filter::layers;
765pub use filter::CollisionFilter;
766#[cfg(feature = "std")]
767pub use flow_viz::{
768 generate_flow_arrows, generate_streamlines, FlowArrow, FlowVizConfig, Streamlines,
769};
770pub use fluid::{Fluid, FluidConfig};
771#[cfg(feature = "std")]
772pub use fluid_netcode::{FluidDelta, FluidSnapshot};
773pub use force::{apply_force_fields, ForceField, ForceFieldInstance};
774#[cfg(feature = "std")]
775pub use fracture::{Crack, FractureConfig, FractureModifier};
776#[cfg(feature = "std")]
777pub use gpu_sdf::{
778 batch_size, GpuDispatchConfig, GpuSdfBatch, GpuSdfInstancedBatch, GpuSdfMultiDispatch,
779 GpuSdfQuery, GpuSdfResult,
780};
781pub use heatmap::{
782 generate_stress_heatmap, generate_temperature_heatmap, heatmap_to_rgba, Heatmap, HeatmapConfig,
783 SliceAxis,
784};
785pub use heightfield::HeightField;
786pub use interpolation::{BodySnapshot, InterpolationState, WorldSnapshot};
787#[allow(deprecated)] pub use joint::{
789 solve_joints, solve_joints_breakable, solve_joints_breaking_on_force,
790 solve_joints_with_reaction_forces,
791};
792pub use joint::{
793 BallJoint, ConeTwistJoint, D6Joint, D6Motion, FixedJoint, HingeJoint, Joint, JointType,
794 SliderJoint, SpringJoint,
795};
796pub use joint_extra::{
797 solve_extra_joints, ExtraJoint, GearJoint, MouseJoint, PulleyJoint, RackAndPinionJoint,
798 WeldJoint,
799};
800pub use mass_properties::{
801 box_mass_properties, capsule_mass_properties, convex_hull_mass_properties,
802 cylinder_mass_properties, sphere_mass_properties, translate_inertia, MassProperties,
803};
804pub use material::{CombineRule, CombinedMaterial, MaterialId, MaterialTable, PhysicsMaterial};
805pub use math::{simd_width, Fix128, Mat3Fix, QuatFix, Vec3Fix, SIMD_WIDTH};
806pub use motor::{JointMotor, MotorMode, PdController};
807pub use multi_world::{MultiWorld, Portal};
808pub use netcode::{
809 DeterministicSimulation, FrameInput, InputApplicator, NetcodeConfig, SimulationChecksum,
810 SimulationSnapshot,
811};
812pub use particle::{LandingEvent, LandingTarget, Particle, ParticleEmitter, ParticleSystem};
813#[cfg(feature = "std")]
814pub use phase_change::{Phase, PhaseChangeConfig, PhaseChangeModifier};
815pub use physics2d::{
816 BodyType2D, Contact2D, Joint2D, PhysicsConfig2D, PhysicsWorld2D, RigidBody2D, Shape2D, Vec2Fix,
817};
818#[cfg(feature = "std")]
819pub use pipeline::{
820 MetricEvent, MetricPipeline, MetricRegistry, MetricSnapshot, MetricType, RingBuffer,
821};
822pub use plane_collider::PlaneCollider;
823#[cfg(feature = "std")]
824pub use pressure::{PressureConfig, PressureModifier};
825#[cfg(feature = "privacy-crypto")]
826pub use privacy::KeyedRappor;
827#[cfg(feature = "std")]
828#[allow(deprecated)] pub use privacy::{
830 LaplaceNoise, PrivacyBudget, PrivateAggregator, RandomizedResponse, Rappor, XorShift64,
831};
832pub use profiling::{PhysicsProfiler, ProfileEntry, StepStats};
833pub use query::{
834 batch_raycast, batch_sphere_cast, capsule_cast, overlap_aabb, overlap_aabb_bvh, overlap_sphere,
835 overlap_sphere_bvh, sphere_cast, BatchRayQuery, OverlapResult, ShapeCastHit,
836};
837pub use raycast::{
838 raycast_all_aabbs, raycast_all_spheres, raycast_any_aabbs, raycast_any_spheres, Ray, RayHit,
839};
840pub use rng::DeterministicRng;
841pub use rope::{Rope, RopeConfig};
842pub use rope_attach::{solve_rope_attachments, RopeAttachment};
843#[cfg(feature = "std")]
844pub use scene_io::{
845 load_scene, load_scene_json, save_scene, save_scene_json, PhysicsScene, SerializedBody,
846 SerializedJoint,
847};
848#[cfg(feature = "std")]
849pub use sdf_adaptive::{AdaptiveConfig, AdaptiveSdfEvaluator};
850pub use sdf_ccd::SdfCcdConfig;
851pub use sdf_collider::{ClosureSdf, SdfCollider, SdfContact, SdfField};
852#[cfg(feature = "std")]
853pub use sdf_destruction::{DestructibleSdf, DestructionShape};
854pub use sdf_force::{SdfForceField, SdfForceType};
855pub use sdf_manifold::ManifoldConfig;
856#[cfg(feature = "std")]
857pub use sdf_manifold::SdfManifold;
858pub use semantics::{PHYSICS_SEMANTICS_ID, PHYSICS_SEMANTICS_PINS};
859pub use shape::{Shape, ShapeError};
860#[cfg(feature = "std")]
861pub use sim_field::{ScalarField3D, VectorField3D};
862#[cfg(feature = "std")]
863pub use sim_modifier::{ModifiedSdf, PhysicsModifier, SingleModifiedSdf};
864#[cfg(feature = "std")]
865pub use sketch::{CountMinSketch, DDSketch, FnvHasher, HeavyHitters, HyperLogLog, Mergeable};
866pub use sleeping::{Island, IslandManager, SleepConfig, SleepData, SleepState};
867pub use soft_body_cut::{cut_cloth, cut_deformable, CutPlane, CutResult};
868#[cfg(feature = "std")]
869pub use solver::ContactModifier;
870#[cfg(feature = "std")]
871pub use solver::TgsCacheStats;
872pub use solver::{
873 BodyType, ContactConstraint, DistanceConstraint, LawCheck, PhysicsConfig, PhysicsWorld,
874 RigidBody, SolverBackend, StageWork, WorldCcdConfig, WorldSnapshotError,
875};
876pub use spatial::SpatialGrid;
877#[cfg(feature = "std")]
878pub use thermal::{HeatSource, ThermalConfig, ThermalModifier};
879pub use torus::Torus;
880pub use trimesh::{TriMesh, Triangle};
881pub use vehicle::{Vehicle, VehicleConfig};
882pub use wedge::Wedge;
883
884pub mod prelude {
886 pub use crate::animation_blend::{AnimationBlender, AnimationClip, BlendMode, SkeletonPose};
887 #[cfg(feature = "std")]
888 pub use crate::anomaly::{
889 AnomalyCallback, AnomalyEvent, CompositeDetector, EwmaDetector, MadDetector,
890 StreamingMedian, ZScoreDetector,
891 };
892 pub use crate::articulation::{ArticulatedBody, FeatherstoneSolver};
893 pub use crate::audio_physics::{AudioConfig, AudioEvent, AudioGenerator, AudioMaterial};
894 pub use crate::box_collider::OrientedBox;
895 pub use crate::bvh::{BvhNode, BvhPrimitive, LinearBvh};
896 pub use crate::ccd::{speculative_contact, CcdConfig};
897 pub use crate::character::{CharacterConfig, CharacterController, MoveResult, PushImpulse};
898 pub use crate::cloth::{Cloth, ClothConfig};
899 pub use crate::cloth_fluid::{
900 apply_cloth_boundary_to_fluid, apply_fluid_forces_to_cloth, ClothFluidCoupling,
901 };
902 pub use crate::collider::{
903 Capsule, CollisionResult, ConvexHull, ScaledShape, Sphere, Support, AABB,
904 };
905 pub use crate::collision_mesh_gen::{
906 compute_mesh_aabb, generate_collision_mesh, simplify_collision_mesh, CollisionMesh,
907 CollisionMeshConfig,
908 };
909 #[cfg(feature = "std")]
910 pub use crate::compound::{CompoundShape, ShapeRef};
911 pub use crate::cone::Cone;
912 pub use crate::contact_cache::{BodyPairKey, ContactCache, ContactManifold};
913 pub use crate::contact_viz::{
914 generate_contact_arrows, generate_friction_cones, ContactArrow, FrictionCone,
915 };
916 #[cfg(feature = "std")]
917 pub use crate::convex_decompose::{DecomposeConfig, DecompositionResult};
918 pub use crate::convex_mesh_builder::{build_convex_hull, compute_centroid};
919 pub use crate::cylinder::Cylinder;
920 pub use crate::debug_render::{debug_draw_world, DebugColor, DebugDrawData, DebugDrawFlags};
921 pub use crate::deformable::{DeformableBody, DeformableConfig};
922 pub use crate::dynamic_bvh::DynamicAabbTree;
923 pub use crate::ellipsoid::Ellipsoid;
924 #[cfg(feature = "std")]
925 pub use crate::erosion::{ErosionConfig, ErosionModifier, ErosionType};
926 pub use crate::error::PhysicsError;
927 pub use crate::event::{
928 ContactEvent, ContactEventType, EventCollector, JointBreakEvent, TriggerEvent,
929 };
930 pub use crate::filter::layers;
931 pub use crate::filter::CollisionFilter;
932 #[cfg(feature = "std")]
933 pub use crate::flow_viz::{
934 generate_flow_arrows, generate_streamlines, FlowArrow, FlowVizConfig, Streamlines,
935 };
936 pub use crate::fluid::{Fluid, FluidConfig};
937 #[cfg(feature = "std")]
938 pub use crate::fluid_netcode::{FluidDelta, FluidSnapshot};
939 pub use crate::force::{apply_force_fields, ForceField, ForceFieldInstance};
940 #[cfg(feature = "std")]
941 pub use crate::fracture::{Crack, FractureConfig, FractureModifier};
942 #[cfg(feature = "std")]
943 pub use crate::gpu_sdf::{
944 batch_size, GpuDispatchConfig, GpuSdfBatch, GpuSdfInstancedBatch, GpuSdfMultiDispatch,
945 GpuSdfQuery, GpuSdfResult,
946 };
947 pub use crate::heatmap::{
948 generate_stress_heatmap, generate_temperature_heatmap, heatmap_to_rgba, Heatmap,
949 HeatmapConfig, SliceAxis,
950 };
951 pub use crate::heightfield::HeightField;
952 pub use crate::interpolation::{BodySnapshot, InterpolationState, WorldSnapshot};
953 #[allow(deprecated)] pub use crate::joint::{
955 solve_joints, solve_joints_breakable, solve_joints_breaking_on_force,
956 solve_joints_with_reaction_forces,
957 };
958 pub use crate::joint::{
959 BallJoint, ConeTwistJoint, D6Joint, D6Motion, FixedJoint, HingeJoint, Joint, JointType,
960 SliderJoint, SpringJoint,
961 };
962 pub use crate::joint_extra::{
963 solve_extra_joints, ExtraJoint, GearJoint, MouseJoint, PulleyJoint, RackAndPinionJoint,
964 WeldJoint,
965 };
966 pub use crate::mass_properties::{
967 box_mass_properties, capsule_mass_properties, convex_hull_mass_properties,
968 cylinder_mass_properties, sphere_mass_properties, translate_inertia, MassProperties,
969 };
970 pub use crate::material::{
971 CombineRule, CombinedMaterial, MaterialId, MaterialTable, PhysicsMaterial,
972 };
973 pub use crate::math::{simd_width, Fix128, Mat3Fix, QuatFix, Vec3Fix, SIMD_WIDTH};
974 pub use crate::motor::{JointMotor, MotorMode, PdController};
975 pub use crate::multi_world::{MultiWorld, Portal};
976 pub use crate::netcode::{
977 DeterministicSimulation, FrameInput, InputApplicator, NetcodeConfig, SimulationChecksum,
978 SimulationSnapshot,
979 };
980 pub use crate::particle::{
981 LandingEvent, LandingTarget, Particle, ParticleEmitter, ParticleSystem,
982 };
983 #[cfg(feature = "std")]
984 pub use crate::phase_change::{Phase, PhaseChangeConfig, PhaseChangeModifier};
985 #[cfg(feature = "std")]
986 pub use crate::pipeline::{
987 MetricEvent, MetricPipeline, MetricRegistry, MetricSnapshot, MetricType, RingBuffer,
988 };
989 pub use crate::plane_collider::PlaneCollider;
990 #[cfg(feature = "std")]
991 pub use crate::pressure::{PressureConfig, PressureModifier};
992 #[cfg(feature = "privacy-crypto")]
993 pub use crate::privacy::KeyedRappor;
994 #[cfg(feature = "std")]
995 #[allow(deprecated)] pub use crate::privacy::{
997 LaplaceNoise, PrivacyBudget, PrivateAggregator, RandomizedResponse, Rappor, XorShift64,
998 };
999 pub use crate::profiling::{PhysicsProfiler, ProfileEntry, StepStats};
1000 pub use crate::query::{
1001 batch_raycast, batch_sphere_cast, capsule_cast, overlap_aabb, overlap_aabb_bvh,
1002 overlap_sphere, overlap_sphere_bvh, sphere_cast, BatchRayQuery, OverlapResult,
1003 ShapeCastHit,
1004 };
1005 pub use crate::raycast::{
1006 raycast_all_aabbs, raycast_all_spheres, raycast_any_aabbs, raycast_any_spheres, Ray, RayHit,
1007 };
1008 pub use crate::rng::DeterministicRng;
1009 pub use crate::rope::{Rope, RopeConfig};
1010 pub use crate::rope_attach::{solve_rope_attachments, RopeAttachment};
1011 #[cfg(feature = "std")]
1012 pub use crate::scene_io::{
1013 load_scene, load_scene_json, save_scene, save_scene_json, PhysicsScene, SerializedBody,
1014 SerializedJoint,
1015 };
1016 #[cfg(feature = "std")]
1017 pub use crate::sdf_adaptive::{AdaptiveConfig, AdaptiveSdfEvaluator};
1018 pub use crate::sdf_ccd::SdfCcdConfig;
1019 pub use crate::sdf_collider::{ClosureSdf, SdfCollider, SdfContact, SdfField};
1020 #[cfg(feature = "std")]
1021 pub use crate::sdf_destruction::{DestructibleSdf, DestructionShape};
1022 pub use crate::sdf_force::{SdfForceField, SdfForceType};
1023 pub use crate::sdf_manifold::ManifoldConfig;
1024 #[cfg(feature = "std")]
1025 pub use crate::sdf_manifold::SdfManifold;
1026 #[cfg(feature = "std")]
1027 pub use crate::sim_field::{ScalarField3D, VectorField3D};
1028 #[cfg(feature = "std")]
1029 pub use crate::sim_modifier::{ModifiedSdf, PhysicsModifier, SingleModifiedSdf};
1030 #[cfg(feature = "std")]
1031 pub use crate::sketch::{
1032 CountMinSketch, DDSketch, FnvHasher, HeavyHitters, HyperLogLog, Mergeable,
1033 };
1034 pub use crate::sleeping::{Island, IslandManager, SleepConfig, SleepData, SleepState};
1035 pub use crate::soft_body_cut::{cut_cloth, cut_deformable, CutPlane, CutResult};
1036 #[cfg(feature = "std")]
1037 pub use crate::solver::ContactModifier;
1038 #[cfg(feature = "std")]
1039 pub use crate::solver::TgsCacheStats;
1040 pub use crate::solver::{
1041 BodyType, ContactConstraint, DistanceConstraint, LawCheck, PhysicsConfig, PhysicsWorld,
1042 RigidBody, SolverBackend, StageWork, WorldCcdConfig, WorldSnapshotError,
1043 };
1044 pub use crate::spatial::SpatialGrid;
1045 #[cfg(feature = "std")]
1046 pub use crate::thermal::{HeatSource, ThermalConfig, ThermalModifier};
1047 pub use crate::torus::Torus;
1048 pub use crate::trimesh::{TriMesh, Triangle};
1049 pub use crate::vehicle::{Vehicle, VehicleConfig};
1050 pub use crate::wedge::Wedge;
1051}
1052
1053#[cfg(all(test, feature = "std"))]
1054mod compile_smoke_tests {
1055 use super::*;
1059
1060 #[test]
1061 fn test_prelude_types_accessible() {
1062 let _ = Fix128::ZERO;
1063 let _ = Vec3Fix::ZERO;
1064 let _ = QuatFix::IDENTITY;
1065 let _ = Mat3Fix::IDENTITY;
1066 let _ = PhysicsConfig::default();
1067 let _ = CollisionFilter::default();
1068 let _ = SleepConfig::default();
1069 }
1070
1071 #[test]
1072 fn test_error_type_accessible() {
1073 let e = PhysicsError::InvalidBodyIndex { index: 0, count: 0 };
1074 let _ = format!("{e}");
1075 }
1076
1077 #[test]
1078 fn test_spatial_grid_accessible() {
1079 let mut grid = SpatialGrid::new(Fix128::ONE, 16);
1080 grid.insert(0, Vec3Fix::ZERO);
1081 grid.build();
1082 let mut neighbors = Vec::new();
1083 grid.query_neighbors_into(Vec3Fix::ZERO, Fix128::ONE, &mut neighbors);
1084 assert!(neighbors.contains(&0));
1085 }
1086
1087 #[test]
1088 fn test_wasm_ffi_mutual_exclusion() {
1089 }
1095}
1096
1097#[cfg(all(test, feature = "std"))]
1098mod tests {
1099 use super::*;
1100
1101 #[test]
1102 fn test_basic_simulation() {
1103 let config = PhysicsConfig::default();
1105 let mut world = PhysicsWorld::new(config);
1106
1107 let mut body = RigidBody::new_dynamic(Vec3Fix::from_int(0, 100, 0), Fix128::ONE);
1109 body.restitution = Fix128::from_ratio(5, 10); let body_id = world.add_body(body);
1112
1113 let ground = RigidBody::new_static(Vec3Fix::ZERO);
1115 world.add_body(ground);
1116
1117 let dt = Fix128::from_ratio(1, 60);
1119 for _ in 0..60 {
1120 world.step(dt);
1121 }
1122
1123 let pos = world.bodies[body_id].position;
1125 assert!(pos.y < Fix128::from_int(100), "Body should have fallen");
1126 }
1127
1128 #[test]
1129 fn test_determinism() {
1130 fn run_simulation() -> Vec3Fix {
1132 let config = PhysicsConfig::default();
1133 let mut world = PhysicsWorld::new(config);
1134
1135 let body = RigidBody::new_dynamic(
1136 Vec3Fix::from_int(5, 50, 3),
1137 Fix128::from_ratio(3, 2), );
1139 world.add_body(body);
1140
1141 let dt = Fix128::from_ratio(1, 60);
1142 for _ in 0..120 {
1143 world.step(dt);
1144 }
1145
1146 world.bodies[0].position
1147 }
1148
1149 let result1 = run_simulation();
1150 let result2 = run_simulation();
1151
1152 assert_eq!(result1.x.hi, result2.x.hi);
1154 assert_eq!(result1.x.lo, result2.x.lo);
1155 assert_eq!(result1.y.hi, result2.y.hi);
1156 assert_eq!(result1.y.lo, result2.y.lo);
1157 assert_eq!(result1.z.hi, result2.z.hi);
1158 assert_eq!(result1.z.lo, result2.z.lo);
1159 }
1160
1161 #[test]
1162 fn test_state_serialization() {
1163 let config = PhysicsConfig::default();
1164 let mut world = PhysicsWorld::new(config);
1165
1166 let body = RigidBody::new_dynamic(Vec3Fix::from_int(10, 20, 30), Fix128::ONE);
1168 world.add_body(body);
1169
1170 let dt = Fix128::from_ratio(1, 60);
1171 for _ in 0..30 {
1172 world.step(dt);
1173 }
1174
1175 let state = world.serialize_state();
1177
1178 for _ in 0..30 {
1180 world.step(dt);
1181 }
1182 let pos_after = world.bodies[0].position;
1183
1184 world.deserialize_state(&state);
1186
1187 for _ in 0..30 {
1189 world.step(dt);
1190 }
1191 let pos_restored = world.bodies[0].position;
1192
1193 assert_eq!(pos_after.x.hi, pos_restored.x.hi);
1195 assert_eq!(pos_after.y.hi, pos_restored.y.hi);
1196 assert_eq!(pos_after.z.hi, pos_restored.z.hi);
1197 }
1198
1199 #[test]
1200 fn test_distance_constraint() {
1201 let config = PhysicsConfig::default();
1202 let mut world = PhysicsWorld::new(config);
1203
1204 let body_a = RigidBody::new_dynamic(Vec3Fix::from_int(0, 10, 0), Fix128::ONE);
1206 let body_b = RigidBody::new_dynamic(Vec3Fix::from_int(5, 10, 0), Fix128::ONE);
1207 let id_a = world.add_body(body_a);
1208 let id_b = world.add_body(body_b);
1209
1210 let constraint = DistanceConstraint {
1212 body_a: id_a,
1213 body_b: id_b,
1214 local_anchor_a: Vec3Fix::ZERO,
1215 local_anchor_b: Vec3Fix::ZERO,
1216 target_distance: Fix128::from_int(5),
1217 compliance: Fix128::ZERO,
1218 cached_lambda: Fix128::ZERO,
1219 };
1220 world.add_distance_constraint(constraint);
1221
1222 let dt = Fix128::from_ratio(1, 120);
1224 for _ in 0..120 {
1225 world.step(dt);
1226 }
1227
1228 let pos_a = world.bodies[id_a].position;
1230 let pos_b = world.bodies[id_b].position;
1231 let dist = (pos_b - pos_a).length();
1232
1233 let target = Fix128::from_int(5);
1234 let error = if dist > target {
1235 dist - target
1236 } else {
1237 target - dist
1238 };
1239
1240 let tolerance = Fix128::from_ratio(1, 10);
1242 assert!(
1243 error < tolerance,
1244 "Distance constraint violated: error = {error:?}, tolerance = {tolerance:?}"
1245 );
1246 }
1247}