├── .circleci └── config.yml ├── .github └── FUNDING.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── build ├── nphysics2d │ └── Cargo.toml ├── nphysics3d │ └── Cargo.toml ├── nphysics_testbed2d │ └── Cargo.toml └── nphysics_testbed3d │ └── Cargo.toml ├── examples2d ├── Cargo.toml ├── all_examples2.rs ├── balls2.rs ├── boxes2.rs ├── broad_phase_filter2.rs ├── capsules2.rs ├── ccd2.rs ├── ccd_substepping2.rs ├── ccd_trigger2.rs ├── collision_groups2.rs ├── compound2.rs ├── constraints2.rs ├── convex2.rs ├── conveyor_belt2.rs ├── cross2.rs ├── damping2.rs ├── disable_sleeping2.rs ├── fem_surface2.rs ├── force_generator2.rs ├── heightfield2.rs ├── kinematic2.rs ├── mass_constraint_system2.rs ├── mass_spring_system2.rs ├── multibody2.rs ├── plasticity2.rs ├── polyline2.rs ├── pyramid2.rs ├── ragdoll2.rs ├── sensor2.rs └── wall2.rs ├── examples3d ├── Cargo.toml ├── all_examples3.rs ├── balls3.rs ├── boxes3.rs ├── broad_phase_filter3.rs ├── capsules3.rs ├── ccd3.rs ├── collision_groups3.rs ├── compound3.rs ├── constraints3.rs ├── convex3.rs ├── convex_decomposition3.rs ├── conveyor_belt3.rs ├── cross3.rs ├── damping3.rs ├── dzhanibekov3.rs ├── fem_volume3.rs ├── fixed_bug_long_thin_box_one_shot_manifold3.rs ├── fixed_bug_tree_like_multibody3.rs ├── force_generator3.rs ├── heightfield3.rs ├── kinematic3.rs ├── known_bug_excentric_convex3.rs ├── mass_constraint_system3.rs ├── mass_spring_system3.rs ├── multibody3.rs ├── plasticity3.rs ├── pyramid3.rs ├── ragdoll3.rs ├── sensor3.rs ├── trimesh3.rs └── wall3.rs ├── publish-testbeds.sh ├── publish.sh ├── rustfmt.toml ├── src ├── algebra │ ├── force2.rs │ ├── force3.rs │ ├── inertia2.rs │ ├── inertia3.rs │ ├── mod.rs │ ├── velocity2.rs │ └── velocity3.rs ├── counters │ ├── ccd_counters.rs │ ├── collision_detection_counters.rs │ ├── mod.rs │ ├── solver_counters.rs │ ├── stages_counters.rs │ └── timer.rs ├── detection │ ├── activation_manager.rs │ ├── collider_contact_manifold.rs │ └── mod.rs ├── force_generator │ ├── constant_acceleration.rs │ ├── force_generator.rs │ ├── mod.rs │ └── spring.rs ├── joint │ ├── ball_constraint.rs │ ├── ball_joint.rs │ ├── cartesian_constraint.rs │ ├── cartesian_joint.rs │ ├── cylindrical_constraint.rs │ ├── cylindrical_joint.rs │ ├── fixed_constraint.rs │ ├── fixed_joint.rs │ ├── free_joint.rs │ ├── helical_joint.rs │ ├── joint.rs │ ├── joint_constraint.rs │ ├── joint_motor.rs │ ├── mod.rs │ ├── mouse_constraint.rs │ ├── pin_slot_constraint.rs │ ├── pin_slot_joint.rs │ ├── planar_constraint.rs │ ├── planar_joint.rs │ ├── prismatic_constraint.rs │ ├── prismatic_joint.rs │ ├── rectangular_constraint.rs │ ├── rectangular_joint.rs │ ├── revolute_constraint.rs │ ├── revolute_joint.rs │ ├── slot_joint.rs │ ├── unit_constraint.rs │ ├── unit_joint.rs │ ├── universal_constraint.rs │ └── universal_joint.rs ├── lib.rs ├── material │ ├── basic_material.rs │ ├── material.rs │ ├── materials_coefficients_table.rs │ └── mod.rs ├── object │ ├── body.rs │ ├── body_set.rs │ ├── collider.rs │ ├── collider_set.rs │ ├── fem_helper.rs │ ├── fem_surface.rs │ ├── fem_volume.rs │ ├── ground.rs │ ├── mass_constraint_system.rs │ ├── mass_spring_system.rs │ ├── mod.rs │ ├── multibody.rs │ ├── multibody_link.rs │ └── rigid_body.rs ├── solver │ ├── constraint.rs │ ├── constraint_set.rs │ ├── contact_model.rs │ ├── helper.rs │ ├── impulse_cache.rs │ ├── integration_parameters.rs │ ├── mod.rs │ ├── moreau_jean_solver.rs │ ├── nonlinear_constraint.rs │ ├── nonlinear_sor_prox.rs │ ├── signorini_coulomb_pyramid_model.rs │ ├── signorini_model.rs │ └── sor_prox.rs ├── tests.rs ├── utils │ ├── deterministic_state.rs │ ├── generalized_cross.rs │ ├── index_mut2.rs │ ├── mod.rs │ ├── union_find.rs │ └── user_data.rs ├── volumetric │ ├── mod.rs │ ├── volumetric.rs │ ├── volumetric_ball.rs │ ├── volumetric_capsule.rs │ ├── volumetric_compound.rs │ ├── volumetric_cone.rs │ ├── volumetric_convex2.rs │ ├── volumetric_convex3.rs │ ├── volumetric_cuboid.rs │ ├── volumetric_cylinder.rs │ └── volumetric_shape.rs └── world │ ├── geometrical_world.rs │ ├── mechanical_world.rs │ └── mod.rs └── src_testbed ├── box2d_world.rs ├── engine.rs ├── lib.rs ├── objects ├── ball.rs ├── box_node.rs ├── capsule.rs ├── convex.rs ├── fluid.rs ├── heightfield.rs ├── mesh.rs ├── mod.rs ├── node.rs ├── plane.rs └── polyline.rs ├── testbed.rs └── ui.rs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | rust-executor: 5 | docker: 6 | - image: rust:latest 7 | 8 | jobs: 9 | check-fmt: 10 | executor: rust-executor 11 | steps: 12 | - checkout 13 | - run: 14 | name: install rustfmt 15 | command: rustup component add rustfmt 16 | - run: 17 | name: check formatting 18 | command: cargo fmt -- --check 19 | build-native: 20 | executor: rust-executor 21 | steps: 22 | - checkout 23 | - run: 24 | name: build nphysics2d 25 | command: cargo build --verbose -p nphysics2d; 26 | - run: 27 | name: build nphysics3d 28 | command: cargo build --verbose -p nphysics3d; 29 | - run: 30 | name: test nphysics2d 31 | command: cargo test --verbose -p nphysics2d; 32 | - run: 33 | name: test nphysics3d 34 | command: cargo test --verbose -p nphysics3d; 35 | # - run: 36 | # name: check nphysics_testbed2d 37 | # command: cargo check --verbose -p nphysics_testbed2d; 38 | # - run: 39 | # name: check nphysics_testbed3d 40 | # command: cargo check --verbose -p nphysics_testbed3d; 41 | # - run: 42 | # name: check nphysics-examples-2d 43 | # command: cargo check -j 1 --verbose -p nphysics-examples-2d; 44 | # - run: 45 | # name: check nphysics-examples-3d 46 | # command: cargo check -j 1 --verbose -p nphysics-examples-3d; 47 | # - run: 48 | # name: check nphysics_testbed2d with fluids 49 | # command: cd build/nphysics_testbed2d && cargo check --verbose --features=fluids; 50 | # - run: 51 | # name: check nphysics_testbed3d with fluids 52 | # command: cd build/nphysics_testbed3d && cargo check --verbose --features=fluids; 53 | build-wasm: 54 | executor: rust-executor 55 | steps: 56 | - checkout 57 | - run: 58 | name: install cargo-web 59 | command: cargo install -f cargo-web; 60 | - run: 61 | name: build nphysics2d 62 | command: cd build/nphysics2d && cargo web build --verbose --target wasm32-unknown-unknown; 63 | - run: 64 | name: build nphysics3d 65 | command: cd build/nphysics3d && cargo web build --verbose --target wasm32-unknown-unknown; 66 | # - run: 67 | # name: build nphysics-examples-2d 68 | # command: cd examples2d && cargo web build --verbose --target wasm32-unknown-unknown; 69 | # - run: 70 | # name: build nphysics-examples-3d 71 | # command: cd examples3d && cargo web build --verbose --target wasm32-unknown-unknown; 72 | # - run: 73 | # name: build nphysics_testbed2d 74 | # command: cd build/nphysics_testbed2d && cargo web build --verbose --target wasm32-unknown-unknown; 75 | # - run: 76 | # name: build nphysics_testbed3d 77 | # command: cd build/nphysics_testbed3d && cargo web build --verbose --target wasm32-unknown-unknown; 78 | 79 | 80 | workflows: 81 | version: 2 82 | build: 83 | jobs: 84 | - check-fmt 85 | - build-native: 86 | requires: 87 | - check-fmt 88 | - build-wasm: 89 | requires: 90 | - check-fmt 91 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [dimforge] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *.html 4 | *.so 5 | *.dSYM 6 | target 7 | private 8 | lib 9 | examples/lib 10 | examples/bin 11 | *~ 12 | BENCH 13 | DESIGN.md 14 | STYLE.md 15 | balls_vee2d 16 | balls_vee3d 17 | boxes_vee2d 18 | boxes_vee3d 19 | compound2d 20 | compound3d 21 | cross2d 22 | cross3d 23 | primitives3d 24 | pyramid2d 25 | pyramid3d 26 | wall2d 27 | wall3d 28 | mesh3d 29 | mesh2d 30 | ragdoll3d 31 | ragdoll2d 32 | Cargo.lock 33 | .vscode/ 34 | .idea/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to `nalgebra`, starting with the version 0.6.0 will be 3 | documented here. 4 | 5 | This project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [0.4.0] 8 | ### Modified 9 | * Use the latest **ncollide** API v0.9.0 which included breaking changes. 10 | * Rename event handler registration methods and traits to remove 11 | `signal_` and `Signal` from their names. 12 | 13 | ## [0.3.0] 14 | ### Added 15 | * Added sensors. 16 | 17 | ### Modified 18 | * `World::add_body` and `World::remove_body` have been renamed to 19 | `World::add_rigid_body` and `World::remove_rigid_body`. 20 | * `World::add_ccd_to(...)` has an additional argument indicating whether 21 | rigid bodies with CCD should trigger events for sensors it should have 22 | traversed. 23 | * `World::interferences()` has been renamed to `World::constraints()`. 24 | * `Volumetric::surface()` has been renamed to `Volumetric::area()`. 25 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ "build/nphysics2d", "build/nphysics_testbed2d", "examples2d", 3 | "build/nphysics3d", "build/nphysics_testbed3d", "examples3d", ] 4 | 5 | [profile.release] 6 | debug = true 7 | lto = true 8 | codegen-units = 1 9 | 10 | [patch.crates-io] 11 | #ncollide2d = { path = "../ncollide/build/ncollide2d" } 12 | #ncollide3d = { path = "../ncollide/build/ncollide3d" } 13 | #kiss3d = { path = "../kiss3d" } 14 | nphysics2d = { path = "build/nphysics2d" } 15 | nphysics3d = { path = "build/nphysics3d" } 16 | #simba = { path = "../simba" } 17 | #cordic = { path = "../cordic/cordic" } 18 | -------------------------------------------------------------------------------- /build/nphysics2d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nphysics2d" 3 | version = "0.23.0" 4 | authors = [ "Sébastien Crozet " ] 5 | description = "2-dimensional physics engine in Rust. This crate is being superseded by the rapier3d crate." 6 | documentation = "http://nphysics.org/rustdoc/nphysics2d/index.html" 7 | homepage = "http://nphysics.org" 8 | repository = "https://github.com/rustsim/nphysics" 9 | readme = "README.md" 10 | keywords = [ "physics", "dynamics", "rigid", "real-time", "joints" ] 11 | license = "Apache-2.0" 12 | edition = "2018" 13 | 14 | [badges] 15 | maintenance = { status = "passively-maintained" } 16 | 17 | [features] 18 | default = [ "dim2", "stdweb" , "instant/stdweb"] 19 | use-wasm-bindgen = [ "dim2", "wasm-bindgen", "web-sys" , "instant/wasm-bindgen" ] 20 | dim2 = [ ] 21 | # Improve numerical stability when working with fixed-point numbers 22 | # so we don't need a too large number of decimals. 23 | improved_fixed_point_support = [ "ncollide2d/improved_fixed_point_support" ] 24 | 25 | [lib] 26 | name = "nphysics2d" 27 | path = "../../src/lib.rs" 28 | required-features = [ "dim2" ] 29 | 30 | [dependencies] 31 | either = "1" 32 | num-traits = "0.2" 33 | slotmap = "1" # For impulse cache. 34 | generational-arena = "0.2" # For default body/collide/constraint/force sets. 35 | smallvec = "1" 36 | lazy_static = "1" 37 | simba = "0.5" 38 | nalgebra = { version = "0.28", features = [ "sparse" ] } 39 | approx = "0.5" 40 | downcast-rs = "1" 41 | bitflags = "1" 42 | ncollide2d = "0.31" 43 | instant = { version = "0.1", features = [ "now" ]} 44 | 45 | [target.wasm32-unknown-unknown.dependencies] 46 | stdweb = {version = "0.4", optional = true} 47 | wasm-bindgen = {version = "0.2", optional = true} 48 | web-sys = {version = "0.3", optional = true, features = ['Window', 'Performance', 'PerformanceTiming']} 49 | 50 | [target.wasm32-unknown-emscripten.dependencies] 51 | stdweb = {version = "0.4", optional = true} 52 | wasm-bindgen = {version = "0.2", optional = true} 53 | web-sys = {version = "0.3", optional = true, features = ['Window', 'Performance', 'PerformanceTiming']} 54 | 55 | [target.asmjs-unknown-emscripten.dependencies] 56 | stdweb = {version = "0.4", optional = true} 57 | wasm-bindgen = {version = "0.2", optional = true} 58 | web-sys = {version = "0.3", optional = true, features = ['Window', 'Performance', 'PerformanceTiming']} 59 | -------------------------------------------------------------------------------- /build/nphysics3d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nphysics3d" 3 | version = "0.23.0" 4 | authors = [ "Sébastien Crozet " ] 5 | description = "3-dimensional physics engine in Rust. This crate is being superseded by the rapier3d crate." 6 | documentation = "http://nphysics.org/rustdoc/nphysics3d/index.html" 7 | homepage = "http://nphysics.org" 8 | repository = "https://github.com/rustsim/nphysics" 9 | readme = "README.md" 10 | keywords = [ "physics", "dynamics", "rigid", "real-time", "joints" ] 11 | license = "Apache-2.0" 12 | edition = "2018" 13 | 14 | [badges] 15 | maintenance = { status = "passively-maintained" } 16 | 17 | [features] 18 | default = [ "dim3", "stdweb" , "instant/stdweb"] 19 | use-wasm-bindgen = [ "dim3", "wasm-bindgen", "web-sys", "instant/wasm-bindgen"] 20 | dim3 = [ ] 21 | 22 | # Improve numerical stability when working with fixed-point numbers 23 | # so we don't need a too large number of decimals. 24 | improved_fixed_point_support = [ "ncollide3d/improved_fixed_point_support" ] 25 | 26 | [lib] 27 | name = "nphysics3d" 28 | path = "../../src/lib.rs" 29 | required-features = [ "dim3" ] 30 | 31 | [dependencies] 32 | either = "1" 33 | num-traits = "0.2" 34 | slotmap = "1" # For impulse cache. 35 | generational-arena = "0.2" # For default body/collide/constraint/force sets. 36 | smallvec = "1" 37 | lazy_static = "1" 38 | simba = "0.5" 39 | nalgebra = { version = "0.28", features = [ "sparse" ] } 40 | approx = "0.5" 41 | downcast-rs = "1" 42 | bitflags = "1" 43 | ncollide3d = "0.31" 44 | instant = { version = "0.1", features = [ "now" ]} 45 | 46 | 47 | [target.wasm32-unknown-unknown.dependencies] 48 | stdweb = {version = "0.4", optional = true} 49 | wasm-bindgen = {version = "0.2", optional = true} 50 | web-sys = {version = "0.3", optional = true, features = ['Window', 'Performance', 'PerformanceTiming']} 51 | 52 | [target.wasm32-unknown-emscripten.dependencies] 53 | stdweb = {version = "0.4", optional = true} 54 | wasm-bindgen = {version = "0.2", optional = true} 55 | web-sys = {version = "0.3.27", optional = true, features = ['Window', 'Performance', 'PerformanceTiming']} 56 | 57 | [target.asmjs-unknown-emscripten.dependencies] 58 | stdweb = {version = "0.4", optional = true} 59 | wasm-bindgen = {version = "0.2", optional = true} 60 | web-sys = {version = "0.3", optional = true, features = ['Window', 'Performance', 'PerformanceTiming']} 61 | -------------------------------------------------------------------------------- /build/nphysics_testbed2d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nphysics_testbed2d" 3 | version = "0.10.0" 4 | authors = [ "Sébastien Crozet " ] 5 | description = "Testbed for the 2-dimensional physics engine in Rust." 6 | homepage = "http://nphysics.org" 7 | repository = "https://github.com/rustsim/nphysics" 8 | keywords = [ "physics", "dynamics", "rigid", "real-time", "joints" ] 9 | license = "BSD-3-Clause" 10 | edition = "2018" 11 | 12 | [badges] 13 | maintenance = { status = "passively-maintained" } 14 | 15 | [lib] 16 | name = "nphysics_testbed2d" 17 | path = "../../src_testbed/lib.rs" 18 | required-features = [ "dim2" ] 19 | 20 | [features] 21 | default = [ "dim2" ] 22 | dim2 = [ ] 23 | box2d-backend = [ "wrapped2d" ] 24 | 25 | 26 | [dependencies] 27 | log = { version = "0.4", optional = true } 28 | bitflags = "1" 29 | num-traits = "0.2" 30 | rand = "0.8" 31 | instant = { version = "0.1", features = [ "stdweb", "now" ]} 32 | simba = "0.4" 33 | nalgebra = "0.26" 34 | kiss3d = { version = "0.31", features = [ "conrod" ] } 35 | ncollide2d = "0.29" 36 | wrapped2d = { version = "0.4", optional = true } 37 | 38 | [dependencies.nphysics2d] 39 | path = "../nphysics2d" 40 | version = "0.23" 41 | -------------------------------------------------------------------------------- /build/nphysics_testbed3d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nphysics_testbed3d" 3 | version = "0.10.0" 4 | authors = [ "Sébastien Crozet " ] 5 | description = "Testbed for the 3-dimensional physics engine in Rust." 6 | homepage = "http://nphysics.org" 7 | repository = "https://github.com/rustsim/nphysics" 8 | keywords = [ "physics", "dynamics", "rigid", "real-time", "joints" ] 9 | license = "BSD-3-Clause" 10 | edition = "2018" 11 | 12 | [badges] 13 | maintenance = { status = "passively-maintained" } 14 | 15 | [lib] 16 | name = "nphysics_testbed3d" 17 | path = "../../src_testbed/lib.rs" 18 | required-features = [ "dim3" ] 19 | 20 | [features] 21 | default = [ "dim3" ] 22 | dim3 = [ ] 23 | 24 | [dependencies] 25 | log = { version = "0.4", optional = true } 26 | bitflags = "1" 27 | num-traits = "0.2" 28 | rand = "0.8" 29 | instant = { version = "0.1", features = [ "stdweb", "now" ]} 30 | simba = "0.4" 31 | nalgebra = "0.26" 32 | kiss3d = { version = "0.31", features = [ "conrod" ] } 33 | ncollide3d = "0.29" 34 | 35 | [dependencies.nphysics3d] 36 | path = "../nphysics3d" 37 | version = "0.23" 38 | -------------------------------------------------------------------------------- /examples2d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nphysics-examples-2d" 3 | version = "0.1.0" 4 | authors = [ "Sébastien Crozet " ] 5 | edition = "2018" 6 | 7 | [features] 8 | box2d-backend = [ "nphysics_testbed2d/box2d-backend" ] 9 | improved_fixed_point_support = [ "nphysics2d/improved_fixed_point_support" ] 10 | 11 | [dependencies] 12 | rand = "0.8" 13 | Inflector = "0.11" 14 | nalgebra = "0.26" 15 | ncollide2d = "0.29" 16 | simba = { version = "0.4", features = [ "partial_fixed_point_support" ]} 17 | 18 | [target.wasm32-unknown-unknown.dependencies] 19 | stdweb = "0.4" 20 | 21 | [target.wasm32-unknown-emscripten.dependencies] 22 | stdweb = "0.4" 23 | 24 | [target.asmjs-unknown-emscripten.dependencies] 25 | stdweb = "0.4" 26 | 27 | [dependencies.nphysics_testbed2d] 28 | path = "../build/nphysics_testbed2d" 29 | 30 | [dependencies.nphysics2d] 31 | path = "../build/nphysics2d" 32 | 33 | [[bin]] 34 | name = "all_examples2" 35 | path = "./all_examples2.rs" 36 | -------------------------------------------------------------------------------- /examples2d/all_examples2.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | extern crate nalgebra as na; 4 | 5 | macro_rules! r( 6 | ($e: expr) => { 7 | nalgebra::convert::($e) 8 | } 9 | ); 10 | 11 | use inflector::Inflector; 12 | 13 | use nphysics_testbed2d::Testbed; 14 | 15 | mod balls2; 16 | mod boxes2; 17 | mod broad_phase_filter2; 18 | mod capsules2; 19 | mod ccd2; 20 | mod ccd_trigger2; 21 | mod collision_groups2; 22 | mod compound2; 23 | mod constraints2; 24 | mod convex2; 25 | mod conveyor_belt2; 26 | mod cross2; 27 | mod damping2; 28 | mod fem_surface2; 29 | mod force_generator2; 30 | mod heightfield2; 31 | mod kinematic2; 32 | mod mass_constraint_system2; 33 | mod mass_spring_system2; 34 | mod multibody2; 35 | mod plasticity2; 36 | mod polyline2; 37 | mod ragdoll2; 38 | mod sensor2; 39 | 40 | fn demo_name_from_command_line() -> Option { 41 | let mut args = std::env::args(); 42 | 43 | while let Some(arg) = args.next() { 44 | if &arg[..] == "--example" { 45 | return args.next(); 46 | } 47 | } 48 | 49 | None 50 | } 51 | 52 | #[cfg(any(target_arch = "wasm32", target_arch = "asmjs"))] 53 | fn demo_name_from_url() -> Option { 54 | let window = stdweb::web::window(); 55 | let hash = window.location()?.search().ok()?; 56 | if !hash.is_empty() { 57 | Some(hash[1..].to_string()) 58 | } else { 59 | None 60 | } 61 | } 62 | 63 | #[cfg(not(any(target_arch = "wasm32", target_arch = "asmjs")))] 64 | fn demo_name_from_url() -> Option { 65 | None 66 | } 67 | 68 | fn main() { 69 | type Real = f32; // simba::scalar::FixedI40F24; 70 | 71 | let demo = demo_name_from_command_line() 72 | .or_else(|| demo_name_from_url()) 73 | .unwrap_or(String::new()) 74 | .to_camel_case(); 75 | 76 | let mut builders: Vec<(_, fn(&mut Testbed))> = vec![ 77 | ("Balls", balls2::init_world), 78 | ("Boxes", boxes2::init_world), 79 | ("Broad-phase filter", broad_phase_filter2::init_world), 80 | ("Capsules", capsules2::init_world), 81 | ("CCD", ccd2::init_world), 82 | ("CCD Trigger", ccd_trigger2::init_world), 83 | ("Collision Groups", collision_groups2::init_world), 84 | ("Compound Shapes", cross2::init_world), 85 | // ("Compound Shapes", compound2::init_world), 86 | ("Constraints", constraints2::init_world), 87 | ("Convex Polygons", convex2::init_world), 88 | ("Conveyor Belt", conveyor_belt2::init_world), 89 | ("Damping", damping2::init_world), 90 | ("FEM Surface", fem_surface2::init_world), 91 | ("Force Generator", force_generator2::init_world), 92 | ("Heightfield", heightfield2::init_world), 93 | ("Kinematic body", kinematic2::init_world), 94 | ( 95 | "Mass-constraint System", 96 | mass_constraint_system2::init_world, 97 | ), 98 | ("Mass-spring System", mass_spring_system2::init_world), 99 | ("Multibody", multibody2::init_world), 100 | ("Plasticity", plasticity2::init_world), 101 | ("Ragdolls", ragdoll2::init_world), 102 | ("Sensor", sensor2::init_world), 103 | ("Polygonal Line", polyline2::init_world), 104 | ]; 105 | 106 | builders.sort_by_key(|builder| builder.0); 107 | 108 | let i = builders 109 | .iter() 110 | .position(|builder| builder.0.to_camel_case().as_str() == demo.as_str()) 111 | .unwrap_or(0); 112 | let testbed = Testbed::::from_builders(i, builders); 113 | 114 | testbed.run() 115 | } 116 | -------------------------------------------------------------------------------- /examples2d/balls2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Ball, Cuboid, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground 30 | */ 31 | let ground_size = r!(25.0); 32 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector2::new(ground_size, r!(1.0)))); 33 | 34 | let ground_handle = bodies.insert(Ground::new()); 35 | let co = ColliderDesc::new(ground_shape) 36 | .translation(-Vector2::y()) 37 | .build(BodyPartHandle(ground_handle, 0)); 38 | colliders.insert(co); 39 | 40 | /* 41 | * Create the balls 42 | */ 43 | let num = 25; 44 | let rad = r!(0.1); 45 | 46 | let ball = ShapeHandle::new(Ball::new(rad)); 47 | 48 | let shift = (rad + ColliderDesc::::default_margin()) * r!(2.0) + r!(0.002); 49 | let centerx = shift * r!(num as f64) / r!(2.0); 50 | let centery = shift / r!(2.0); 51 | 52 | for i in 0usize..num { 53 | for j in 0..num { 54 | let x = r!(i as f64) * shift - centerx; 55 | let y = r!(j as f64) * shift + centery; 56 | 57 | // Build the rigid body. 58 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 59 | let rb_handle = bodies.insert(rb); 60 | 61 | // Build the collider. 62 | let co = ColliderDesc::new(ball.clone()) 63 | .density(r!(1.0)) 64 | .build(BodyPartHandle(rb_handle, 0)); 65 | colliders.insert(co); 66 | } 67 | } 68 | 69 | /* 70 | * Set up the testbed. 71 | */ 72 | testbed.set_ground_handle(Some(ground_handle)); 73 | testbed.set_world( 74 | mechanical_world, 75 | geometrical_world, 76 | bodies, 77 | colliders, 78 | joint_constraints, 79 | force_generators, 80 | ); 81 | testbed.look_at(Point2::new(0.0, 2.5), 95.0); 82 | } 83 | 84 | fn main() { 85 | let testbed = Testbed::::from_builders(0, vec![("Balls", init_world)]); 86 | testbed.run() 87 | } 88 | -------------------------------------------------------------------------------- /examples2d/boxes2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Cuboid, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground 30 | */ 31 | let ground_size = r!(25.0); 32 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector2::new(ground_size, r!(1.0)))); 33 | 34 | let ground_handle = bodies.insert(Ground::new()); 35 | let co = ColliderDesc::new(ground_shape) 36 | .translation(-Vector2::y()) 37 | .build(BodyPartHandle(ground_handle, 0)); 38 | colliders.insert(co); 39 | 40 | /* 41 | * Create the boxes 42 | */ 43 | let num = 10; 44 | let rad = r!(0.1); 45 | 46 | let cuboid = ShapeHandle::new(Cuboid::new(Vector2::repeat(rad))); 47 | 48 | let shift = (rad + ColliderDesc::::default_margin()) * r!(2.0); 49 | let centerx = shift * r!(num as f64) / r!(2.0); 50 | let centery = shift / r!(2.0); 51 | 52 | for i in 0usize..num { 53 | for j in 0..num { 54 | let x = r!(i as f64) * shift - centerx; 55 | let y = r!(j as f64) * shift + centery; 56 | 57 | // Build the rigid body. 58 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 59 | let rb_handle = bodies.insert(rb); 60 | 61 | // Build the collider. 62 | let co = ColliderDesc::new(cuboid.clone()) 63 | .density(r!(1.0)) 64 | .build(BodyPartHandle(rb_handle, 0)); 65 | colliders.insert(co); 66 | } 67 | } 68 | 69 | /* 70 | * Set up the testbed. 71 | */ 72 | testbed.set_ground_handle(Some(ground_handle)); 73 | testbed.set_world( 74 | mechanical_world, 75 | geometrical_world, 76 | bodies, 77 | colliders, 78 | joint_constraints, 79 | force_generators, 80 | ); 81 | testbed.look_at(Point2::new(0.0, 2.5), 95.0); 82 | } 83 | 84 | fn main() { 85 | let testbed = Testbed::::from_builders(0, vec![("Boxes", init_world)]); 86 | testbed.run() 87 | } 88 | -------------------------------------------------------------------------------- /examples2d/capsules2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Capsule, Cuboid, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground 30 | */ 31 | let ground_size = r!(25.0); 32 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector2::new(ground_size, r!(1.0)))); 33 | 34 | let ground_handle = bodies.insert(Ground::new()); 35 | let co = ColliderDesc::new(ground_shape) 36 | .translation(-Vector2::y()) 37 | .build(BodyPartHandle(ground_handle, 0)); 38 | colliders.insert(co); 39 | 40 | /* 41 | * Create the boxes 42 | */ 43 | let num = 15; 44 | let rad = r!(0.1); 45 | let half_height = r!(0.1); 46 | 47 | let capsule = ShapeHandle::new(Capsule::new(half_height, rad)); 48 | 49 | let shiftx = (rad + ColliderDesc::::default_margin()) * r!(2.0); 50 | let shifty = (half_height + rad) * r!(2.0); 51 | let centerx = shiftx * r!(num as f64) / r!(2.0); 52 | let centery = shifty / r!(2.0); 53 | 54 | for i in 0usize..num { 55 | for j in 0..num { 56 | let x = r!(i as f64) * shiftx - centerx; 57 | let y = r!(j as f64) * shifty + centery; 58 | 59 | // Build the rigid body. 60 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 61 | let rb_handle = bodies.insert(rb); 62 | 63 | // Build the collider. 64 | let co = ColliderDesc::new(capsule.clone()) 65 | .density(r!(1.0)) 66 | .build(BodyPartHandle(rb_handle, 0)); 67 | colliders.insert(co); 68 | } 69 | } 70 | 71 | /* 72 | * Set up the testbed. 73 | */ 74 | testbed.set_ground_handle(Some(ground_handle)); 75 | testbed.set_world( 76 | mechanical_world, 77 | geometrical_world, 78 | bodies, 79 | colliders, 80 | joint_constraints, 81 | force_generators, 82 | ); 83 | testbed.look_at(Point2::new(0.0, 2.5), 95.0); 84 | } 85 | 86 | fn main() { 87 | let testbed = Testbed::::from_builders(0, vec![("Capsules", init_world)]); 88 | testbed.run() 89 | } 90 | -------------------------------------------------------------------------------- /examples2d/compound2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry2, Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Compound, Cuboid, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground 30 | */ 31 | let ground_size = r!(25.0); 32 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector2::new(ground_size, r!(1.0)))); 33 | 34 | let ground_handle = bodies.insert(Ground::new()); 35 | let co = ColliderDesc::new(ground_shape) 36 | .translation(-Vector2::y()) 37 | .build(BodyPartHandle(ground_handle, 0)); 38 | colliders.insert(co); 39 | 40 | /* 41 | * Cross shaped geometry 42 | */ 43 | let large_rad = r!(1.0); 44 | let small_rad = r!(0.05); 45 | 46 | let delta1 = Isometry2::new(Vector2::new(r!(0.0), large_rad), na::zero()); 47 | let delta2 = Isometry2::new(Vector2::new(-large_rad, r!(0.0)), na::zero()); 48 | let delta3 = Isometry2::new(Vector2::new(large_rad, r!(0.0)), na::zero()); 49 | 50 | let mut cross_geoms = Vec::new(); 51 | let vertical = ShapeHandle::new(Cuboid::new(Vector2::new(small_rad, large_rad))); 52 | let horizontal = ShapeHandle::new(Cuboid::new(Vector2::new(large_rad, small_rad))); 53 | cross_geoms.push((delta1, horizontal)); 54 | cross_geoms.push((delta2, vertical.clone())); 55 | cross_geoms.push((delta3, vertical)); 56 | 57 | let compound = Compound::new(cross_geoms); 58 | let cross = ShapeHandle::new(compound); 59 | 60 | /* 61 | * Create the rigid bodies. 62 | */ 63 | let num = 15; 64 | let shift = r!(2.5) * large_rad; 65 | let centerx = (shift + ColliderDesc::::default_margin()) * r!(num as f64) / r!(2.0); 66 | let centery = (shift + ColliderDesc::::default_margin()) * r!(num as f64) / r!(2.0); 67 | 68 | for i in 0usize..num { 69 | for j in 0usize..num { 70 | let x = r!(i as f64) * r!(2.5) * large_rad - centerx; 71 | let y = r!(j as f64) * r!(2.5) * -large_rad + centery * r!(2.0); 72 | 73 | // Build the rigid body. 74 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 75 | let rb_handle = bodies.insert(rb); 76 | 77 | // Build the collider. 78 | let co = ColliderDesc::new(cross.clone()) 79 | .density(r!(1.0)) 80 | .build(BodyPartHandle(rb_handle, 0)); 81 | colliders.insert(co); 82 | } 83 | } 84 | 85 | /* 86 | * Run the simulation. 87 | */ 88 | testbed.set_ground_handle(Some(ground_handle)); 89 | testbed.set_world( 90 | mechanical_world, 91 | geometrical_world, 92 | bodies, 93 | colliders, 94 | joint_constraints, 95 | force_generators, 96 | ); 97 | testbed.look_at(Point2::new(0.0, 3.0), 95.0); 98 | } 99 | 100 | fn main() { 101 | let testbed = Testbed::::from_builders(0, vec![("Compound", init_world)]); 102 | testbed.run() 103 | } 104 | -------------------------------------------------------------------------------- /examples2d/convex2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use rand::distributions::{Distribution, Standard}; 4 | use rand::{rngs::StdRng, SeedableRng}; 5 | 6 | use na::{Point2, RealField, Vector2}; 7 | use ncollide2d::shape::{ConvexPolygon, Cuboid, ShapeHandle}; 8 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 9 | use nphysics2d::joint::DefaultJointConstraintSet; 10 | use nphysics2d::object::{ 11 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 12 | }; 13 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 14 | use nphysics_testbed2d::Testbed; 15 | 16 | /* 17 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 18 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 19 | */ 20 | pub fn init_world(testbed: &mut Testbed) { 21 | /* 22 | * World 23 | */ 24 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 25 | let geometrical_world = DefaultGeometricalWorld::new(); 26 | let mut bodies = DefaultBodySet::new(); 27 | let mut colliders = DefaultColliderSet::new(); 28 | let joint_constraints = DefaultJointConstraintSet::new(); 29 | let force_generators = DefaultForceGeneratorSet::new(); 30 | 31 | /* 32 | * Ground 33 | */ 34 | let ground_size = r!(50.0); 35 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector2::new(ground_size, r!(1.0)))); 36 | 37 | let ground_handle = bodies.insert(Ground::new()); 38 | let co = ColliderDesc::new(ground_shape) 39 | .translation(-Vector2::y()) 40 | .build(BodyPartHandle(ground_handle, 0)); 41 | colliders.insert(co); 42 | 43 | /* 44 | * Create the convex geometries. 45 | */ 46 | let npts = 10usize; 47 | let num = 25; 48 | let shift = r!(0.4); 49 | let centerx = shift * r!(num as f64) / r!(2.0); 50 | let centery = shift; 51 | let mut rng = StdRng::seed_from_u64(0); 52 | let distribution = Standard; 53 | 54 | for i in 0usize..num { 55 | for j in 0usize..num { 56 | let x = r!(i as f64) * shift - centerx; 57 | let y = r!(j as f64) * shift + centery; 58 | 59 | let mut pts = Vec::with_capacity(npts); 60 | 61 | for _ in 0..npts { 62 | let pt: Point2 = distribution.sample(&mut rng); 63 | pts.push(na::convert::<_, Point2>(pt) * r!(0.4)); 64 | } 65 | 66 | // Build the rigid body. 67 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 68 | let rb_handle = bodies.insert(rb); 69 | 70 | // Build the collider. 71 | let geom = ShapeHandle::new(ConvexPolygon::try_from_points(&pts).unwrap()); 72 | let co = ColliderDesc::new(geom) 73 | .density(r!(1.0)) 74 | .build(BodyPartHandle(rb_handle, 0)); 75 | colliders.insert(co); 76 | } 77 | } 78 | 79 | /* 80 | * Set up the testbed. 81 | */ 82 | testbed.set_ground_handle(Some(ground_handle)); 83 | testbed.set_world( 84 | mechanical_world, 85 | geometrical_world, 86 | bodies, 87 | colliders, 88 | joint_constraints, 89 | force_generators, 90 | ); 91 | testbed.look_at(Point2::new(0.0, 2.5), 95.0); 92 | } 93 | 94 | fn main() { 95 | let testbed = Testbed::::from_builders(0, vec![("Convex", init_world)]); 96 | testbed.run() 97 | } 98 | -------------------------------------------------------------------------------- /examples2d/conveyor_belt2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Cuboid, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::material::{BasicMaterial, MaterialHandle}; 8 | use nphysics2d::object::{ 9 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 10 | }; 11 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 12 | use nphysics_testbed2d::Testbed; 13 | 14 | /* 15 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 16 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 17 | */ 18 | pub fn init_world(testbed: &mut Testbed) { 19 | /* 20 | * World 21 | */ 22 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 23 | let geometrical_world = DefaultGeometricalWorld::new(); 24 | let mut bodies = DefaultBodySet::new(); 25 | let mut colliders = DefaultColliderSet::new(); 26 | let joint_constraints = DefaultJointConstraintSet::new(); 27 | let force_generators = DefaultForceGeneratorSet::new(); 28 | 29 | /* 30 | * Setup a static body used as the ground. 31 | */ 32 | let ground_handle = bodies.insert(Ground::new()); 33 | 34 | /* 35 | * Ground 36 | */ 37 | let ground_size = r!(5.0); 38 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector2::new(ground_size, r!(0.2)))); 39 | let conveyor_material1 = BasicMaterial { 40 | surface_velocity: Some(Vector2::x()), 41 | ..BasicMaterial::default() 42 | }; 43 | let conveyor_material2 = BasicMaterial { 44 | surface_velocity: Some(-Vector2::x()), 45 | ..BasicMaterial::default() 46 | }; 47 | 48 | for i in 0..10 { 49 | let co = ColliderDesc::new(ground_shape.clone()) 50 | .translation(Vector2::new(r!(-2.0), r!(5.0) - r!(i as f64) * r!(4.0))) 51 | .rotation(r!(0.1)) 52 | .material(MaterialHandle::new(conveyor_material1)) 53 | .build(BodyPartHandle(ground_handle, 0)); 54 | colliders.insert(co); 55 | 56 | let co = ColliderDesc::new(ground_shape.clone()) 57 | .translation(Vector2::new(r!(2.0), r!(3.0) - r!(i as f64) * r!(4.0))) 58 | .rotation(r!(-0.1)) 59 | .material(MaterialHandle::new(conveyor_material2)) 60 | .build(BodyPartHandle(ground_handle, 0)); 61 | colliders.insert(co); 62 | } 63 | 64 | /* 65 | * Create the boxes 66 | */ 67 | let num = 5; 68 | let rad = r!(0.1); 69 | 70 | let cuboid = ShapeHandle::new(Cuboid::new(Vector2::repeat(rad))); 71 | 72 | let shift = (rad + ColliderDesc::::default_margin()) * r!(2.0); 73 | let centerx = shift * r!(num as f64) / r!(2.0) + r!(5.0); 74 | let centery = shift / r!(2.0) + r!(5.0); 75 | 76 | for i in 0usize..num { 77 | for j in 0..num { 78 | let x = r!(i as f64) * shift - centerx; 79 | let y = r!(j as f64) * shift + centery; 80 | 81 | // Build the rigid body. 82 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 83 | let rb_handle = bodies.insert(rb); 84 | 85 | // Build the collider. 86 | let co = ColliderDesc::new(cuboid.clone()) 87 | .density(r!(1.0)) 88 | .build(BodyPartHandle(rb_handle, 0)); 89 | colliders.insert(co); 90 | } 91 | } 92 | 93 | /* 94 | * Set up the testbed. 95 | */ 96 | testbed.set_ground_handle(Some(ground_handle)); 97 | testbed.set_world( 98 | mechanical_world, 99 | geometrical_world, 100 | bodies, 101 | colliders, 102 | joint_constraints, 103 | force_generators, 104 | ); 105 | testbed.look_at(Point2::new(0.0, 2.5), 95.0); 106 | } 107 | 108 | fn main() { 109 | let testbed = Testbed::::from_builders(0, vec![("Conveyor belt", init_world)]); 110 | testbed.run() 111 | } 112 | -------------------------------------------------------------------------------- /examples2d/cross2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Compound, Cuboid, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground 30 | */ 31 | let ground_size = r!(25.0); 32 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector2::new(ground_size, r!(1.0)))); 33 | 34 | let ground_handle = bodies.insert(Ground::new()); 35 | let co = ColliderDesc::new(ground_shape) 36 | .translation(-Vector2::y()) 37 | .build(BodyPartHandle(ground_handle, 0)); 38 | colliders.insert(co); 39 | 40 | /* 41 | * Cross shaped geometry 42 | */ 43 | let mut cross_geoms = Vec::new(); 44 | 45 | let large_rad = r!(1.0); 46 | let small_rad = r!(0.05); 47 | 48 | let edge_x = Cuboid::new(Vector2::new(large_rad, small_rad)); 49 | let edge_y = Cuboid::new(Vector2::new(small_rad, large_rad)); 50 | 51 | cross_geoms.push((na::one(), ShapeHandle::new(edge_x))); 52 | cross_geoms.push((na::one(), ShapeHandle::new(edge_y))); 53 | 54 | let compound = Compound::new(cross_geoms); 55 | let cross = ShapeHandle::new(compound); 56 | 57 | /* 58 | * Create the boxes 59 | */ 60 | let num = 15; 61 | let shift = r!(2.5) * large_rad; 62 | let centerx = shift * r!(num as f64) / r!(2.0); 63 | let centery = shift * r!(num as f64) / r!(2.0); 64 | 65 | for i in 0usize..num { 66 | for j in 0usize..num { 67 | let x = r!(i as f64) * r!(2.5) * large_rad - centerx; 68 | let y = r!(j as f64) * r!(2.5) * -large_rad + centery * r!(2.0); 69 | 70 | // Build the rigid body. 71 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 72 | let rb_handle = bodies.insert(rb); 73 | 74 | // Build the collider. 75 | let co = ColliderDesc::new(cross.clone()) 76 | .density(r!(1.0)) 77 | .build(BodyPartHandle(rb_handle, 0)); 78 | colliders.insert(co); 79 | } 80 | } 81 | 82 | /* 83 | * Run the simulation. 84 | */ 85 | testbed.set_ground_handle(Some(ground_handle)); 86 | testbed.set_world( 87 | mechanical_world, 88 | geometrical_world, 89 | bodies, 90 | colliders, 91 | joint_constraints, 92 | force_generators, 93 | ); 94 | testbed.look_at(Point2::new(0.0, 8.0), 30.0); 95 | } 96 | 97 | fn main() { 98 | let testbed = Testbed::::from_builders(0, vec![("Cross", init_world)]); 99 | testbed.run() 100 | } 101 | -------------------------------------------------------------------------------- /examples2d/damping2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use std::f32; 4 | 5 | use na::{Point2, RealField, Vector2}; 6 | use ncollide2d::shape::{Cuboid, ShapeHandle}; 7 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 8 | use nphysics2d::joint::DefaultJointConstraintSet; 9 | use nphysics2d::math::Velocity; 10 | use nphysics2d::object::{ 11 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 12 | }; 13 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 14 | use nphysics_testbed2d::Testbed; 15 | 16 | /* 17 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 18 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 19 | */ 20 | pub fn init_world(testbed: &mut Testbed) { 21 | /* 22 | * World 23 | */ 24 | let mechanical_world = DefaultMechanicalWorld::new(na::zero()); 25 | let geometrical_world = DefaultGeometricalWorld::new(); 26 | let mut bodies = DefaultBodySet::new(); 27 | let mut colliders = DefaultColliderSet::new(); 28 | let joint_constraints = DefaultJointConstraintSet::new(); 29 | let force_generators = DefaultForceGeneratorSet::new(); 30 | 31 | /* 32 | * Create the balls 33 | */ 34 | let num = 10; 35 | let rad = r!(0.2); 36 | 37 | let cube = ShapeHandle::new(Cuboid::new(Vector2::repeat(rad))); 38 | let _shift = (rad + ColliderDesc::::default_margin()) * r!(2.0) + r!(0.002); 39 | let subdiv = r!(1.0) / r!(num as f64); 40 | 41 | for i in 0usize..num { 42 | let (x, y) = (r!(i as f64) * subdiv * r!(std::f64::consts::PI) * r!(2.0)).sin_cos(); 43 | let dir = Vector2::new(x, y); 44 | 45 | // Build the rigid body. 46 | let rb = RigidBodyDesc::new() 47 | .translation(dir) 48 | .velocity(Velocity::new(dir * r!(10.0), r!(100.0))) 49 | .linear_damping(r!((i + 1) as f64) * subdiv * r!(10.0)) 50 | .angular_damping(r!((num - i) as f64) * subdiv * r!(10.0)) 51 | .build(); 52 | let rb_handle = bodies.insert(rb); 53 | 54 | // Build the collider. 55 | let co = ColliderDesc::new(cube.clone()) 56 | .density(r!(1.0)) 57 | .build(BodyPartHandle(rb_handle, 0)); 58 | colliders.insert(co); 59 | } 60 | 61 | /* 62 | * Set up the testbed. 63 | */ 64 | // We add a ground to make dragging object work (this is a requirement of the testbed, not nphysics itself). 65 | let ground_handle = bodies.insert(Ground::new()); 66 | testbed.set_ground_handle(Some(ground_handle)); 67 | testbed.set_world( 68 | mechanical_world, 69 | geometrical_world, 70 | bodies, 71 | colliders, 72 | joint_constraints, 73 | force_generators, 74 | ); 75 | testbed.look_at(Point2::new(3.0, 2.0), 75.0); 76 | } 77 | 78 | fn main() { 79 | let testbed = Testbed::::from_builders(0, vec![("Damping", init_world)]); 80 | testbed.run() 81 | } 82 | -------------------------------------------------------------------------------- /examples2d/disable_sleeping2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | extern crate ncollide2d; 3 | extern crate nphysics2d; 4 | extern crate nphysics_testbed2d; 5 | 6 | use na::{Point2, RealField, Vector2}; 7 | use ncollide2d::shape::{Cuboid, ShapeHandle}; 8 | use nphysics2d::math::Velocity; 9 | use nphysics2d::object::{ColliderDesc, RigidBodyDesc}; 10 | use nphysics2d::world::World; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mut world = World::new(); 22 | world.set_gravity(Vector2::new(0.0, 0.0)); 23 | 24 | /* 25 | * Create the box that will be deactivated. 26 | */ 27 | let rad = r!(0.1); 28 | let cuboid = ShapeHandle::new(Cuboid::new(Vector2::new(rad, rad))); 29 | let collider_desc = ColliderDesc::new(cuboid).density(r!(1.0)); 30 | 31 | /* 32 | * Create the body that will be deactivated. 33 | */ 34 | RigidBodyDesc::new() 35 | .collider(&collider_desc) 36 | .velocity(Velocity::linear(0.099, 0.0)) 37 | .build(&mut world); 38 | 39 | /* 40 | * Create the body that cannot be deactivated by 41 | * setting its sleep threshold to None. 42 | */ 43 | RigidBodyDesc::new() 44 | .collider(&collider_desc) 45 | .translation(Vector2::y() * 0.3) 46 | .velocity(Velocity::linear(0.099, 0.0)) 47 | .sleep_threshold(None) 48 | .build(&mut world); 49 | 50 | /* 51 | * Set up the testbed. 52 | */ 53 | testbed.set_world(world); 54 | testbed.look_at(Point2::origin(), 95.0); 55 | } 56 | 57 | fn main() { 58 | let mut testbed = Testbed::new_empty(); 59 | init_world(&mut testbed); 60 | testbed.run(); 61 | } 62 | -------------------------------------------------------------------------------- /examples2d/heightfield2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{DVector, Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Cuboid, HeightField, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | use rand::{rngs::StdRng, Rng, SeedableRng}; 14 | 15 | /* 16 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 17 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 18 | */ 19 | pub fn init_world(testbed: &mut Testbed) { 20 | /* 21 | * World 22 | */ 23 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 24 | let geometrical_world = DefaultGeometricalWorld::new(); 25 | let mut bodies = DefaultBodySet::new(); 26 | let mut colliders = DefaultColliderSet::new(); 27 | let joint_constraints = DefaultJointConstraintSet::new(); 28 | let force_generators = DefaultForceGeneratorSet::new(); 29 | 30 | /* 31 | * Polyline 32 | */ 33 | let mut rng = StdRng::seed_from_u64(0); 34 | let heights = DVector::from_fn(20, |_, _| na::convert(rng.gen::())); 35 | 36 | let mut heightfield = HeightField::new(heights, Vector2::new(r!(20.0), r!(1.0))); 37 | 38 | // It is possible to remove some segments from the heightfield. 39 | heightfield.set_segment_removed(3, true); 40 | heightfield.set_segment_removed(13, true); 41 | 42 | let ground_handle = bodies.insert(Ground::new()); 43 | let co = 44 | ColliderDesc::new(ShapeHandle::new(heightfield)).build(BodyPartHandle(ground_handle, 0)); 45 | colliders.insert(co); 46 | 47 | /* 48 | * Create the boxes 49 | */ 50 | let width = 75; 51 | let height = 7; 52 | let rad = r!(0.1); 53 | 54 | let cuboid = ShapeHandle::new(Cuboid::new(Vector2::repeat(rad))); 55 | 56 | let shift = r!(2.0) * (rad + ColliderDesc::::default_margin()); 57 | let centerx = shift * r!(width as f64) / r!(2.0); 58 | 59 | for i in 0usize..height { 60 | for j in 0usize..width { 61 | let fj = r!(j as f64); 62 | let fi = r!(i as f64); 63 | let x = fj * shift - centerx; 64 | let y = fi * shift + r!(1.0); 65 | 66 | // Build the rigid body. 67 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 68 | let rb_handle = bodies.insert(rb); 69 | 70 | // Build the collider. 71 | let co = ColliderDesc::new(cuboid.clone()) 72 | .density(r!(1.0)) 73 | .build(BodyPartHandle(rb_handle, 0)); 74 | colliders.insert(co); 75 | } 76 | } 77 | 78 | /* 79 | * Run the simulation. 80 | */ 81 | testbed.set_ground_handle(Some(ground_handle)); 82 | testbed.set_world( 83 | mechanical_world, 84 | geometrical_world, 85 | bodies, 86 | colliders, 87 | joint_constraints, 88 | force_generators, 89 | ); 90 | testbed.look_at(Point2::origin(), 75.0); 91 | } 92 | 93 | fn main() { 94 | let testbed = Testbed::::from_builders(0, vec![("Heightfield", init_world)]); 95 | testbed.run() 96 | } 97 | -------------------------------------------------------------------------------- /examples2d/polyline2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Cuboid, Polyline, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | use rand::distributions::{Distribution, Standard}; 14 | use rand::{rngs::StdRng, SeedableRng}; 15 | 16 | /* 17 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 18 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 19 | */ 20 | pub fn init_world(testbed: &mut Testbed) { 21 | /* 22 | * World 23 | */ 24 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 25 | let geometrical_world = DefaultGeometricalWorld::new(); 26 | let mut bodies = DefaultBodySet::new(); 27 | let mut colliders = DefaultColliderSet::new(); 28 | let joint_constraints = DefaultJointConstraintSet::new(); 29 | let force_generators = DefaultForceGeneratorSet::new(); 30 | 31 | /* 32 | * Polyline 33 | */ 34 | let num_split = 20; 35 | let begin = r!(-15.0); 36 | let max_h = r!(3.0); 37 | let begin_h = r!(-3.0); 38 | let step = (begin.abs() * r!(2.0)) / r!(num_split as f64); 39 | let mut vertices: Vec> = (0..num_split + 2) 40 | .map(|i| Point2::new(begin + r!(i as f64) * step, r!(0.0))) 41 | .collect(); 42 | 43 | let mut rng = StdRng::seed_from_u64(0); 44 | let distribution = Standard; 45 | 46 | for i in 0usize..num_split { 47 | let h: f64 = distribution.sample(&mut rng); 48 | vertices[i + 1].y = r!(h) * max_h + begin_h; 49 | } 50 | 51 | let polyline = ShapeHandle::new(Polyline::new(vertices, None)); 52 | let ground_handle = bodies.insert(Ground::new()); 53 | let co = ColliderDesc::new(polyline) 54 | .translation(-Vector2::y()) 55 | .build(BodyPartHandle(ground_handle, 0)); 56 | colliders.insert(co); 57 | 58 | /* 59 | * Create the boxes 60 | */ 61 | let width = 75; 62 | let height = 7; 63 | let rad = r!(0.1); 64 | 65 | let cuboid = ShapeHandle::new(Cuboid::new(Vector2::repeat(rad))); 66 | 67 | let shift = r!(2.0) * (rad + ColliderDesc::::default_margin()); 68 | let centerx = shift * r!(width as f64) / r!(2.0); 69 | 70 | for i in 0usize..height { 71 | for j in 0usize..width { 72 | let fj = r!(j as f64); 73 | let fi = r!(i as f64); 74 | let x = fj * shift - centerx; 75 | let y = fi * shift + r!(0.5); 76 | 77 | // Build the rigid body. 78 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 79 | let rb_handle = bodies.insert(rb); 80 | 81 | // Build the collider. 82 | let co = ColliderDesc::new(cuboid.clone()) 83 | .density(r!(1.0)) 84 | .build(BodyPartHandle(rb_handle, 0)); 85 | colliders.insert(co); 86 | } 87 | } 88 | 89 | /* 90 | * Run the simulation. 91 | */ 92 | testbed.set_ground_handle(Some(ground_handle)); 93 | testbed.set_world( 94 | mechanical_world, 95 | geometrical_world, 96 | bodies, 97 | colliders, 98 | joint_constraints, 99 | force_generators, 100 | ); 101 | testbed.look_at(Point2::origin(), 75.0); 102 | } 103 | 104 | fn main() { 105 | let testbed = Testbed::::from_builders(0, vec![("Polyline", init_world)]); 106 | testbed.run(); 107 | } 108 | -------------------------------------------------------------------------------- /examples2d/pyramid2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Cuboid, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground 30 | */ 31 | let ground_size = r!(25.0); 32 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector2::new(ground_size, r!(1.0)))); 33 | 34 | let ground_handle = bodies.insert(Ground::new()); 35 | let co = ColliderDesc::new(ground_shape) 36 | .translation(-Vector2::y()) 37 | .build(BodyPartHandle(ground_handle, 0)); 38 | colliders.insert(co); 39 | 40 | /* 41 | * Create the boxes 42 | */ 43 | let num = 20; 44 | let rad = r!(0.1); 45 | 46 | let cuboid = ShapeHandle::new(Cuboid::new(Vector2::repeat(rad))); 47 | 48 | let shift = r!(2.0) * (rad + ColliderDesc::::default_margin()); 49 | let centerx = shift * r!(num as f64) / r!(2.0); 50 | let centery = rad + ColliderDesc::::default_margin() * r!(2.0); 51 | 52 | for i in 0usize..num { 53 | for j in i..num { 54 | let fj = j as f64; 55 | let fi = i as f64; 56 | let x = (fi * shift / r!(2.0)) + (fj - fi) * shift - centerx; 57 | let y = fi * shift + centery; 58 | 59 | // Build the rigid body. 60 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 61 | let rb_handle = bodies.insert(rb); 62 | 63 | // Build the collider. 64 | let co = ColliderDesc::new(cuboid.clone()) 65 | .density(r!(1.0)) 66 | .build(BodyPartHandle(rb_handle, 0)); 67 | colliders.insert(co); 68 | } 69 | } 70 | 71 | /* 72 | * Run the simulation. 73 | */ 74 | testbed.set_ground_handle(Some(ground_handle)); 75 | testbed.set_world( 76 | mechanical_world, 77 | geometrical_world, 78 | bodies, 79 | colliders, 80 | joint_constraints, 81 | force_generators, 82 | ); 83 | testbed.look_at(Point2::new(0.0, 3.0), 95.0); 84 | } 85 | 86 | fn main() { 87 | let testbed = Testbed::::from_builders(0, vec![("Pyramid", init_world)]); 88 | testbed.run() 89 | } 90 | -------------------------------------------------------------------------------- /examples2d/wall2.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point2, RealField, Vector2}; 4 | use ncollide2d::shape::{Cuboid, ShapeHandle}; 5 | use nphysics2d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics2d::joint::DefaultJointConstraintSet; 7 | use nphysics2d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics2d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed2d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector2::new(r!(0.0), r!(-9.81))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground 30 | */ 31 | let ground_size = r!(25.0); 32 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector2::new(ground_size, r!(1.0)))); 33 | 34 | let ground_handle = bodies.insert(Ground::new()); 35 | let co = ColliderDesc::new(ground_shape) 36 | .translation(-Vector2::y()) 37 | .build(BodyPartHandle(ground_handle, 0)); 38 | colliders.insert(co); 39 | 40 | /* 41 | * Create the boxes 42 | */ 43 | let width = 100; 44 | let height = 20; 45 | let rad = r!(0.1); 46 | 47 | let cuboid = ShapeHandle::new(Cuboid::new(Vector2::repeat(rad))); 48 | 49 | let shift = r!(2.0) * (rad + ColliderDesc::::default_margin()); 50 | let centerx = shift * (width as f64) / r!(2.0); 51 | let centery = rad + r!(0.04); 52 | 53 | for i in 0usize..height { 54 | for j in 0usize..width { 55 | let fj = j as f64; 56 | let fi = i as f64; 57 | let x = fj * shift - centerx; 58 | let y = fi * shift + centery; 59 | 60 | // Build the rigid body. 61 | let rb = RigidBodyDesc::new().translation(Vector2::new(x, y)).build(); 62 | let rb_handle = bodies.insert(rb); 63 | 64 | // Build the collider. 65 | let co = ColliderDesc::new(cuboid.clone()) 66 | .density(r!(1.0)) 67 | .build(BodyPartHandle(rb_handle, 0)); 68 | colliders.insert(co); 69 | } 70 | } 71 | 72 | /* 73 | * Run the simulation. 74 | */ 75 | testbed.set_ground_handle(Some(ground_handle)); 76 | testbed.set_world( 77 | mechanical_world, 78 | geometrical_world, 79 | bodies, 80 | colliders, 81 | joint_constraints, 82 | force_generators, 83 | ); 84 | testbed.look_at(Point2::new(0.0, 3.0), 95.0); 85 | } 86 | 87 | fn main() { 88 | let testbed = Testbed::::from_builders(0, vec![("Wall", init_world)]); 89 | testbed.run() 90 | } 91 | -------------------------------------------------------------------------------- /examples3d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nphysics-examples-3d" 3 | version = "0.1.0" 4 | authors = [ "Sébastien Crozet " ] 5 | edition = "2018" 6 | 7 | [features] 8 | improved_fixed_point_support = [ "nphysics3d/improved_fixed_point_support" ] 9 | 10 | 11 | [dependencies] 12 | rand = "0.8" 13 | rand_distr = "0.4" 14 | num-traits = "0.2" 15 | Inflector = "0.11" 16 | nalgebra = "0.26" 17 | ncollide3d = "0.29" 18 | kiss3d = "0.31" 19 | simba = { version = "0.4", features = [ "partial_fixed_point_support" ]} 20 | 21 | [target.wasm32-unknown-unknown.dependencies] 22 | stdweb = "0.4" 23 | 24 | [target.wasm32-unknown-emscripten.dependencies] 25 | stdweb = "0.4" 26 | 27 | [target.asmjs-unknown-emscripten.dependencies] 28 | stdweb = "0.4" 29 | 30 | [dependencies.nphysics3d] 31 | path = "../build/nphysics3d" 32 | 33 | [dependencies.nphysics_testbed3d] 34 | path = "../build/nphysics_testbed3d" 35 | 36 | [[bin]] 37 | name = "all_examples3" 38 | path = "./all_examples3.rs" 39 | -------------------------------------------------------------------------------- /examples3d/all_examples3.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | extern crate nalgebra as na; 4 | extern crate ncollide3d; 5 | extern crate nphysics3d; 6 | extern crate nphysics_testbed3d; 7 | 8 | macro_rules! r( 9 | ($e: expr) => { 10 | nalgebra::convert::($e) 11 | } 12 | ); 13 | 14 | use inflector::Inflector; 15 | 16 | use nphysics_testbed3d::Testbed; 17 | 18 | mod balls3; 19 | mod boxes3; 20 | mod broad_phase_filter3; 21 | mod capsules3; 22 | mod ccd3; 23 | mod collision_groups3; 24 | mod compound3; 25 | mod constraints3; 26 | mod convex3; 27 | mod conveyor_belt3; 28 | mod cross3; 29 | mod damping3; 30 | mod dzhanibekov3; 31 | mod fem_volume3; 32 | mod force_generator3; 33 | mod heightfield3; 34 | mod kinematic3; 35 | mod mass_constraint_system3; 36 | mod mass_spring_system3; 37 | mod multibody3; 38 | mod plasticity3; 39 | mod ragdoll3; 40 | mod sensor3; 41 | mod trimesh3; 42 | 43 | fn demo_name_from_command_line() -> Option { 44 | let mut args = std::env::args(); 45 | 46 | while let Some(arg) = args.next() { 47 | if &arg[..] == "--example" { 48 | return args.next(); 49 | } 50 | } 51 | 52 | None 53 | } 54 | 55 | #[cfg(any(target_arch = "wasm32", target_arch = "asmjs"))] 56 | fn demo_name_from_url() -> Option { 57 | let window = stdweb::web::window(); 58 | let hash = window.location()?.search().ok()?; 59 | if !hash.is_empty() { 60 | Some(hash[1..].to_string()) 61 | } else { 62 | None 63 | } 64 | } 65 | 66 | #[cfg(not(any(target_arch = "wasm32", target_arch = "asmjs")))] 67 | fn demo_name_from_url() -> Option { 68 | None 69 | } 70 | 71 | fn main() { 72 | type Real = f32; // simba::scalar::FixedI40F24; 73 | 74 | let demo = demo_name_from_command_line() 75 | .or_else(|| demo_name_from_url()) 76 | .unwrap_or(String::new()) 77 | .to_camel_case(); 78 | 79 | let mut builders: Vec<(_, fn(&mut Testbed))> = vec![ 80 | ("Balls", balls3::init_world), 81 | ("Boxes", boxes3::init_world), 82 | ("Broad-phase filter", broad_phase_filter3::init_world), 83 | ("Capsules", capsules3::init_world), 84 | ("CCD", ccd3::init_world), 85 | ("Collision Groups", collision_groups3::init_world), 86 | ("Compound Shapes", cross3::init_world), 87 | // ("Compound Shapes", compound3::init_world::::from_builders(i, builders); 113 | 114 | testbed.run() 115 | } 116 | -------------------------------------------------------------------------------- /examples3d/balls3.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point3, RealField, Vector3}; 4 | use ncollide3d::shape::{Ball, Cuboid, ShapeHandle}; 5 | use nphysics3d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics3d::joint::DefaultJointConstraintSet; 7 | use nphysics3d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics3d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed3d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector3::new(r!(0.0), r!(-9.81), r!(0.0))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground. 30 | */ 31 | let ground_thickness = r!(0.2); 32 | let ground_width = r!(3.0); 33 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector3::new( 34 | ground_width, 35 | ground_thickness, 36 | ground_width, 37 | ))); 38 | 39 | let ground_handle = bodies.insert(Ground::new()); 40 | let co = ColliderDesc::new(ground_shape) 41 | .translation(Vector3::y() * -ground_thickness) 42 | .build(BodyPartHandle(ground_handle, 0)); 43 | colliders.insert(co); 44 | 45 | /* 46 | * Create the balls. 47 | */ 48 | let num = 8; 49 | let rad = r!(0.1); 50 | 51 | let ball = ShapeHandle::new(Ball::new(rad)); 52 | 53 | let shift = (rad + ColliderDesc::::default_margin()) * r!(2.0) + r!(0.002); 54 | let centerx = shift * r!(num as f64) / r!(2.0); 55 | let centery = shift / r!(2.0); 56 | let centerz = shift * r!(num as f64) / r!(2.0); 57 | let height = r!(3.0); 58 | 59 | for i in 0usize..num { 60 | for j in 0usize..num { 61 | for k in 0usize..num { 62 | let x = r!(i as f64) * shift - centerx; 63 | let y = r!(j as f64) * shift + centery + height; 64 | let z = r!(k as f64) * shift - centerz; 65 | 66 | // Build the rigid body. 67 | let rb = RigidBodyDesc::new() 68 | .translation(Vector3::new(x, y, z)) 69 | .build(); 70 | let rb_handle = bodies.insert(rb); 71 | 72 | // Build the collider. 73 | let co = ColliderDesc::new(ball.clone()) 74 | .density(r!(1.0)) 75 | .build(BodyPartHandle(rb_handle, 0)); 76 | colliders.insert(co); 77 | } 78 | } 79 | } 80 | 81 | /* 82 | * Set up the testbed. 83 | */ 84 | testbed.set_ground_handle(Some(ground_handle)); 85 | testbed.set_world( 86 | mechanical_world, 87 | geometrical_world, 88 | bodies, 89 | colliders, 90 | joint_constraints, 91 | force_generators, 92 | ); 93 | testbed.look_at(Point3::new(-4.0, 1.0, -4.0), Point3::new(0.0, 1.0, 0.0)); 94 | } 95 | 96 | fn main() { 97 | let testbed = Testbed::::from_builders(0, vec![("Balls", init_world)]); 98 | 99 | testbed.run() 100 | } 101 | -------------------------------------------------------------------------------- /examples3d/boxes3.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point3, RealField, Vector3}; 4 | use ncollide3d::shape::{Cuboid, ShapeHandle}; 5 | use nphysics3d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics3d::joint::DefaultJointConstraintSet; 7 | use nphysics3d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics3d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed3d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector3::new(r!(0.0), r!(-9.81), r!(0.0))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground. 30 | */ 31 | let ground_thickness = r!(0.2); 32 | let ground_width = r!(3.0); 33 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector3::new( 34 | ground_width, 35 | ground_thickness, 36 | ground_width, 37 | ))); 38 | 39 | let ground_handle = bodies.insert(Ground::new()); 40 | let co = ColliderDesc::new(ground_shape) 41 | .translation(Vector3::y() * -ground_thickness) 42 | .build(BodyPartHandle(ground_handle, 0)); 43 | colliders.insert(co); 44 | 45 | /* 46 | * Create the boxes. 47 | */ 48 | let num = 6; 49 | let rad = r!(0.1); 50 | 51 | let cuboid = ShapeHandle::new(Cuboid::new(Vector3::repeat(rad))); 52 | 53 | let shift = (rad + ColliderDesc::::default_margin()) * r!(2.0); 54 | let centerx = shift * r!(num as f64) / r!(2.0); 55 | let centery = shift / r!(2.0); 56 | let centerz = shift * r!(num as f64) / r!(2.0); 57 | let height = r!(3.0); 58 | 59 | for i in 0usize..num { 60 | for j in 0usize..num { 61 | for k in 0usize..num { 62 | let x = r!(i as f64) * shift - centerx; 63 | let y = r!(j as f64) * shift + centery + height; 64 | let z = r!(k as f64) * shift - centerz; 65 | 66 | // Build the rigid body. 67 | let rb = RigidBodyDesc::new() 68 | .translation(Vector3::new(x, y, z)) 69 | .build(); 70 | let rb_handle = bodies.insert(rb); 71 | 72 | // Build the collider. 73 | let co = ColliderDesc::new(cuboid.clone()) 74 | .density(r!(1.0)) 75 | .build(BodyPartHandle(rb_handle, 0)); 76 | colliders.insert(co); 77 | } 78 | } 79 | } 80 | 81 | /* 82 | * Set up the testbed. 83 | */ 84 | testbed.set_ground_handle(Some(ground_handle)); 85 | testbed.set_world( 86 | mechanical_world, 87 | geometrical_world, 88 | bodies, 89 | colliders, 90 | joint_constraints, 91 | force_generators, 92 | ); 93 | testbed.look_at(Point3::new(-4.0, 1.0, -4.0), Point3::new(0.0, 1.0, 0.0)); 94 | } 95 | 96 | fn main() { 97 | let testbed = Testbed::::from_builders(0, vec![("Boxes", init_world)]); 98 | testbed.run() 99 | } 100 | -------------------------------------------------------------------------------- /examples3d/capsules3.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point3, RealField, Vector3}; 4 | use ncollide3d::shape::{Capsule, Cuboid, ShapeHandle}; 5 | use nphysics3d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics3d::joint::DefaultJointConstraintSet; 7 | use nphysics3d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics3d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed3d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector3::new(r!(0.0), r!(-9.81), r!(0.0))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground. 30 | */ 31 | let ground_thickness = r!(0.2); 32 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector3::new( 33 | r!(3.0), 34 | ground_thickness, 35 | r!(3.0), 36 | ))); 37 | 38 | let ground_handle = bodies.insert(Ground::new()); 39 | let co = ColliderDesc::new(ground_shape) 40 | .translation(Vector3::y() * -ground_thickness) 41 | .build(BodyPartHandle(ground_handle, 0)); 42 | colliders.insert(co); 43 | 44 | /* 45 | * Create the boxes 46 | */ 47 | let num = 5; 48 | let rad = r!(0.1); 49 | let half_height = r!(0.2); 50 | 51 | let capsule = ShapeHandle::new(Capsule::new(half_height, rad)); 52 | 53 | let shift = (rad + ColliderDesc::::default_margin()) * r!(2.0); 54 | let shifty = (rad + half_height + ColliderDesc::::default_margin()) * r!(2.0); 55 | let centerx = shift * r!(num as f64) / r!(2.0); 56 | let centery = shifty / r!(2.0); 57 | let centerz = shift * r!(num as f64) / r!(2.0); 58 | let altitude = r!(0.0); 59 | 60 | for i in 0usize..num { 61 | for j in 0usize..num { 62 | for k in 0usize..num { 63 | let x = r!(i as f64) * shift - centerx; 64 | let y = r!(j as f64) * shifty + centery + altitude; 65 | let z = r!(k as f64) * shift - centerz; 66 | 67 | // Build the rigid body. 68 | let rb = RigidBodyDesc::new() 69 | .translation(Vector3::new(x, y, z)) 70 | .build(); 71 | let rb_handle = bodies.insert(rb); 72 | 73 | // Build the collider. 74 | let co = ColliderDesc::new(capsule.clone()) 75 | .density(r!(1.0)) 76 | .build(BodyPartHandle(rb_handle, 0)); 77 | colliders.insert(co); 78 | } 79 | } 80 | } 81 | 82 | /* 83 | * Set up the testbed. 84 | */ 85 | testbed.set_ground_handle(Some(ground_handle)); 86 | testbed.set_world( 87 | mechanical_world, 88 | geometrical_world, 89 | bodies, 90 | colliders, 91 | joint_constraints, 92 | force_generators, 93 | ); 94 | testbed.look_at(Point3::new(-4.0, 1.0, -4.0), Point3::new(0.0, 1.0, 0.0)); 95 | } 96 | 97 | fn main() { 98 | let testbed = Testbed::::from_builders(0, vec![("Capsules", init_world)]); 99 | testbed.run() 100 | } 101 | -------------------------------------------------------------------------------- /examples3d/convex3.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point3, RealField, Vector3}; 4 | use ncollide3d::shape::{ConvexHull, Cuboid, ShapeHandle}; 5 | use nphysics3d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics3d::joint::DefaultJointConstraintSet; 7 | use nphysics3d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics3d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed3d::Testbed; 12 | 13 | use rand::distributions::{Distribution, Standard}; 14 | use rand::{rngs::StdRng, SeedableRng}; 15 | 16 | /* 17 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 18 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 19 | */ 20 | pub fn init_world(testbed: &mut Testbed) { 21 | /* 22 | * World 23 | */ 24 | let mechanical_world = DefaultMechanicalWorld::new(Vector3::new(r!(0.0), r!(-9.81), r!(0.0))); 25 | let geometrical_world = DefaultGeometricalWorld::new(); 26 | let mut bodies = DefaultBodySet::new(); 27 | let mut colliders = DefaultColliderSet::new(); 28 | let joint_constraints = DefaultJointConstraintSet::new(); 29 | let force_generators = DefaultForceGeneratorSet::new(); 30 | 31 | /* 32 | * Ground 33 | */ 34 | let ground_thickness = r!(0.2); 35 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector3::new( 36 | r!(3.0), 37 | ground_thickness, 38 | r!(3.0), 39 | ))); 40 | 41 | let ground_handle = bodies.insert(Ground::new()); 42 | let co = ColliderDesc::new(ground_shape) 43 | .translation(Vector3::y() * -ground_thickness) 44 | .build(BodyPartHandle(ground_handle, 0)); 45 | colliders.insert(co); 46 | 47 | /* 48 | * Create the convex geometries. 49 | */ 50 | let npts = 10usize; 51 | let num = 6; 52 | let shift = r!(0.4); 53 | let centerx = shift * r!(num as f64) / r!(2.0); 54 | let centery = shift / r!(2.0); 55 | let centerz = shift * r!(num as f64) / r!(2.0); 56 | let mut rng = StdRng::seed_from_u64(0); 57 | let distribution = Standard; 58 | 59 | for i in 0usize..num { 60 | for j in 0usize..num { 61 | for k in 0usize..num { 62 | let x = r!(i as f64) * shift - centerx; 63 | let y = r!(j as f64) * shift + centery; 64 | let z = r!(k as f64) * shift - centerz; 65 | 66 | let mut pts = Vec::with_capacity(npts); 67 | 68 | for _ in 0..npts { 69 | let pt: Point3 = distribution.sample(&mut rng); 70 | pts.push((na::convert::<_, Point3>(pt) * r!(0.4)).into()); 71 | } 72 | 73 | // Build the rigid body. 74 | let rb = RigidBodyDesc::new() 75 | .translation(Vector3::new(x, y, z)) 76 | .build(); 77 | let rb_handle = bodies.insert(rb); 78 | 79 | // Build the collider. 80 | if let Some(chull) = ConvexHull::try_from_points(&pts) { 81 | let geom = ShapeHandle::new(chull); 82 | let co = ColliderDesc::new(geom) 83 | .density(r!(1.0)) 84 | .build(BodyPartHandle(rb_handle, 0)); 85 | colliders.insert(co); 86 | } 87 | } 88 | } 89 | } 90 | 91 | /* 92 | * Set up the testbed. 93 | */ 94 | testbed.set_ground_handle(Some(ground_handle)); 95 | testbed.set_world( 96 | mechanical_world, 97 | geometrical_world, 98 | bodies, 99 | colliders, 100 | joint_constraints, 101 | force_generators, 102 | ); 103 | testbed.look_at(Point3::new(-4.0, 1.0, -4.0), Point3::new(0.0, 1.0, 0.0)); 104 | } 105 | 106 | fn main() { 107 | let testbed = Testbed::::from_builders(0, vec![("Convex", init_world)]); 108 | 109 | testbed.run() 110 | } 111 | -------------------------------------------------------------------------------- /examples3d/damping3.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use std::f32; 4 | 5 | use na::{Point3, RealField, Vector3}; 6 | use ncollide3d::shape::{Cuboid, ShapeHandle}; 7 | use nphysics3d::force_generator::DefaultForceGeneratorSet; 8 | use nphysics3d::joint::DefaultJointConstraintSet; 9 | use nphysics3d::math::Velocity; 10 | use nphysics3d::object::{ 11 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 12 | }; 13 | use nphysics3d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 14 | use nphysics_testbed3d::Testbed; 15 | 16 | /* 17 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 18 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 19 | */ 20 | pub fn init_world(testbed: &mut Testbed) { 21 | /* 22 | * World 23 | */ 24 | let mechanical_world = DefaultMechanicalWorld::new(na::zero()); 25 | let geometrical_world = DefaultGeometricalWorld::new(); 26 | let mut bodies = DefaultBodySet::new(); 27 | let mut colliders = DefaultColliderSet::new(); 28 | let joint_constraints = DefaultJointConstraintSet::new(); 29 | let force_generators = DefaultForceGeneratorSet::new(); 30 | 31 | /* 32 | * Create the balls 33 | */ 34 | let num = 10; 35 | let rad = r!(0.2); 36 | 37 | let cube = ShapeHandle::new(Cuboid::new(Vector3::repeat(rad))); 38 | let subdiv = r!(1.0 / (num as f64)); 39 | 40 | for i in 0usize..num { 41 | let (x, y) = (r!(i as f64) * subdiv * r!(std::f64::consts::PI) * r!(2.0)).sin_cos(); 42 | let dir = Vector3::new(x, y, r!(0.0)); 43 | 44 | // Build the rigid body. 45 | let rb = RigidBodyDesc::new() 46 | .translation(dir) 47 | .velocity(Velocity::new(dir * r!(10.0), Vector3::z() * r!(100.0))) 48 | .linear_damping(r!((i + 1) as f64) * subdiv * r!(10.0)) 49 | .angular_damping(r!((num - i) as f64) * subdiv * r!(10.0)) 50 | .build(); 51 | let rb_handle = bodies.insert(rb); 52 | 53 | // Build the collider. 54 | let co = ColliderDesc::new(cube.clone()) 55 | .density(r!(1.0)) 56 | .build(BodyPartHandle(rb_handle, 0)); 57 | colliders.insert(co); 58 | } 59 | 60 | /* 61 | * Set up the testbed. 62 | */ 63 | // We add a ground to make dragging object work (this is a requirement of the testbed, not nphysics itself). 64 | let ground_handle = bodies.insert(Ground::new()); 65 | testbed.set_ground_handle(Some(ground_handle)); 66 | testbed.set_world( 67 | mechanical_world, 68 | geometrical_world, 69 | bodies, 70 | colliders, 71 | joint_constraints, 72 | force_generators, 73 | ); 74 | testbed.look_at(Point3::new(2.0, 2.5, 20.0), Point3::new(2.0, 2.5, 0.0)); 75 | } 76 | 77 | fn main() { 78 | let testbed = Testbed::::from_builders(0, vec![("Damping", init_world)]); 79 | testbed.run() 80 | } 81 | -------------------------------------------------------------------------------- /examples3d/dzhanibekov3.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry3, Point3, RealField, Vector3}; 4 | use ncollide3d::shape::{Compound, Cuboid, ShapeHandle}; 5 | use nphysics3d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics3d::joint::DefaultJointConstraintSet; 7 | use nphysics3d::math::Velocity; 8 | use nphysics3d::object::{ 9 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, RigidBodyDesc, 10 | }; 11 | use nphysics3d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 12 | use nphysics_testbed3d::Testbed; 13 | 14 | /* 15 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 16 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 17 | */ 18 | pub fn init_world(testbed: &mut Testbed) { 19 | /* 20 | * World 21 | */ 22 | let mechanical_world = DefaultMechanicalWorld::new(Vector3::zeros()); 23 | let geometrical_world = DefaultGeometricalWorld::new(); 24 | let mut bodies = DefaultBodySet::new(); 25 | let mut colliders = DefaultColliderSet::new(); 26 | let joint_constraints = DefaultJointConstraintSet::new(); 27 | let force_generators = DefaultForceGeneratorSet::new(); 28 | 29 | /* 30 | * Create boxes to compute the inertia. 31 | */ 32 | let mut shapes = Vec::new(); 33 | shapes.push(( 34 | Isometry3::identity(), 35 | ShapeHandle::new(Cuboid::new(Vector3::new(r!(1.0), r!(0.1), r!(0.1)))), 36 | )); 37 | shapes.push(( 38 | Isometry3::translation(r!(0.0), r!(0.4), r!(0.0)), 39 | ShapeHandle::new(Cuboid::new(Vector3::new(r!(0.1), r!(0.2), r!(0.1)))), 40 | )); 41 | 42 | /* 43 | * Create the rigid body. 44 | */ 45 | let rb = RigidBodyDesc::new() 46 | .velocity(Velocity::angular(r!(0.0), r!(10.0), r!(0.1))) 47 | .build(); 48 | let rb_handle = bodies.insert(rb); 49 | 50 | /* 51 | * Create the collider from which the inertia will be automatically computed. 52 | */ 53 | let geom = ShapeHandle::new(Compound::new(shapes)); 54 | let co = ColliderDesc::new(geom) 55 | .density(r!(1.0)) 56 | .build(BodyPartHandle(rb_handle, 0)); 57 | colliders.insert(co); 58 | 59 | /* 60 | * Set up the testbed. 61 | */ 62 | testbed.set_world( 63 | mechanical_world, 64 | geometrical_world, 65 | bodies, 66 | colliders, 67 | joint_constraints, 68 | force_generators, 69 | ); 70 | testbed.look_at(Point3::new(0.0, 0.0, 5.0), Point3::new(0.0, 0.0, 0.0)); 71 | } 72 | 73 | fn main() { 74 | let testbed = Testbed::::from_builders(0, vec![("Dzhanibekov effect", init_world)]); 75 | 76 | testbed.run() 77 | } 78 | -------------------------------------------------------------------------------- /examples3d/fem_volume3.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point3, RealField, Vector3}; 4 | use ncollide3d::shape::{Cuboid, ShapeHandle}; 5 | use nphysics3d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics3d::joint::DefaultJointConstraintSet; 7 | use nphysics3d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, FEMVolumeDesc, Ground, 9 | }; 10 | use nphysics3d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed3d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector3::new(r!(0.0), r!(-9.81), r!(0.0))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Ground. 30 | */ 31 | // Static body to which all the obstacles and the ground will be attached. 32 | let ground_handle = bodies.insert(Ground::new()); 33 | 34 | let ground_thickness = r!(0.2); 35 | let ground = ShapeHandle::new(Cuboid::new(Vector3::new( 36 | r!(3.0), 37 | ground_thickness, 38 | r!(3.0), 39 | ))); 40 | 41 | let co = ColliderDesc::new(ground) 42 | .translation(Vector3::y() * (-ground_thickness - r!(1.0))) 43 | .build(BodyPartHandle(ground_handle, 0)); 44 | colliders.insert(co); 45 | 46 | let ground_size = r!(3.0); 47 | let obstacle = ShapeHandle::new(Cuboid::new(Vector3::new(r!(0.02), r!(0.02), ground_size))); 48 | 49 | let mut obstacle_desc = ColliderDesc::new(obstacle); 50 | 51 | let co = obstacle_desc 52 | .set_translation(Vector3::new(r!(0.4), r!(-0.01), r!(0.0))) 53 | .build(BodyPartHandle(ground_handle, 0)); 54 | colliders.insert(co); 55 | 56 | let co = obstacle_desc 57 | .set_translation(Vector3::new(r!(-0.4), r!(-0.01), r!(0.0))) 58 | .build(BodyPartHandle(ground_handle, 0)); 59 | colliders.insert(co); 60 | 61 | /* 62 | * Create the deformable body and a collider for its boundary. 63 | */ 64 | let mut fem_body = FEMVolumeDesc::cube(20, 1, 1) 65 | .scale(Vector3::new(r!(1.0), r!(0.1), r!(0.1))) 66 | .translation(Vector3::y() * r!(0.1)) 67 | .young_modulus(r!(1.0e3)) 68 | .poisson_ratio(r!(0.2)) 69 | .mass_damping(r!(0.2)) 70 | .build(); 71 | let boundary_desc = fem_body.boundary_collider_desc(); 72 | let fem_body_handle = bodies.insert(fem_body); 73 | 74 | let co = boundary_desc.build(fem_body_handle); 75 | colliders.insert(co); 76 | 77 | /* 78 | * Set up the testbed. 79 | */ 80 | testbed.set_ground_handle(Some(ground_handle)); 81 | testbed.set_world( 82 | mechanical_world, 83 | geometrical_world, 84 | bodies, 85 | colliders, 86 | joint_constraints, 87 | force_generators, 88 | ); 89 | testbed.look_at(Point3::new(0.0, 0.0, 2.0), Point3::new(0.0, 0.0, 0.0)); 90 | } 91 | 92 | fn main() { 93 | let testbed = Testbed::::from_builders(0, vec![("FEM volume", init_world)]); 94 | testbed.run() 95 | } 96 | -------------------------------------------------------------------------------- /examples3d/fixed_bug_long_thin_box_one_shot_manifold3.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Expected behaviour: 3 | * The box stands vertically until it falls asleep. 4 | * The box should not fall (horizontally) on the ground. 5 | * The box should not traverse the ground. 6 | * 7 | * # Symptoms: 8 | * The long, thin, box fails to collide with the plane: it just ignores it. 9 | * 10 | * # Cause: 11 | * The one shot contact manifold generator was incorrect in this case. This generator rotated the 12 | * object wrt its center to sample the contact manifold. If the object is long and the theoretical 13 | * contact surface is small, all contacts will be invalidated whenever the incremental contact 14 | * manifold will get a new point from the one-shot generator. 15 | * 16 | * # Solution: 17 | * Rotate the object wrt the contact center, not wrt the object center. 18 | * 19 | * # Limitations of the solution: 20 | * This will create only a three-points manifold for a small axis-alligned cube, instead of four. 21 | */ 22 | 23 | extern crate nalgebra as na; 24 | extern crate ncollide3d; 25 | extern crate nphysics3d; 26 | extern crate nphysics_testbed3d; 27 | 28 | use na::{Point3, Vector3}; 29 | use ncollide3d::shape::{Cuboid, Plane, ShapeHandle}; 30 | use nphysics3d::object::{ColliderDesc, RigidBodyDesc}; 31 | use nphysics3d::world::World; 32 | use nphysics_testbed3d::Testbed; 33 | 34 | fn main() { 35 | /* 36 | * World 37 | */ 38 | let mut world = World::new(); 39 | world.set_gravity(Vector3::new(r!(0.0), r!(-9.81), r!(0.0))); 40 | 41 | /* 42 | * Plane 43 | */ 44 | let ground_shape = ShapeHandle::new(Plane::new(Vector3::y_axis())); 45 | ColliderDesc::new(ground_shape).build(&mut world); 46 | 47 | /* 48 | * Create the box 49 | */ 50 | let rad = r!(0.1); 51 | let x = rad; 52 | let y = rad + 10.0; 53 | let z = rad; 54 | 55 | let geom = ShapeHandle::new(Cuboid::new(Vector3::new(rad, rad * 10.0, rad))); 56 | let collider_desc = ColliderDesc::new(geom).density(r!(1.0)); 57 | 58 | RigidBodyDesc::new() 59 | .collider(&collider_desc) 60 | .translation(Vector3::new(x, y, z)) 61 | .build(&mut world); 62 | 63 | /* 64 | * Set up the testbed. 65 | */ 66 | let mut testbed = Testbed::new(world); 67 | 68 | testbed.look_at(Point3::new(-30.0, 30.0, -30.0), Point3::new(0.0, 0.0, 0.0)); 69 | testbed.run(); 70 | } 71 | -------------------------------------------------------------------------------- /examples3d/fixed_bug_tree_like_multibody3.rs: -------------------------------------------------------------------------------- 1 | // Issue #110 2 | extern crate nalgebra as na; 3 | extern crate ncollide3d; 4 | extern crate nphysics3d; 5 | extern crate nphysics_testbed3d; 6 | 7 | use na::{Isometry3, Point3, Vector3}; 8 | use ncollide3d::shape::{Cuboid, ShapeHandle}; 9 | use nphysics3d::joint::{FixedJoint, FreeJoint, Joint}; 10 | use nphysics3d::object::{BodyPartHandle, ColliderDesc, MultibodyDesc}; 11 | use nphysics3d::world::World; 12 | use nphysics_testbed3d::Testbed; 13 | 14 | fn new_link>( 15 | world: &mut World, 16 | joint: J, 17 | parent: BodyPartHandle, 18 | ) -> BodyPartHandle { 19 | let shape = ShapeHandle::new(Cuboid::new(Vector3::new(0.2, 0.2, 0.2))); 20 | let collider_desc = ColliderDesc::new(shape).density(r!(1.0)); 21 | 22 | MultibodyDesc::new(joint) 23 | .collider(&collider_desc) 24 | .build_with_parent(parent, world) 25 | .unwrap() 26 | .part_handle() 27 | } 28 | 29 | fn main() { 30 | let mut world: World = World::new(); 31 | world.set_gravity(Vector3::y() * -9.81); 32 | 33 | /* 34 | * Ground. 35 | */ 36 | let ground_size = 50.0; 37 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector3::repeat(ground_size))); 38 | 39 | ColliderDesc::new(ground_shape) 40 | .translation(Vector3::y() * (-ground_size - 5.0)) 41 | .build(&mut world); 42 | 43 | /* 44 | * Tree-like structure. 45 | */ 46 | let root = new_link( 47 | &mut world, 48 | FreeJoint::new(Isometry3::identity()), 49 | BodyPartHandle::ground(), 50 | ); 51 | 52 | let left_joint = FixedJoint::new(Isometry3::translation(-1.0, 1.5, 0.0)); 53 | let right_joint = FixedJoint::new(Isometry3::translation(1.0, 1.5, 0.0)); 54 | 55 | // two children under root 56 | let a = new_link(&mut world, left_joint, root); 57 | let b = new_link(&mut world, right_joint, root); 58 | 59 | new_link(&mut world, left_joint, a); 60 | new_link(&mut world, right_joint, a); 61 | new_link(&mut world, right_joint, b); 62 | 63 | /* 64 | * Set up the testbed. 65 | */ 66 | let mut testbed = Testbed::new(world); 67 | testbed.look_at(Point3::new(-1.0, 5.0, -1.0), Point3::new(0.0, 0.0, 0.0)); 68 | testbed.run(); 69 | } 70 | -------------------------------------------------------------------------------- /examples3d/known_bug_excentric_convex3.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | * # Expected behaviour: 3 | * Same as the box_vee3d demo. 4 | * 5 | * It seems to behave as expected if the excentricity is not too big (tested with 10 and 100). 6 | * 7 | * # Symptoms: 8 | * Some objets jitter when they touch the ground. 9 | * 10 | * # Cause: 11 | * Not sure, but this seems to be an accuracy limitation of the contact manifold generators. 12 | * 13 | * This might be (but it is very unlikely) a problem with the DBVT that might become invalid. 14 | * Though this seems very unlikely as the AABBs seem to be fine and the plane has an infinite aabb 15 | * anyway. Thurthermore, the ray-cast (which uses the dbvt…) works fine, even for "jumpy" objects. 16 | * 17 | * 18 | * # Solution: 19 | * 20 | */ 21 | 22 | extern crate nalgebra as na; 23 | extern crate ncollide3d; 24 | extern crate nphysics3d; 25 | extern crate nphysics_testbed3d; 26 | 27 | use na::{Isometry3, Point3, Vector3}; 28 | use ncollide3d::procedural; 29 | use ncollide3d::shape::{ConvexHull, Cuboid, ShapeHandle}; 30 | use nphysics3d::object::{ColliderDesc, RigidBodyDesc}; 31 | use nphysics3d::volumetric::Volumetric; 32 | use nphysics3d::world::World; 33 | use nphysics_testbed3d::Testbed; 34 | 35 | /* 36 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 37 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 38 | */ 39 | fn main() { 40 | /* 41 | * World 42 | */ 43 | let mut world = World::new(); 44 | world.set_gravity(Vector3::new(r!(0.0), r!(-9.81), r!(0.0))); 45 | 46 | /* 47 | * Ground 48 | */ 49 | let ground_size = 50.0; 50 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector3::repeat(ground_size))); 51 | 52 | ColliderDesc::new(ground_shape) 53 | .translation(Vector3::y() * -ground_size) 54 | .build(&mut world); 55 | 56 | /* 57 | * Create the convex geometries. 58 | */ 59 | let num = 8; 60 | let rad = r!(0.1); 61 | let excentricity = 1000.0f32; 62 | 63 | let pts = vec![ 64 | Point3::new(-rad, -rad, -rad) + Vector3::repeat(excentricity), 65 | Point3::new(-rad, -rad, rad) + Vector3::repeat(excentricity), 66 | Point3::new(-rad, rad, -rad) + Vector3::repeat(excentricity), 67 | Point3::new(-rad, rad, rad) + Vector3::repeat(excentricity), 68 | Point3::new(rad, -rad, -rad) + Vector3::repeat(excentricity), 69 | Point3::new(rad, -rad, rad) + Vector3::repeat(excentricity), 70 | Point3::new(rad, rad, -rad) + Vector3::repeat(excentricity), 71 | Point3::new(rad, rad, rad) + Vector3::repeat(excentricity), 72 | ]; 73 | 74 | let shape = ShapeHandle::new(ConvexHull::try_from_points(&pts).unwrap()); 75 | let collider_desc = ColliderDesc::new(shape).density(r!(1.0)); 76 | 77 | let mut rb_desc = RigidBodyDesc::new().collider(&collider_desc); 78 | 79 | let shift = (rad + collider_desc.get_margin()) * r!(2.0); 80 | let centerx = shift * r!(num as f64) / r!(2.0); 81 | let centery = shift / r!(2.0); 82 | let centerz = shift * r!(num as f64) / r!(2.0); 83 | 84 | for i in 0usize..num { 85 | for j in 0usize..num { 86 | for k in 0usize..num { 87 | let x = r!(i as f64) * shift - centerx - excentricity; 88 | let y = r!(j as f64) * shift + centery - excentricity; 89 | let z = r!(k as f64) * shift - centerz - excentricity; 90 | 91 | // Build the rigid body and its collider. 92 | rb_desc 93 | .set_translation(Vector3::new(x, y, z)) 94 | .build(&mut world); 95 | } 96 | } 97 | } 98 | 99 | /* 100 | * Set up the testbed. 101 | */ 102 | let mut testbed = Testbed::new(world); 103 | 104 | testbed.look_at(Point3::new(-10.0, 10.0, -10.0), Point3::new(0.0, 0.0, 0.0)); 105 | testbed.run(); 106 | } 107 | -------------------------------------------------------------------------------- /examples3d/pyramid3.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point3, RealField, Vector3}; 4 | use ncollide3d::shape::{Cuboid, ShapeHandle}; 5 | use nphysics3d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics3d::joint::DefaultJointConstraintSet; 7 | use nphysics3d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics3d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed3d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector3::new(r!(0.0), r!(-9.81), r!(0.0))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Planes 30 | */ 31 | let ground_handle = bodies.insert(Ground::new()); 32 | 33 | let ground_thickness = r!(0.2); 34 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector3::new(6.0, ground_thickness, 6.0))); 35 | 36 | let co = ColliderDesc::new(ground_shape) 37 | .translation(Vector3::y() * -ground_thickness) 38 | .build(BodyPartHandle(ground_handle, 0)); 39 | colliders.insert(co); 40 | 41 | /* 42 | * Create the boxes 43 | */ 44 | let num = 30; 45 | let rad = r!(0.1); 46 | 47 | let cuboid = ShapeHandle::new(Cuboid::new(Vector3::repeat(rad))); 48 | 49 | let shift = (rad + ColliderDesc::::default_margin()) * r!(2.0); 50 | let centerx = shift * r!(num as f64) / r!(2.0); 51 | let centery = shift / r!(2.0); 52 | 53 | for i in 0usize..num { 54 | for j in i..num { 55 | let fi = i as f64; 56 | let fj = (j - i) as f64; 57 | let x = (fi * shift / r!(2.0)) + fj * shift - centerx; 58 | let y = fi * shift + centery; 59 | 60 | // Build the rigid body. 61 | let rb = RigidBodyDesc::new() 62 | .translation(Vector3::new(x, y, 0.0)) 63 | .build(); 64 | let rb_handle = bodies.insert(rb); 65 | 66 | // Build the collider. 67 | let co = ColliderDesc::new(cuboid.clone()) 68 | .density(r!(1.0)) 69 | .build(BodyPartHandle(rb_handle, 0)); 70 | colliders.insert(co); 71 | } 72 | } 73 | 74 | /* 75 | * Set up the testbed. 76 | */ 77 | testbed.set_ground_handle(Some(ground_handle)); 78 | testbed.set_world( 79 | mechanical_world, 80 | geometrical_world, 81 | bodies, 82 | colliders, 83 | joint_constraints, 84 | force_generators, 85 | ); 86 | testbed.look_at(Point3::new(-4.0, 1.0, -4.0), Point3::new(0.0, 1.0, 0.0)); 87 | } 88 | 89 | fn main() { 90 | let testbed = Testbed::::from_builders(0, vec![("Pyramid", init_world)]); 91 | 92 | testbed.run() 93 | } 94 | -------------------------------------------------------------------------------- /examples3d/wall3.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Point3, RealField, Vector3}; 4 | use ncollide3d::shape::{Cuboid, ShapeHandle}; 5 | use nphysics3d::force_generator::DefaultForceGeneratorSet; 6 | use nphysics3d::joint::DefaultJointConstraintSet; 7 | use nphysics3d::object::{ 8 | BodyPartHandle, ColliderDesc, DefaultBodySet, DefaultColliderSet, Ground, RigidBodyDesc, 9 | }; 10 | use nphysics3d::world::{DefaultGeometricalWorld, DefaultMechanicalWorld}; 11 | use nphysics_testbed3d::Testbed; 12 | 13 | /* 14 | * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. 15 | * This simplifies experimentation with various scalar types (f32, fixed-point numbers, etc.) 16 | */ 17 | pub fn init_world(testbed: &mut Testbed) { 18 | /* 19 | * World 20 | */ 21 | let mechanical_world = DefaultMechanicalWorld::new(Vector3::new(r!(0.0), r!(-9.81), r!(0.0))); 22 | let geometrical_world = DefaultGeometricalWorld::new(); 23 | let mut bodies = DefaultBodySet::new(); 24 | let mut colliders = DefaultColliderSet::new(); 25 | let joint_constraints = DefaultJointConstraintSet::new(); 26 | let force_generators = DefaultForceGeneratorSet::new(); 27 | 28 | /* 29 | * Planes 30 | */ 31 | let ground_thickness = r!(0.2); 32 | let ground_shape = ShapeHandle::new(Cuboid::new(Vector3::new(15.0, ground_thickness, 3.0))); 33 | 34 | let ground_handle = bodies.insert(Ground::new()); 35 | let co = ColliderDesc::new(ground_shape) 36 | .translation(Vector3::y() * -ground_thickness) 37 | .build(BodyPartHandle(ground_handle, 0)); 38 | colliders.insert(co); 39 | 40 | /* 41 | * Create the boxes 42 | */ 43 | let width = 50; 44 | let height = 10; 45 | let rad = r!(0.1); 46 | 47 | let cuboid = ShapeHandle::new(Cuboid::new(Vector3::repeat(rad))); 48 | 49 | let shift = (rad + ColliderDesc::::default_margin()) * r!(2.0); 50 | let centerx = shift * (width as f64) / r!(2.0); 51 | let centery = shift / r!(2.0); 52 | 53 | for i in 0usize..width { 54 | for j in 0usize..height { 55 | let x = r!(i as f64) * shift - centerx; 56 | let y = r!(j as f64) * shift + centery; 57 | 58 | // Build the rigid body. 59 | let rb = RigidBodyDesc::new() 60 | .translation(Vector3::new(x, y, 0.0)) 61 | .build(); 62 | let rb_handle = bodies.insert(rb); 63 | 64 | // Build the collider. 65 | let co = ColliderDesc::new(cuboid.clone()) 66 | .density(r!(1.0)) 67 | .build(BodyPartHandle(rb_handle, 0)); 68 | colliders.insert(co); 69 | } 70 | } 71 | 72 | /* 73 | * Set up the testbed. 74 | */ 75 | testbed.set_ground_handle(Some(ground_handle)); 76 | testbed.set_world( 77 | mechanical_world, 78 | geometrical_world, 79 | bodies, 80 | colliders, 81 | joint_constraints, 82 | force_generators, 83 | ); 84 | testbed.look_at(Point3::new(-5.0, 5.0, -5.0), Point3::new(0.0, 0.0, 0.0)); 85 | } 86 | 87 | fn main() { 88 | let testbed = Testbed::::from_builders(0, vec![("Wall", init_world)]); 89 | 90 | testbed.run() 91 | } 92 | -------------------------------------------------------------------------------- /publish-testbeds.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | tmp=$(mktemp -d) 4 | 5 | echo "$tmp" 6 | 7 | cp -r src "$tmp"/. 8 | cp -r src_testbed "$tmp"/. 9 | cp -r build "$tmp"/. 10 | cp -r LICENSE README.md "$tmp"/. 11 | 12 | ### Publish the 2D version. 13 | sed 's#\.\./\.\./src#src#g' build/nphysics_testbed2d/Cargo.toml > "$tmp"/Cargo.toml 14 | sed -i 's#\.\./nphysics#./build/nphysics#g' "$tmp"/Cargo.toml 15 | currdir=$(pwd) 16 | cd "$tmp" && cargo publish 17 | cd "$currdir" || exit 18 | 19 | 20 | ### Publish the 3D version. 21 | sed 's#\.\./\.\./src#src#g' build/nphysics_testbed3d/Cargo.toml > "$tmp"/Cargo.toml 22 | sed -i 's#\.\./nphysics#./build/nphysics#g' "$tmp"/Cargo.toml 23 | cp -r LICENSE README.md "$tmp"/. 24 | cd "$tmp" && cargo publish 25 | 26 | rm -rf "$tmp" 27 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | tmp=$(mktemp -d) 4 | 5 | echo "$tmp" 6 | 7 | cp -r src "$tmp"/. 8 | cp -r LICENSE README.md "$tmp"/. 9 | 10 | ### Publish the 2D version. 11 | sed 's#\.\./\.\./src#src#g' build/nphysics2d/Cargo.toml > "$tmp"/Cargo.toml 12 | currdir=$(pwd) 13 | cd "$tmp" && cargo publish 14 | cd "$currdir" || exit 15 | 16 | 17 | ### Publish the 3D version. 18 | sed 's#\.\./\.\./src#src#g' build/nphysics3d/Cargo.toml > "$tmp"/Cargo.toml 19 | cp -r LICENSE README.md "$tmp"/. 20 | cd "$tmp" && cargo publish 21 | 22 | rm -rf "$tmp" 23 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | #indent_style = "Block" 2 | #where_single_line = true 3 | #brace_style = "PreferSameLine" -------------------------------------------------------------------------------- /src/algebra/inertia2.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ops::{Add, AddAssign, Mul, Neg}; 3 | 4 | use crate::algebra::{Force2, Velocity2}; 5 | use na::{self, Isometry2, Matrix1, Matrix3, RealField, Vector3}; 6 | 7 | /// The inertia of a rigid body grouping both its mass and its angular inertia. 8 | #[derive(Clone, Copy, Debug)] 9 | pub struct Inertia2 { 10 | /// The linear part (mass) of the inertia. 11 | pub linear: N, 12 | /// The angular inertia. 13 | pub angular: N, 14 | } 15 | 16 | impl Inertia2 { 17 | /// Creates an inertia from its linear and angular components. 18 | pub fn new(linear: N, angular: N) -> Self { 19 | Inertia2 { linear, angular } 20 | } 21 | 22 | /// Creates an inertia from its linear and angular components. 23 | pub fn new_with_angular_matrix(linear: N, angular: Matrix1) -> Self { 24 | Self::new(linear, angular.x) 25 | } 26 | 27 | /// Get the mass. 28 | pub fn mass(&self) -> N { 29 | self.linear 30 | } 31 | 32 | /// Get the inverse mass. 33 | /// 34 | /// Returns 0.0 if the mass is 0.0. 35 | pub fn inv_mass(&self) -> N { 36 | if self.linear.is_zero() { 37 | N::zero() 38 | } else { 39 | self.linear 40 | } 41 | } 42 | 43 | /// Create a zero inertia. 44 | pub fn zero() -> Self { 45 | Inertia2::new(na::zero(), na::zero()) 46 | } 47 | 48 | /// Get the angular inertia tensor. 49 | #[inline] 50 | pub fn angular_matrix(&self) -> &Matrix1 { 51 | unsafe { mem::transmute(&self.angular) } 52 | } 53 | 54 | /// Convert the inertia into a matrix where the mass is represented as a 2x2 55 | /// diagonal matrix on the upper-left corner, and the angular part as a 1x1 56 | /// matrix on the lower-rigth corner. 57 | pub fn to_matrix(&self) -> Matrix3 { 58 | let diag = Vector3::new(self.linear, self.linear, self.angular); 59 | Matrix3::from_diagonal(&diag) 60 | } 61 | 62 | /// Compute the inertia on the given coordinate frame. 63 | pub fn transformed(&self, _: &Isometry2) -> Self { 64 | *self 65 | } 66 | 67 | /// Inverts this inetia matrix. 68 | /// 69 | /// Sets the angular part to zero if it is not invertible. 70 | pub fn inverse(&self) -> Self { 71 | let inv_mass = if self.linear.is_zero() { 72 | N::zero() 73 | } else { 74 | N::one() / self.linear 75 | }; 76 | let inv_angular = if self.angular.is_zero() { 77 | N::zero() 78 | } else { 79 | N::one() / self.angular 80 | }; 81 | Inertia2::new(inv_mass, inv_angular) 82 | } 83 | } 84 | 85 | impl Neg for Inertia2 { 86 | type Output = Self; 87 | 88 | #[inline] 89 | fn neg(self) -> Self { 90 | Self::new(-self.linear, -self.angular) 91 | } 92 | } 93 | 94 | impl Add> for Inertia2 { 95 | type Output = Inertia2; 96 | 97 | #[inline] 98 | fn add(self, rhs: Inertia2) -> Inertia2 { 99 | Inertia2::new(self.linear + rhs.linear, self.angular + rhs.angular) 100 | } 101 | } 102 | 103 | impl AddAssign> for Inertia2 { 104 | #[inline] 105 | fn add_assign(&mut self, rhs: Inertia2) { 106 | self.linear += rhs.linear; 107 | self.angular += rhs.angular; 108 | } 109 | } 110 | 111 | impl Mul> for Inertia2 { 112 | type Output = Force2; 113 | 114 | #[inline] 115 | fn mul(self, rhs: Velocity2) -> Force2 { 116 | Force2::new(rhs.linear * self.linear, self.angular * rhs.angular) 117 | } 118 | } 119 | 120 | // NOTE: This is meaningful when `self` is the inverse inertia. 121 | impl Mul> for Inertia2 { 122 | type Output = Velocity2; 123 | 124 | #[inline] 125 | fn mul(self, rhs: Force2) -> Velocity2 { 126 | Velocity2::new(rhs.linear * self.linear, self.angular * rhs.angular) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/algebra/mod.rs: -------------------------------------------------------------------------------- 1 | //! Dynamics-specific algebraic entities: velocity, forces, and inertias. 2 | 3 | pub use self::velocity2::Velocity2; 4 | pub use self::velocity3::Velocity3; 5 | 6 | pub use self::force2::Force2; 7 | pub use self::force3::Force3; 8 | 9 | pub use self::inertia2::Inertia2; 10 | pub use self::inertia3::Inertia3; 11 | 12 | /// The type of force to be applied with the `.apply_force` methods of a Body. 13 | /// 14 | /// See the [user guide](https://www.nphysics.org/rigid_body_simulations_with_contacts/#one-time-force-application-and-impulses) 15 | /// for details. 16 | #[derive(Clone, Copy, Debug)] 17 | pub enum ForceType { 18 | /// A regular force. 19 | Force, 20 | /// An impulsive force (delta-time is ignored). 21 | Impulse, 22 | /// A direct acceleration change (mass is ignored). 23 | AccelerationChange, 24 | /// A direct velocity change (mass and delta time are ignored). 25 | VelocityChange, 26 | } 27 | 28 | mod velocity2; 29 | mod velocity3; 30 | 31 | mod force2; 32 | mod force3; 33 | 34 | mod inertia2; 35 | mod inertia3; 36 | -------------------------------------------------------------------------------- /src/counters/ccd_counters.rs: -------------------------------------------------------------------------------- 1 | use crate::counters::Timer; 2 | use std::fmt::{Display, Formatter, Result}; 3 | 4 | /// Performance counters related to continuous collision detection (CCD). 5 | #[derive(Default, Clone, Copy)] 6 | pub struct CCDCounters { 7 | /// The number of substeps actually performed by the CCD resolution. 8 | pub num_substeps: usize, 9 | /// The total time spent for TOI computation in the CCD resolution. 10 | pub toi_computation_time: Timer, 11 | /// The total time spent for force computation and integration in the CCD resolution. 12 | pub solver_time: Timer, 13 | /// The total time spent by the broad-phase in the CCD resolution. 14 | pub broad_phase_time: Timer, 15 | /// The total time spent by the narrow-phase in the CCD resolution. 16 | pub narrow_phase_time: Timer, 17 | } 18 | 19 | impl CCDCounters { 20 | /// Creates a new counter initialized to zero. 21 | pub fn new() -> Self { 22 | CCDCounters { 23 | num_substeps: 0, 24 | toi_computation_time: Timer::new(), 25 | solver_time: Timer::new(), 26 | broad_phase_time: Timer::new(), 27 | narrow_phase_time: Timer::new(), 28 | } 29 | } 30 | 31 | /// Resets this counter to 0. 32 | pub fn reset(&mut self) { 33 | self.num_substeps = 0; 34 | self.toi_computation_time.reset(); 35 | self.solver_time.reset(); 36 | self.broad_phase_time.reset(); 37 | self.narrow_phase_time.reset(); 38 | } 39 | } 40 | 41 | impl Display for CCDCounters { 42 | fn fmt(&self, f: &mut Formatter) -> Result { 43 | writeln!(f, "Number of substeps: {}", self.num_substeps)?; 44 | writeln!(f, "TOI computation time: {}", self.toi_computation_time)?; 45 | writeln!(f, "Constraints solver time: {}", self.solver_time)?; 46 | writeln!(f, "Broad-phase time: {}", self.broad_phase_time)?; 47 | writeln!(f, "Narrow-phase time: {}", self.narrow_phase_time) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/counters/collision_detection_counters.rs: -------------------------------------------------------------------------------- 1 | use crate::counters::Timer; 2 | use std::fmt::{Display, Formatter, Result}; 3 | 4 | /// Performance counters related to collision detection. 5 | #[derive(Default, Clone, Copy)] 6 | pub struct CollisionDetectionCounters { 7 | /// Number of contact pairs detected. 8 | pub ncontact_pairs: usize, 9 | /// Time spent for the broad-phase of the collision detection. 10 | pub broad_phase_time: Timer, 11 | /// Time spent for the narrow-phase of the collision detection. 12 | pub narrow_phase_time: Timer, 13 | } 14 | 15 | impl CollisionDetectionCounters { 16 | /// Creates a new counter initialized to zero. 17 | pub fn new() -> Self { 18 | CollisionDetectionCounters { 19 | ncontact_pairs: 0, 20 | broad_phase_time: Timer::new(), 21 | narrow_phase_time: Timer::new(), 22 | } 23 | } 24 | } 25 | 26 | impl Display for CollisionDetectionCounters { 27 | fn fmt(&self, f: &mut Formatter) -> Result { 28 | writeln!(f, "Number of contact pairs: {}", self.ncontact_pairs)?; 29 | writeln!(f, "Broad-phase time: {}", self.broad_phase_time)?; 30 | writeln!(f, "Narrow-phase time: {}", self.narrow_phase_time) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/counters/solver_counters.rs: -------------------------------------------------------------------------------- 1 | use crate::counters::Timer; 2 | use std::fmt::{Display, Formatter, Result}; 3 | 4 | /// Performance counters related to constraints resolution. 5 | #[derive(Default, Clone, Copy)] 6 | pub struct SolverCounters { 7 | /// Number of constraints generated. 8 | pub nconstraints: usize, 9 | /// Number of contacts found. 10 | pub ncontacts: usize, 11 | /// Time spent for the resolution of the constraints (force computation). 12 | pub velocity_resolution_time: Timer, 13 | /// Time spent for the assembly of all the constraints into a linear complentarity problem. 14 | pub assembly_time: Timer, 15 | /// Time spent for the update of the velocity of the bodies. 16 | pub velocity_update_time: Timer, 17 | /// Time spent for the update of the position of the bodies. 18 | pub position_resolution_time: Timer, 19 | } 20 | 21 | impl SolverCounters { 22 | /// Creates a new counter initialized to zero. 23 | pub fn new() -> Self { 24 | SolverCounters { 25 | nconstraints: 0, 26 | ncontacts: 0, 27 | assembly_time: Timer::new(), 28 | velocity_resolution_time: Timer::new(), 29 | velocity_update_time: Timer::new(), 30 | position_resolution_time: Timer::new(), 31 | } 32 | } 33 | } 34 | 35 | impl Display for SolverCounters { 36 | fn fmt(&self, f: &mut Formatter) -> Result { 37 | writeln!(f, "Number of contacts: {}", self.ncontacts)?; 38 | writeln!(f, "Number of constraints: {}", self.nconstraints)?; 39 | writeln!(f, "Assembly time: {}", self.assembly_time)?; 40 | writeln!( 41 | f, 42 | "Velocity resolution time: {}", 43 | self.velocity_resolution_time 44 | )?; 45 | writeln!(f, "Velocity update time: {}", self.velocity_update_time)?; 46 | writeln!( 47 | f, 48 | "Position resolution time: {}", 49 | self.position_resolution_time 50 | ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/counters/stages_counters.rs: -------------------------------------------------------------------------------- 1 | use crate::counters::Timer; 2 | use std::fmt::{Display, Formatter, Result}; 3 | 4 | /// Performance counters related to each stage of the time step. 5 | #[derive(Default, Clone, Copy)] 6 | pub struct StagesCounters { 7 | /// Time spent for updating the kinematic and dynamics of every body. 8 | pub update_time: Timer, 9 | /// Total time spent for the collision detection (including both broad- and narrow- phases). 10 | pub collision_detection_time: Timer, 11 | /// Time spent for the computation of collision island and body activation/deactivation (sleeping). 12 | pub island_construction_time: Timer, 13 | /// Total time spent for the constraints resolution and position update.t 14 | pub solver_time: Timer, 15 | /// Total time spent for CCD and CCD resolution. 16 | pub ccd_time: Timer, 17 | } 18 | 19 | impl StagesCounters { 20 | /// Create a new counter intialized to zero. 21 | pub fn new() -> Self { 22 | StagesCounters { 23 | update_time: Timer::new(), 24 | collision_detection_time: Timer::new(), 25 | island_construction_time: Timer::new(), 26 | solver_time: Timer::new(), 27 | ccd_time: Timer::new(), 28 | } 29 | } 30 | } 31 | 32 | impl Display for StagesCounters { 33 | fn fmt(&self, f: &mut Formatter) -> Result { 34 | writeln!(f, "Update time: {}", self.update_time)?; 35 | writeln!( 36 | f, 37 | "Collision detection time: {}", 38 | self.collision_detection_time 39 | )?; 40 | writeln!( 41 | f, 42 | "Island construction time: {}", 43 | self.island_construction_time 44 | )?; 45 | writeln!(f, "Solver time: {}", self.solver_time)?; 46 | writeln!(f, "CCD time: {}", self.ccd_time) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/counters/timer.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Error, Formatter}; 2 | 3 | /// A timer. 4 | #[derive(Copy, Clone, Debug, Default)] 5 | pub struct Timer { 6 | time: f64, 7 | start: Option, 8 | } 9 | 10 | impl Timer { 11 | /// Creates a new timer initialized to zero and not started. 12 | pub fn new() -> Self { 13 | Timer { 14 | time: 0.0, 15 | start: None, 16 | } 17 | } 18 | 19 | /// Resets the timer to 0. 20 | pub fn reset(&mut self) { 21 | self.time = 0.0 22 | } 23 | 24 | /// Start the timer. 25 | pub fn start(&mut self) { 26 | self.time = 0.0; 27 | self.start = Some(instant::now()); 28 | } 29 | 30 | /// Pause the timer. 31 | pub fn pause(&mut self) { 32 | if let Some(start) = self.start { 33 | self.time += instant::now() - start; 34 | } 35 | self.start = None; 36 | } 37 | 38 | /// Resume the timer. 39 | pub fn resume(&mut self) { 40 | self.start = Some(instant::now()); 41 | } 42 | 43 | /// The measured time between the last `.start()` and `.pause()` calls. 44 | pub fn time(&self) -> f64 { 45 | self.time 46 | } 47 | } 48 | 49 | impl Display for Timer { 50 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 51 | write!(f, "{}s", self.time) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/detection/mod.rs: -------------------------------------------------------------------------------- 1 | //! Collision detection information. 2 | 3 | pub use self::activation_manager::ActivationManager; 4 | pub use self::collider_contact_manifold::ColliderContactManifold; 5 | 6 | mod activation_manager; 7 | mod collider_contact_manifold; 8 | -------------------------------------------------------------------------------- /src/force_generator/constant_acceleration.rs: -------------------------------------------------------------------------------- 1 | use na::RealField; 2 | 3 | use crate::force_generator::ForceGenerator; 4 | use crate::math::{Force, ForceType, Vector, Velocity}; 5 | use crate::object::{BodyHandle, BodyPartHandle, BodySet}; 6 | use crate::solver::IntegrationParameters; 7 | 8 | /// Force generator adding a constant acceleration 9 | /// at the center of mass of a set of body parts. 10 | pub struct ConstantAcceleration { 11 | parts: Vec>, 12 | acceleration: Velocity, 13 | } 14 | 15 | impl ConstantAcceleration { 16 | /// Adds a new constant acceleration generator. 17 | /// 18 | /// The acceleration is expressed in world-space. 19 | #[cfg(feature = "dim3")] 20 | pub fn new(linear_acc: Vector, angular_acc: Vector) -> Self { 21 | ConstantAcceleration { 22 | parts: Vec::new(), 23 | acceleration: Velocity::new(linear_acc, angular_acc), 24 | } 25 | } 26 | 27 | /// Adds a new constant acceleration generator. 28 | /// 29 | /// The acceleration is expressed in world-space. 30 | #[cfg(feature = "dim2")] 31 | pub fn new(linear_acc: Vector, angular_acc: N) -> Self { 32 | ConstantAcceleration { 33 | parts: Vec::new(), 34 | acceleration: Velocity::new(linear_acc, angular_acc), 35 | } 36 | } 37 | 38 | /// Add a body part to be affected by this force generator. 39 | pub fn add_body_part(&mut self, body: BodyPartHandle) { 40 | self.parts.push(body) 41 | } 42 | } 43 | 44 | impl ForceGenerator 45 | for ConstantAcceleration 46 | { 47 | fn apply( 48 | &mut self, 49 | _: &IntegrationParameters, 50 | bodies: &mut dyn BodySet, 51 | ) { 52 | let acceleration = self.acceleration; 53 | self.parts.retain(|h| { 54 | if let Some(body) = bodies.get_mut(h.0) { 55 | body.apply_force( 56 | h.1, 57 | &Force::new(acceleration.linear, acceleration.angular), 58 | ForceType::AccelerationChange, 59 | false, 60 | ); 61 | true 62 | } else { 63 | false 64 | } 65 | }); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/force_generator/force_generator.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] // for downcast. 2 | 3 | use downcast_rs::Downcast; 4 | use generational_arena::Arena; 5 | use na::RealField; 6 | 7 | use crate::object::{BodyHandle, BodySet, DefaultBodyHandle}; 8 | use crate::solver::IntegrationParameters; 9 | 10 | /// Default force generator set based on an arena with generational indices. 11 | pub type DefaultForceGeneratorSet = 12 | Arena>>; 13 | 14 | /// Trait implemented by sets of force generators. 15 | /// 16 | /// A set of bodies maps a force generator handle to a force generator instance. 17 | pub trait ForceGeneratorSet { 18 | /// Type of a force generator stored in this set. 19 | type ForceGenerator: ?Sized + ForceGenerator; 20 | /// Type of a force generator handle identifying a force generator in this set. 21 | type Handle: Copy; 22 | 23 | /// Gets a reference to the force generator identified by `handle`. 24 | fn get(&self, handle: Self::Handle) -> Option<&Self::ForceGenerator>; 25 | /// Gets a mutable reference to the force generator identified by `handle`. 26 | fn get_mut(&mut self, handle: Self::Handle) -> Option<&mut Self::ForceGenerator>; 27 | 28 | /// Check if this set contains a force generator identified by `handle`. 29 | fn contains(&self, handle: Self::Handle) -> bool; 30 | 31 | /// Iterate through all the force generators on this set, applying the closure `f` on them. 32 | fn foreach(&self, f: impl FnMut(Self::Handle, &Self::ForceGenerator)); 33 | /// Mutable iterates through all the force generators on this set, applying the closure `f` on them. 34 | fn foreach_mut(&mut self, f: impl FnMut(Self::Handle, &mut Self::ForceGenerator)); 35 | } 36 | 37 | impl ForceGeneratorSet 38 | for DefaultForceGeneratorSet 39 | { 40 | type ForceGenerator = dyn ForceGenerator; 41 | type Handle = DefaultForceGeneratorHandle; 42 | 43 | fn get(&self, handle: Self::Handle) -> Option<&Self::ForceGenerator> { 44 | self.get(handle).map(|c| &**c) 45 | } 46 | 47 | fn get_mut(&mut self, handle: Self::Handle) -> Option<&mut Self::ForceGenerator> { 48 | self.get_mut(handle).map(|c| &mut **c) 49 | } 50 | 51 | fn contains(&self, handle: Self::Handle) -> bool { 52 | self.contains(handle) 53 | } 54 | 55 | fn foreach(&self, mut f: impl FnMut(Self::Handle, &Self::ForceGenerator)) { 56 | for (h, b) in self.iter() { 57 | f(h, &**b) 58 | } 59 | } 60 | 61 | fn foreach_mut(&mut self, mut f: impl FnMut(Self::Handle, &mut Self::ForceGenerator)) { 62 | for (h, b) in self.iter_mut() { 63 | f(h, &mut **b) 64 | } 65 | } 66 | } 67 | 68 | /// The handle of a force generator. 69 | pub type DefaultForceGeneratorHandle = generational_arena::Index; 70 | 71 | /// A persistent force generator. 72 | /// 73 | /// A force generator applies a force to one or several bodies at each step of the simulation. 74 | pub trait ForceGenerator: Downcast + Send + Sync { 75 | /// Apply forces to some bodies. 76 | fn apply( 77 | &mut self, 78 | parameters: &IntegrationParameters, 79 | bodies: &mut dyn BodySet, 80 | ); 81 | } 82 | 83 | impl_downcast!(ForceGenerator where N: RealField, Handle: BodyHandle); 84 | -------------------------------------------------------------------------------- /src/force_generator/mod.rs: -------------------------------------------------------------------------------- 1 | //! Persistent force generation. 2 | 3 | pub use self::constant_acceleration::ConstantAcceleration; 4 | pub use self::force_generator::{ 5 | DefaultForceGeneratorHandle, DefaultForceGeneratorSet, ForceGenerator, ForceGeneratorSet, 6 | }; 7 | pub use self::spring::Spring; 8 | 9 | mod constant_acceleration; 10 | mod force_generator; 11 | mod spring; 12 | -------------------------------------------------------------------------------- /src/force_generator/spring.rs: -------------------------------------------------------------------------------- 1 | use na::{RealField, Unit}; 2 | 3 | use crate::force_generator::ForceGenerator; 4 | use crate::math::{ForceType, Point, Vector}; 5 | use crate::object::{BodyHandle, BodyPartHandle, BodySet}; 6 | use crate::solver::IntegrationParameters; 7 | 8 | /// Generator of a force proportional to the distance separating two bodies. 9 | pub struct Spring { 10 | b1: BodyPartHandle, 11 | b2: BodyPartHandle, 12 | anchor1: Point, 13 | anchor2: Point, 14 | length: N, 15 | stiffness: N, 16 | } 17 | 18 | impl Spring { 19 | /// Initialize a spring attached to `b1` and `b2` at the points `anchor1` and `anchor2`. 20 | /// 21 | /// Anchors are expressed in the local coordinates of the corresponding bodies. 22 | /// The spring has a rest length of `length` and a stiffness of `stiffness`. 23 | pub fn new( 24 | b1: BodyPartHandle, 25 | b2: BodyPartHandle, 26 | anchor1: Point, 27 | anchor2: Point, 28 | length: N, 29 | stiffness: N, 30 | ) -> Self { 31 | Spring { 32 | b1, 33 | b2, 34 | anchor1, 35 | anchor2, 36 | length, 37 | stiffness, 38 | } 39 | } 40 | 41 | /// Sets the attach point to the first body. 42 | /// 43 | /// The anchor is expressed in the local coordinatse of the first body. 44 | pub fn set_anchor_1(&mut self, anchor: Point) { 45 | self.anchor1 = anchor; 46 | } 47 | 48 | /// Sets the attach point to the second body. 49 | /// 50 | /// The anchor is expressed in the local coordinatse of the second body. 51 | pub fn set_anchor_2(&mut self, anchor: Point) { 52 | self.anchor2 = anchor 53 | } 54 | } 55 | 56 | impl ForceGenerator for Spring { 57 | fn apply( 58 | &mut self, 59 | _: &IntegrationParameters, 60 | bodies: &mut dyn BodySet, 61 | ) { 62 | let body1 = try_ret!(bodies.get(self.b1.0)); 63 | let body2 = try_ret!(bodies.get(self.b2.0)); 64 | let part1 = try_ret!(body1.part(self.b1.1)); 65 | let part2 = try_ret!(body2.part(self.b2.1)); 66 | 67 | let anchor1 = body1.world_point_at_material_point(part1, &self.anchor1); 68 | let anchor2 = body2.world_point_at_material_point(part2, &self.anchor2); 69 | 70 | let force_dir; 71 | let delta_length; 72 | 73 | if let Some((dir, length)) = Unit::try_new_and_get(anchor2 - anchor1, N::default_epsilon()) 74 | { 75 | force_dir = dir; 76 | delta_length = length - self.length; 77 | } else { 78 | force_dir = Vector::y_axis(); 79 | delta_length = -self.length; 80 | } 81 | 82 | let force = force_dir.as_ref() * delta_length * self.stiffness; 83 | bodies 84 | .get_mut(self.b1.0) 85 | .unwrap() 86 | .apply_force_at_local_point(self.b1.1, &force, &self.anchor1, ForceType::Force, false); 87 | bodies 88 | .get_mut(self.b2.0) 89 | .unwrap() 90 | .apply_force_at_local_point(self.b2.1, &-force, &self.anchor2, ForceType::Force, false); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/joint/ball_joint.rs: -------------------------------------------------------------------------------- 1 | use na::{ 2 | self, DVectorSliceMut, Isometry3, Matrix3, RealField, Translation3, UnitQuaternion, Vector3, 3 | VectorSlice3, 4 | }; 5 | 6 | use crate::joint::Joint; 7 | use crate::math::{JacobianSliceMut, Velocity}; 8 | use crate::solver::IntegrationParameters; 9 | use crate::utils::GeneralizedCross; 10 | 11 | /// A joint that allows only all rotational degrees of freedom between two multibody links. 12 | #[derive(Copy, Clone, Debug)] 13 | pub struct BallJoint { 14 | rot: UnitQuaternion, 15 | 16 | jacobian_v: Matrix3, 17 | jacobian_dot_v: Matrix3, 18 | } 19 | 20 | impl BallJoint { 21 | /// Create a ball joint with the an initial position given by a rotation in axis-angle form. 22 | pub fn new(axisangle: Vector3) -> Self { 23 | BallJoint { 24 | rot: UnitQuaternion::new(axisangle), 25 | jacobian_v: na::zero(), 26 | jacobian_dot_v: na::zero(), 27 | } 28 | } 29 | } 30 | 31 | impl Joint for BallJoint { 32 | #[inline] 33 | fn ndofs(&self) -> usize { 34 | 3 35 | } 36 | 37 | fn body_to_parent(&self, parent_shift: &Vector3, body_shift: &Vector3) -> Isometry3 { 38 | let trans = Translation3::from(parent_shift - self.rot * body_shift); 39 | Isometry3::from_parts(trans, self.rot) 40 | } 41 | 42 | fn update_jacobians(&mut self, body_shift: &Vector3, vels: &[N]) { 43 | let shift = self.rot * -body_shift; 44 | let angvel = VectorSlice3::from_slice(vels); 45 | 46 | self.jacobian_v = shift.gcross_matrix_tr(); 47 | self.jacobian_dot_v = angvel.cross(&shift).gcross_matrix_tr(); 48 | } 49 | 50 | fn jacobian(&self, transform: &Isometry3, out: &mut JacobianSliceMut) { 51 | // FIXME: could we avoid the computation of rotation matrix on each `jacobian_*` ? 52 | let rotmat = transform.rotation.to_rotation_matrix(); 53 | out.fixed_rows_mut::<3>(0) 54 | .copy_from(&(rotmat * self.jacobian_v)); 55 | out.fixed_rows_mut::<3>(3).copy_from(rotmat.matrix()); 56 | } 57 | 58 | fn jacobian_dot(&self, transform: &Isometry3, out: &mut JacobianSliceMut) { 59 | let rotmat = transform.rotation.to_rotation_matrix(); 60 | out.fixed_rows_mut::<3>(0) 61 | .copy_from(&(rotmat * self.jacobian_dot_v)); 62 | } 63 | 64 | fn jacobian_dot_veldiff_mul_coordinates( 65 | &self, 66 | transform: &Isometry3, 67 | acc: &[N], 68 | out: &mut JacobianSliceMut, 69 | ) { 70 | let angvel = Vector3::from_row_slice(&acc[..3]); 71 | let rotmat = transform.rotation.to_rotation_matrix(); 72 | let res = rotmat * angvel.gcross_matrix() * self.jacobian_v; 73 | out.fixed_rows_mut::<3>(0).copy_from(&res); 74 | } 75 | 76 | fn jacobian_mul_coordinates(&self, acc: &[N]) -> Velocity { 77 | let angvel = Vector3::from_row_slice(&acc[..3]); 78 | let linvel = self.jacobian_v * angvel; 79 | Velocity::new(linvel, angvel) 80 | } 81 | 82 | fn jacobian_dot_mul_coordinates(&self, acc: &[N]) -> Velocity { 83 | let angvel = Vector3::from_row_slice(&acc[..3]); 84 | let linvel = self.jacobian_dot_v * angvel; 85 | Velocity::new(linvel, na::zero()) 86 | } 87 | 88 | fn default_damping(&self, out: &mut DVectorSliceMut) { 89 | out.fill(na::convert(0.1f64)) 90 | } 91 | 92 | fn integrate(&mut self, parameters: &IntegrationParameters, vels: &[N]) { 93 | let angvel = Vector3::from_row_slice(&vels[..3]); 94 | let disp = UnitQuaternion::new_eps(angvel * parameters.dt(), N::zero()); 95 | self.rot = disp * self.rot; 96 | } 97 | 98 | fn apply_displacement(&mut self, disp: &[N]) { 99 | let angle = Vector3::from_row_slice(&disp[..3]); 100 | let disp = UnitQuaternion::new(angle); 101 | self.rot = disp * self.rot; 102 | } 103 | 104 | #[inline] 105 | fn clone(&self) -> Box> { 106 | Box::new(*self) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/joint/cartesian_joint.rs: -------------------------------------------------------------------------------- 1 | use na::{self, DVectorSliceMut, RealField}; 2 | 3 | use crate::joint::Joint; 4 | 5 | use crate::math::{Isometry, JacobianSliceMut, Translation, Vector, Velocity, DIM}; 6 | use crate::solver::IntegrationParameters; 7 | 8 | /// A joint that allows only all the translational degrees of freedom between two multibody links. 9 | #[derive(Copy, Clone, Debug)] 10 | pub struct CartesianJoint { 11 | position: Vector, 12 | } 13 | 14 | impl CartesianJoint { 15 | /// Create a cartesian joint with an initial position given by `position`. 16 | pub fn new(position: Vector) -> Self { 17 | CartesianJoint { position } 18 | } 19 | } 20 | 21 | impl Joint for CartesianJoint { 22 | #[inline] 23 | fn ndofs(&self) -> usize { 24 | DIM 25 | } 26 | 27 | fn body_to_parent(&self, parent_shift: &Vector, body_shift: &Vector) -> Isometry { 28 | let t = Translation::from(parent_shift - body_shift + self.position); 29 | Isometry::from_parts(t, na::one()) 30 | } 31 | 32 | fn update_jacobians(&mut self, _: &Vector, _: &[N]) {} 33 | 34 | fn jacobian(&self, _: &Isometry, out: &mut JacobianSliceMut) { 35 | out.fill_diagonal(N::one()) 36 | } 37 | 38 | fn jacobian_dot(&self, _: &Isometry, _: &mut JacobianSliceMut) {} 39 | 40 | fn jacobian_dot_veldiff_mul_coordinates( 41 | &self, 42 | _: &Isometry, 43 | _: &[N], 44 | _: &mut JacobianSliceMut, 45 | ) { 46 | } 47 | 48 | fn jacobian_mul_coordinates(&self, vels: &[N]) -> Velocity { 49 | Velocity::from_vectors(Vector::from_row_slice(&vels[..DIM]), na::zero()) 50 | } 51 | 52 | fn jacobian_dot_mul_coordinates(&self, _: &[N]) -> Velocity { 53 | Velocity::zero() 54 | } 55 | 56 | fn default_damping(&self, _: &mut DVectorSliceMut) {} 57 | 58 | fn integrate(&mut self, parameters: &IntegrationParameters, vels: &[N]) { 59 | self.position += Vector::from_row_slice(&vels[..DIM]) * parameters.dt(); 60 | } 61 | 62 | fn apply_displacement(&mut self, disp: &[N]) { 63 | self.position += Vector::from_row_slice(&disp[..DIM]); 64 | } 65 | 66 | #[inline] 67 | fn clone(&self) -> Box> { 68 | Box::new(*self) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/joint/fixed_joint.rs: -------------------------------------------------------------------------------- 1 | use na::{DVectorSliceMut, RealField}; 2 | 3 | use crate::joint::Joint; 4 | 5 | use crate::math::{Isometry, JacobianSliceMut, Translation, Vector, Velocity}; 6 | use crate::solver::IntegrationParameters; 7 | 8 | /// A joint that does not allow any relative degrees of freedom. 9 | #[derive(Copy, Clone, Debug)] 10 | pub struct FixedJoint { 11 | body_to_parent: Isometry, 12 | } 13 | 14 | impl FixedJoint { 15 | /// Create a joint that does not a allow any degrees of freedom between two multibody links. 16 | /// 17 | /// The descendent attached to this joint will have a position maintained to `pos_wrt_body` 18 | /// relative to its parent. 19 | pub fn new(pos_wrt_body: Isometry) -> Self { 20 | FixedJoint { 21 | body_to_parent: pos_wrt_body.inverse(), 22 | } 23 | } 24 | } 25 | 26 | impl Joint for FixedJoint { 27 | fn ndofs(&self) -> usize { 28 | 0 29 | } 30 | 31 | fn body_to_parent(&self, parent_shift: &Vector, body_shift: &Vector) -> Isometry { 32 | let parent_trans = Translation::from(*parent_shift); 33 | let body_trans = Translation::from(*body_shift); 34 | parent_trans * self.body_to_parent * body_trans 35 | } 36 | 37 | fn update_jacobians(&mut self, _: &Vector, _: &[N]) {} 38 | 39 | fn jacobian(&self, _: &Isometry, _: &mut JacobianSliceMut) {} 40 | 41 | fn jacobian_dot(&self, _: &Isometry, _: &mut JacobianSliceMut) {} 42 | 43 | fn jacobian_dot_veldiff_mul_coordinates( 44 | &self, 45 | _: &Isometry, 46 | _: &[N], 47 | _: &mut JacobianSliceMut, 48 | ) { 49 | } 50 | 51 | fn integrate(&mut self, _: &IntegrationParameters, _: &[N]) {} 52 | fn apply_displacement(&mut self, _: &[N]) {} 53 | 54 | fn jacobian_mul_coordinates(&self, _: &[N]) -> Velocity { 55 | Velocity::zero() 56 | } 57 | 58 | fn jacobian_dot_mul_coordinates(&self, _: &[N]) -> Velocity { 59 | Velocity::zero() 60 | } 61 | 62 | fn default_damping(&self, _: &mut DVectorSliceMut) {} 63 | 64 | #[inline] 65 | fn clone(&self) -> Box> { 66 | Box::new(*self) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/joint/free_joint.rs: -------------------------------------------------------------------------------- 1 | use na::{DVectorSliceMut, RealField}; 2 | 3 | use crate::joint::Joint; 4 | 5 | use crate::math::{Isometry, JacobianSliceMut, Vector, Velocity, SPATIAL_DIM}; 6 | use crate::solver::IntegrationParameters; 7 | 8 | /// A joint that allows all the relative degrees of freedom between two multibody links. 9 | /// 10 | /// This joint can only be added between a `Ground` body (as parent) and any other body. 11 | #[derive(Copy, Clone, Debug)] 12 | pub struct FreeJoint { 13 | position: Isometry, 14 | } 15 | 16 | impl FreeJoint { 17 | /// Creates a free joint with the given initial position of the descendent, relative to the ground. 18 | pub fn new(position: Isometry) -> Self { 19 | FreeJoint { position } 20 | } 21 | 22 | fn apply_displacement(&mut self, disp: &Velocity) { 23 | let disp = Isometry::new(disp.linear, disp.angular); 24 | self.position = Isometry::from_parts( 25 | disp.translation * self.position.translation, 26 | disp.rotation * self.position.rotation, 27 | ) 28 | } 29 | } 30 | 31 | impl Joint for FreeJoint { 32 | fn ndofs(&self) -> usize { 33 | SPATIAL_DIM 34 | } 35 | 36 | fn body_to_parent(&self, _: &Vector, _: &Vector) -> Isometry { 37 | self.position 38 | } 39 | 40 | fn update_jacobians(&mut self, _: &Vector, _: &[N]) {} 41 | 42 | fn jacobian(&self, _: &Isometry, out: &mut JacobianSliceMut) { 43 | out.fill_diagonal(N::one()); 44 | } 45 | 46 | fn jacobian_dot(&self, _: &Isometry, _: &mut JacobianSliceMut) {} 47 | 48 | fn jacobian_dot_veldiff_mul_coordinates( 49 | &self, 50 | _: &Isometry, 51 | _: &[N], 52 | _: &mut JacobianSliceMut, 53 | ) { 54 | } 55 | 56 | fn integrate(&mut self, parameters: &IntegrationParameters, vels: &[N]) { 57 | let disp = Velocity::from_slice(vels) * parameters.dt(); 58 | self.apply_displacement(&disp); 59 | } 60 | 61 | fn apply_displacement(&mut self, disp: &[N]) { 62 | let disp = Velocity::from_slice(disp); 63 | self.apply_displacement(&disp); 64 | } 65 | 66 | fn jacobian_mul_coordinates(&self, vels: &[N]) -> Velocity { 67 | Velocity::from_slice(vels) 68 | } 69 | 70 | fn jacobian_dot_mul_coordinates(&self, _: &[N]) -> Velocity { 71 | // The jacobian derivative is zero. 72 | Velocity::zero() 73 | } 74 | 75 | fn default_damping(&self, _: &mut DVectorSliceMut) {} 76 | 77 | #[inline] 78 | fn clone(&self) -> Box> { 79 | Box::new(*self) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/joint/joint.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] // For downcast. 2 | 3 | use downcast_rs::Downcast; 4 | use na::{DVectorSliceMut, RealField}; 5 | 6 | use crate::math::{Isometry, JacobianSliceMut, Vector, Velocity}; 7 | use crate::object::{BodyPartHandle, Multibody, MultibodyLink}; 8 | use crate::solver::{ConstraintSet, GenericNonlinearConstraint, IntegrationParameters}; 9 | 10 | /// Trait implemented by all joints following the reduced-coordinate formation. 11 | pub trait Joint: Downcast + Send + Sync { 12 | /// The number of degrees of freedom allowed by the joint. 13 | fn ndofs(&self) -> usize; 14 | /// The position of the multibody link containing this joint relative to its parent. 15 | fn body_to_parent(&self, parent_shift: &Vector, body_shift: &Vector) -> Isometry; 16 | /// Update the jacobians of this joint. 17 | fn update_jacobians(&mut self, body_shift: &Vector, vels: &[N]); 18 | /// Integrate the position of this joint. 19 | fn integrate(&mut self, parameters: &IntegrationParameters, vels: &[N]); 20 | /// Apply a displacement to the joint. 21 | fn apply_displacement(&mut self, disp: &[N]); 22 | 23 | /// Sets in `out` the non-zero entries of the joint jacobian transformed by `transform`. 24 | fn jacobian(&self, transform: &Isometry, out: &mut JacobianSliceMut); 25 | /// Sets in `out` the non-zero entries of the time-derivative of the joint jacobian transformed by `transform`. 26 | fn jacobian_dot(&self, transform: &Isometry, out: &mut JacobianSliceMut); 27 | /// Sets in `out` the non-zero entries of the velocity-derivative of the time-derivative of the joint jacobian transformed by `transform`. 28 | fn jacobian_dot_veldiff_mul_coordinates( 29 | &self, 30 | transform: &Isometry, 31 | vels: &[N], 32 | out: &mut JacobianSliceMut, 33 | ); 34 | 35 | /// Multiply the joint jacobian by generalized velocities to obtain the 36 | /// relative velocity of the multibody link containing this joint. 37 | fn jacobian_mul_coordinates(&self, vels: &[N]) -> Velocity; 38 | /// Multiply the joint jacobian by generalized accelerations to obtain the 39 | /// relative acceleration of the multibody link containing this joint. 40 | fn jacobian_dot_mul_coordinates(&self, vels: &[N]) -> Velocity; 41 | 42 | /// Fill `out` with the non-zero entries of a damping that can be applied by default to ensure a good stability of the joint. 43 | fn default_damping(&self, out: &mut DVectorSliceMut); 44 | 45 | /// The maximum number of impulses needed by this joints for 46 | /// its constraints. 47 | fn nimpulses(&self) -> usize { 48 | // FIXME: keep this? 49 | self.ndofs() * 3 50 | } 51 | 52 | /// Maximum number of velocity constrains that can be generated by this joint. 53 | fn num_velocity_constraints(&self) -> usize { 54 | 0 55 | } 56 | 57 | /// Initialize and generate velocity constraints to enforce, e.g., joint limits and motors. 58 | fn velocity_constraints( 59 | &self, 60 | _params: &IntegrationParameters, 61 | _multibody: &Multibody, 62 | _link: &MultibodyLink, 63 | _assembly_id: usize, 64 | _dof_id: usize, 65 | _ext_vels: &[N], 66 | _ground_j_id: &mut usize, 67 | _jacobians: &mut [N], 68 | _velocity_constraints: &mut ConstraintSet, 69 | ) { 70 | } 71 | 72 | /// The maximum number of non-linear position constraints that can be generated by this joint. 73 | fn num_position_constraints(&self) -> usize { 74 | 0 75 | } 76 | 77 | /// Initialize and generate the i-th position constraints to enforce, e.g., joint limits. 78 | fn position_constraint( 79 | &self, 80 | _i: usize, 81 | _multibody: &Multibody, 82 | _link: &MultibodyLink, 83 | _handle: BodyPartHandle<()>, 84 | _dof_id: usize, 85 | _jacobians: &mut [N], 86 | ) -> Option> { 87 | None 88 | } 89 | 90 | fn clone(&self) -> Box>; 91 | } 92 | 93 | impl_downcast!(Joint where N: RealField); 94 | -------------------------------------------------------------------------------- /src/joint/joint_motor.rs: -------------------------------------------------------------------------------- 1 | use crate::solver::ImpulseLimits; 2 | use na::RealField; 3 | use num::Zero; 4 | 5 | /// Description of a motor applied to a joint. 6 | #[derive(Copy, Clone, Debug)] 7 | pub struct JointMotor { 8 | /// The velocity the motor will attempt to reach. 9 | pub desired_velocity: V, 10 | /// The maximum velocity the motor will attempt to reach. 11 | pub max_velocity: N, 12 | /// The maximum force deliverable by the motor. 13 | pub max_force: N, 14 | /// Whether or not the motor is active. 15 | pub enabled: bool, 16 | } 17 | 18 | impl JointMotor { 19 | /// Create a disable motor with zero desired velocity. 20 | /// 21 | /// The max force is initialized to a virtually infinite value, i.e., `N::max_value()`. 22 | pub fn new() -> Self { 23 | JointMotor { 24 | desired_velocity: V::zero(), 25 | max_velocity: N::max_value(), 26 | max_force: N::max_value(), 27 | enabled: false, 28 | } 29 | } 30 | 31 | /// The limits of the impulse applicable by the motor on the body parts. 32 | pub fn impulse_limits(&self) -> ImpulseLimits { 33 | ImpulseLimits::Independent { 34 | min: -self.max_force, 35 | max: self.max_force, 36 | } 37 | } 38 | } 39 | 40 | impl Default for JointMotor { 41 | fn default() -> Self { 42 | Self::new() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/joint/mod.rs: -------------------------------------------------------------------------------- 1 | //! Joints using the reduced-coordinates formalism or using constraints. 2 | 3 | pub use self::cartesian_joint::CartesianJoint; 4 | pub use self::fixed_joint::FixedJoint; 5 | pub use self::free_joint::FreeJoint; 6 | pub use self::joint::Joint; 7 | pub use self::prismatic_joint::PrismaticJoint; 8 | pub use self::revolute_joint::RevoluteJoint; 9 | pub use self::unit_joint::{ 10 | unit_joint_num_velocity_constraints, unit_joint_position_constraint, 11 | unit_joint_velocity_constraints, UnitJoint, 12 | }; 13 | 14 | #[cfg(feature = "dim3")] 15 | pub use self::ball_joint::BallJoint; 16 | #[cfg(feature = "dim3")] 17 | pub use self::cylindrical_joint::CylindricalJoint; 18 | #[cfg(feature = "dim3")] 19 | pub use self::helical_joint::HelicalJoint; 20 | #[cfg(feature = "dim3")] 21 | pub use self::pin_slot_joint::PinSlotJoint; 22 | #[cfg(feature = "dim3")] 23 | pub use self::planar_joint::PlanarJoint; 24 | #[cfg(feature = "dim3")] 25 | pub use self::rectangular_joint::RectangularJoint; 26 | #[cfg(feature = "dim3")] 27 | pub use self::universal_joint::UniversalJoint; 28 | 29 | pub use self::cartesian_constraint::CartesianConstraint; 30 | pub use self::fixed_constraint::FixedConstraint; 31 | pub use self::joint_constraint::{ 32 | DefaultJointConstraintHandle, DefaultJointConstraintSet, JointConstraint, JointConstraintSet, 33 | }; 34 | pub use self::joint_motor::JointMotor; 35 | pub use self::mouse_constraint::MouseConstraint; 36 | pub use self::prismatic_constraint::PrismaticConstraint; 37 | pub use self::revolute_constraint::RevoluteConstraint; 38 | 39 | #[cfg(feature = "dim3")] 40 | pub use self::ball_constraint::BallConstraint; 41 | #[cfg(feature = "dim3")] 42 | pub use self::cylindrical_constraint::CylindricalConstraint; 43 | #[cfg(feature = "dim3")] 44 | pub use self::pin_slot_constraint::PinSlotConstraint; 45 | #[cfg(feature = "dim3")] 46 | pub use self::planar_constraint::PlanarConstraint; 47 | #[cfg(feature = "dim3")] 48 | pub use self::rectangular_constraint::RectangularConstraint; 49 | #[cfg(feature = "dim3")] 50 | pub use self::universal_constraint::UniversalConstraint; 51 | 52 | mod cartesian_joint; 53 | mod fixed_joint; 54 | mod free_joint; 55 | mod joint; 56 | mod prismatic_joint; 57 | mod revolute_joint; 58 | mod unit_joint; 59 | 60 | #[cfg(feature = "dim3")] 61 | mod ball_joint; 62 | #[cfg(feature = "dim3")] 63 | mod cylindrical_joint; 64 | #[cfg(feature = "dim3")] 65 | mod helical_joint; 66 | #[cfg(feature = "dim3")] 67 | mod pin_slot_joint; 68 | #[cfg(feature = "dim3")] 69 | mod planar_joint; 70 | #[cfg(feature = "dim3")] 71 | mod rectangular_joint; 72 | #[cfg(feature = "dim3")] 73 | mod universal_joint; 74 | 75 | mod cartesian_constraint; 76 | mod fixed_constraint; 77 | mod joint_constraint; 78 | mod joint_motor; 79 | mod mouse_constraint; 80 | mod prismatic_constraint; 81 | mod revolute_constraint; 82 | mod unit_constraint; 83 | 84 | #[cfg(feature = "dim3")] 85 | mod ball_constraint; 86 | #[cfg(feature = "dim3")] 87 | mod cylindrical_constraint; 88 | #[cfg(feature = "dim3")] 89 | mod pin_slot_constraint; 90 | #[cfg(feature = "dim3")] 91 | mod planar_constraint; 92 | #[cfg(feature = "dim3")] 93 | mod rectangular_constraint; 94 | #[cfg(feature = "dim3")] 95 | mod universal_constraint; 96 | -------------------------------------------------------------------------------- /src/joint/slot_joint.rs: -------------------------------------------------------------------------------- 1 | use na::{self, RealField, U3, VectorSlice3, Vector, Point, Matrix3, Isometry, Translation3, UnitQuaternion}; 2 | 3 | use crate::utils::GeneralizedCross; 4 | use crate::joint::Joint; 5 | use crate::solver::IntegrationParameters; 6 | use crate::math::{Velocity, JacobianSliceMut}; 7 | 8 | 9 | #[derive(Copy, Clone, Debug)] 10 | pub struct SlotJoint { 11 | shift: Vector, 12 | axis: Unit>, 13 | 14 | pos: N, 15 | rot: Rotation, 16 | 17 | jacobian_v: Matrix3, 18 | jacobian_dot_v: Matrix3, 19 | } 20 | 21 | impl SlotJoint { 22 | pub fn new(center: Point, axisangle: Vector) -> Self { 23 | SlotJoint { 24 | shift: -center.coords, 25 | rot: UnitQuaternion::new(axisangle), 26 | jacobian_v: na::zero(), 27 | jacobian_dot_v: na::zero() 28 | } 29 | } 30 | } 31 | 32 | 33 | impl Joint for SlotJoint { 34 | #[inline] 35 | fn ndofs(&self) -> usize { 36 | ANGULAR_DIM + 1 37 | } 38 | 39 | fn body_to_parent(&self) -> Isometry { 40 | let trans = Translation3::from(self.shift + &*self.axis * self.pos); 41 | let rot = 42 | Isometry::from_parts(trans, self.rot) 43 | } 44 | 45 | fn update_jacobians(&mut self, vels: &[N]) { 46 | let parent_shift = self.rot * self.shift; 47 | let angvel = VectorSlice3::new(vels); 48 | 49 | self.jacobian_v = parent_shift.gcross_matrix_tr(); 50 | self.jacobian_dot_v = angvel.cross(&parent_shift).gcross_matrix_tr(); 51 | } 52 | 53 | fn jacobian(&self, transform: &Isometry, out: &mut JacobianSliceMut) { 54 | // FIXME: could we avoid the computation of rotation matrix on each `jacobian_*` ? 55 | let rotmat = transform.rotation.to_rotation_matrix(); 56 | out.fixed_rows_mut::(0).copy_from(&(rotmat * self.jacobian_v)); 57 | out.fixed_rows_mut::(3).copy_from(rotmat.matrix()); 58 | } 59 | 60 | fn jacobian_dot(&self, transform: &Isometry, out: &mut JacobianSliceMut) { 61 | let rotmat = transform.rotation.to_rotation_matrix(); 62 | out.fixed_rows_mut::(0).copy_from(&(rotmat * self.jacobian_dot_v)); 63 | } 64 | 65 | fn jacobian_dot_veldiff_mul_coordinates(&self, transform: &Isometry, acc: &[N], 66 | out: &mut JacobianSliceMut) { 67 | let angvel = Vector::from_row_slice(&acc[.. 3]); 68 | let rotmat = transform.rotation.to_rotation_matrix(); 69 | let res = rotmat * angvel.gcross_matrix() * self.jacobian_v; 70 | out.fixed_rows_mut::(0).copy_from(&res); 71 | } 72 | 73 | fn jacobian_mul_coordinates(&self, acc: &[N]) -> Velocity { 74 | let angvel = Vector::from_row_slice(&acc[.. 3]); 75 | let linvel = self.jacobian_v * angvel; 76 | Velocity::new(linvel, angvel) 77 | } 78 | 79 | fn jacobian_dot_mul_coordinates(&self, acc: &[N]) -> Velocity { 80 | let angvel = Vector::from_row_slice(&acc[.. 3]); 81 | let linvel = self.jacobian_dot_v * angvel; 82 | Velocity::new(linvel, na::zero()) 83 | } 84 | 85 | fn integrate(&mut self, dt: N, vels: &[N]) { 86 | let angvel = Vector::from_row_slice(&vels[.. 3]); 87 | let disp = UnitQuaternion::new(angvel * dt); 88 | self.rot = disp * self.rot; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/material/basic_material.rs: -------------------------------------------------------------------------------- 1 | use na::RealField; 2 | 3 | use crate::material::{LocalMaterialProperties, Material, MaterialCombineMode, MaterialContext}; 4 | 5 | use crate::math::Vector; 6 | 7 | /// Description of the state of surface of a solid. 8 | /// 9 | /// Strictly speaking, the coefficient provided here only exist 10 | /// when considering a pair of touching surfaces. In practice, nphysics 11 | /// will average the coefficient of the two surfaces in contact in order 12 | /// to deduce the restitution/friction coefficient. 13 | #[derive(Copy, Clone, Debug)] 14 | pub struct BasicMaterial { 15 | /// The ID of this material for automatic lookup. 16 | pub id: Option, 17 | /// Restitution coefficient of the surface. 18 | pub restitution: N, 19 | /// Friction coefficient of the surface. 20 | pub friction: N, 21 | /// The fictitious velocity at the surface of this material. 22 | pub surface_velocity: Option>, 23 | /// The way restitution coefficients are combined if no match 24 | /// was found in the material lookup tables. 25 | pub restitution_combine_mode: MaterialCombineMode, 26 | /// The way friction coefficients are combined if no match 27 | /// was found in the material lookup tables. 28 | pub friction_combine_mode: MaterialCombineMode, 29 | } 30 | 31 | impl BasicMaterial { 32 | /// Initialize a material with the specified restitution and friction coefficients. 33 | pub fn new(restitution: N, friction: N) -> Self { 34 | BasicMaterial { 35 | id: None, 36 | restitution, 37 | friction, 38 | surface_velocity: None, 39 | restitution_combine_mode: MaterialCombineMode::Average, 40 | friction_combine_mode: MaterialCombineMode::Average, 41 | } 42 | } 43 | } 44 | 45 | impl Material for BasicMaterial { 46 | fn local_properties(&self, context: MaterialContext) -> LocalMaterialProperties { 47 | LocalMaterialProperties { 48 | id: self.id, 49 | restitution: (self.restitution, self.restitution_combine_mode), 50 | friction: (self.friction, self.friction_combine_mode), 51 | surface_velocity: self 52 | .surface_velocity 53 | .map(|v| context.position * v) 54 | .unwrap_or_else(Vector::zeros), 55 | } 56 | } 57 | } 58 | 59 | impl Default for BasicMaterial { 60 | fn default() -> Self { 61 | BasicMaterial::new(N::zero(), na::convert(0.5)) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/material/materials_coefficients_table.rs: -------------------------------------------------------------------------------- 1 | use na::RealField; 2 | use ncollide::utils::SortedPair; 3 | use std::collections::HashMap; 4 | 5 | use crate::material::MaterialId; 6 | 7 | /// A lookup table for friction and restitution coefficient associated to certain pairs of materials. 8 | #[derive(Clone)] 9 | pub struct MaterialsCoefficientsTable { 10 | friction: HashMap, N>, 11 | restitution: HashMap, N>, 12 | } 13 | 14 | impl MaterialsCoefficientsTable { 15 | /// Initialize an empty table of friction and restitution coefficients. 16 | pub fn new() -> Self { 17 | MaterialsCoefficientsTable { 18 | friction: HashMap::new(), 19 | restitution: HashMap::new(), 20 | } 21 | } 22 | 23 | /// Sets the friction coefficient associated to this pair of materials. 24 | pub fn set_friction_coefficient(&mut self, m1: MaterialId, m2: MaterialId, coeff: N) { 25 | let _ = self.friction.insert(SortedPair::new(m1, m2), coeff); 26 | } 27 | 28 | /// Sets the restitution coefficient associated to this pair of materials. 29 | pub fn set_restitution_coefficient(&mut self, m1: MaterialId, m2: MaterialId, coeff: N) { 30 | let _ = self.restitution.insert(SortedPair::new(m1, m2), coeff); 31 | } 32 | 33 | /// Retrieves the friction coefficient associated to this pair of materials. 34 | /// 35 | /// Returns `None` if no coefficient was associated to this pair. 36 | pub fn friction_coefficient(&self, m1: MaterialId, m2: MaterialId) -> Option { 37 | self.friction.get(&SortedPair::new(m1, m2)).cloned() 38 | } 39 | 40 | /// Retrieves the restitution coefficient associated to this pair of materials. 41 | /// 42 | /// Returns `None` if no coefficient was associated to this pair. 43 | pub fn restitution_coefficient(&self, m1: MaterialId, m2: MaterialId) -> Option { 44 | self.restitution.get(&SortedPair::new(m1, m2)).cloned() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/material/mod.rs: -------------------------------------------------------------------------------- 1 | //! Material data structures. 2 | 3 | pub use self::basic_material::BasicMaterial; 4 | pub use self::material::{ 5 | LocalMaterialProperties, Material, MaterialCombineMode, MaterialContext, MaterialHandle, 6 | MaterialId, 7 | }; 8 | pub use self::materials_coefficients_table::MaterialsCoefficientsTable; 9 | 10 | mod basic_material; 11 | mod material; 12 | mod materials_coefficients_table; 13 | -------------------------------------------------------------------------------- /src/object/mod.rs: -------------------------------------------------------------------------------- 1 | //! Objects that may be added to the physical world. 2 | 3 | pub use self::body::{ 4 | ActivationStatus, Body, BodyPart, BodyPartMotion, BodyStatus, BodyUpdateStatus, 5 | }; 6 | pub use self::body_set::{ 7 | BodyDesc, BodyHandle, BodyPartHandle, BodySet, DefaultBodyHandle, DefaultBodyPartHandle, 8 | DefaultBodySet, 9 | }; 10 | pub use self::collider::{ 11 | Collider, ColliderAnchor, ColliderData, ColliderDesc, ColliderRemovalData, 12 | DeformableColliderDesc, 13 | }; 14 | pub use self::collider_set::{ 15 | ColliderHandle, ColliderSet, DefaultColliderHandle, DefaultColliderSet, 16 | }; 17 | pub(crate) use self::fem_helper::FiniteElementIndices; 18 | #[cfg(feature = "dim2")] 19 | pub use self::fem_surface::{FEMSurface, FEMSurfaceDesc}; 20 | #[cfg(feature = "dim3")] 21 | pub use self::fem_volume::{FEMVolume, FEMVolumeDesc}; 22 | pub use self::ground::Ground; 23 | pub use self::mass_constraint_system::{MassConstraintSystem, MassConstraintSystemDesc}; 24 | pub use self::mass_spring_system::{MassSpringSystem, MassSpringSystemDesc}; 25 | pub use self::multibody::{Multibody, MultibodyDesc}; 26 | pub use self::multibody_link::MultibodyLink; 27 | pub(crate) use self::multibody_link::MultibodyLinkVec; 28 | pub use self::rigid_body::{RigidBody, RigidBodyDesc}; 29 | 30 | mod body; 31 | mod body_set; 32 | mod collider; 33 | mod collider_set; 34 | pub(crate) mod fem_helper; 35 | #[cfg(feature = "dim2")] 36 | mod fem_surface; 37 | #[cfg(feature = "dim3")] 38 | mod fem_volume; 39 | mod ground; 40 | mod mass_constraint_system; 41 | mod mass_spring_system; 42 | mod multibody; 43 | mod multibody_link; 44 | mod rigid_body; 45 | -------------------------------------------------------------------------------- /src/solver/constraint_set.rs: -------------------------------------------------------------------------------- 1 | use crate::object::{BodyHandle, ColliderHandle}; 2 | use crate::solver::{ 3 | BilateralConstraint, BilateralGroundConstraint, NonlinearUnilateralConstraint, 4 | UnilateralConstraint, UnilateralGroundConstraint, 5 | }; 6 | use na::RealField; 7 | 8 | /// Set of velocity-based constraints. 9 | pub struct LinearConstraints { 10 | /// Unilateral velocity constraints involving a dynamic body and the ground (or a body without any degrees of freedoms). 11 | pub unilateral_ground: Vec>, 12 | /// Unilateral velocity constraints between dynamic bodies. 13 | pub unilateral: Vec>, 14 | /// Bilateral velocity constraints involving a dynamic body and the ground (or a body without any degrees of freedoms). 15 | pub bilateral_ground: Vec>, 16 | /// Bilateral velocity constraints between dynamic bodies. 17 | pub bilateral: Vec>, 18 | } 19 | 20 | impl LinearConstraints { 21 | /// Creates a new empty set of constraints. 22 | pub fn new() -> Self { 23 | LinearConstraints { 24 | unilateral_ground: Vec::new(), 25 | unilateral: Vec::new(), 26 | bilateral_ground: Vec::new(), 27 | bilateral: Vec::new(), 28 | } 29 | } 30 | 31 | /// The total number of constraints on this set. 32 | pub fn len(&self) -> usize { 33 | self.unilateral_ground.len() 34 | + self.unilateral.len() 35 | + self.bilateral_ground.len() 36 | + self.bilateral.len() 37 | } 38 | 39 | /// Remove all constraints from this set. 40 | pub fn clear(&mut self) { 41 | self.unilateral_ground.clear(); 42 | self.unilateral.clear(); 43 | self.bilateral_ground.clear(); 44 | self.bilateral.clear(); 45 | } 46 | } 47 | 48 | /// Set of non-linear position-based constraints. 49 | pub struct NonlinearConstraints { 50 | /// Unilateral position-based constraints between two bodies. 51 | pub unilateral: Vec>, 52 | } 53 | 54 | impl 55 | NonlinearConstraints 56 | { 57 | /// Create an empty set of nonlinear position-based constraints. 58 | pub fn new() -> Self { 59 | NonlinearConstraints { 60 | unilateral: Vec::new(), 61 | } 62 | } 63 | 64 | /// The total number of constraints on this set. 65 | pub fn len(&self) -> usize { 66 | self.unilateral.len() 67 | } 68 | 69 | /// Remove all constraints from this set. 70 | pub fn clear(&mut self) { 71 | self.unilateral.clear(); 72 | } 73 | } 74 | 75 | /// A set of all velocity constraints and non-linear position-based constraints. 76 | pub struct ConstraintSet { 77 | /// The velocity constraints constructed. 78 | pub velocity: LinearConstraints, 79 | /// The position constraints constructed. 80 | pub position: NonlinearConstraints, 81 | } 82 | 83 | impl 84 | ConstraintSet 85 | { 86 | /// Create a new empty set of constraints. 87 | pub fn new() -> Self { 88 | ConstraintSet { 89 | velocity: LinearConstraints::new(), 90 | position: NonlinearConstraints::new(), 91 | } 92 | } 93 | 94 | /// The total number of constraints on this set. 95 | pub fn len(&self) -> usize { 96 | self.velocity.len() + self.position.len() 97 | } 98 | 99 | /// Remove all constraints from this set. 100 | pub fn clear(&mut self) { 101 | self.velocity.clear(); 102 | self.position.clear(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/solver/contact_model.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | use downcast_rs::Downcast; 4 | use na::{DVector, RealField}; 5 | use ncollide::query::ContactId; 6 | 7 | use crate::detection::ColliderContactManifold; 8 | use crate::material::MaterialsCoefficientsTable; 9 | use crate::object::{BodyHandle, BodySet, ColliderHandle}; 10 | use crate::solver::{ConstraintSet, IntegrationParameters}; 11 | 12 | /// The modeling of a contact. 13 | pub trait ContactModel: 14 | Downcast + Send + Sync 15 | { 16 | /// Maximum number of velocity constraint to be generated for each contact. 17 | fn num_velocity_constraints( 18 | &self, 19 | manifold: &ColliderContactManifold, 20 | ) -> usize; 21 | /// Generate all constraints for the given contact manifolds. 22 | fn constraints( 23 | &mut self, 24 | parameters: &IntegrationParameters, 25 | material_coefficients: &MaterialsCoefficientsTable, 26 | bodies: &dyn BodySet, 27 | ext_vels: &DVector, 28 | manifolds: &[ColliderContactManifold], 29 | ground_j_id: &mut usize, 30 | j_id: &mut usize, 31 | jacobians: &mut [N], 32 | constraints: &mut ConstraintSet, 33 | ); 34 | 35 | /// Stores all the impulses found by the solver into a cache for warmstarting. 36 | fn cache_impulses(&mut self, constraints: &ConstraintSet); 37 | } 38 | 39 | impl_downcast!(ContactModel where N: RealField, Handle: BodyHandle, CollHandle: ColliderHandle); 40 | -------------------------------------------------------------------------------- /src/solver/impulse_cache.rs: -------------------------------------------------------------------------------- 1 | use slotmap::SecondaryMap; 2 | 3 | use ncollide::query::ContactId; 4 | 5 | /// A cache for impulses resulting from contacts and joints. 6 | pub type ImpulseCache = SecondaryMap; 7 | -------------------------------------------------------------------------------- /src/solver/mod.rs: -------------------------------------------------------------------------------- 1 | //! Constraint solver. 2 | 3 | pub use self::constraint::{ 4 | BilateralConstraint, BilateralGroundConstraint, ConstraintGeometry, ImpulseLimits, 5 | UnilateralConstraint, UnilateralGroundConstraint, 6 | }; 7 | pub use self::constraint_set::{ConstraintSet, LinearConstraints}; 8 | pub use self::contact_model::ContactModel; 9 | pub use self::helper::ForceDirection; 10 | pub use self::impulse_cache::ImpulseCache; 11 | pub use self::integration_parameters::IntegrationParameters; 12 | pub use self::moreau_jean_solver::MoreauJeanSolver; 13 | pub use self::nonlinear_constraint::{ 14 | GenericNonlinearConstraint, MultibodyJointLimitsNonlinearConstraintGenerator, 15 | NonlinearConstraintGenerator, NonlinearUnilateralConstraint, 16 | }; 17 | pub(crate) use self::nonlinear_sor_prox::NonlinearSORProx; 18 | pub use self::signorini_coulomb_pyramid_model::SignoriniCoulombPyramidModel; 19 | pub use self::signorini_model::SignoriniModel; 20 | pub(crate) use self::sor_prox::SORProx; 21 | 22 | mod constraint; 23 | mod constraint_set; 24 | mod contact_model; 25 | pub mod helper; 26 | mod impulse_cache; 27 | mod integration_parameters; 28 | mod moreau_jean_solver; 29 | mod nonlinear_constraint; 30 | mod nonlinear_sor_prox; 31 | mod signorini_coulomb_pyramid_model; 32 | mod signorini_model; 33 | mod sor_prox; 34 | -------------------------------------------------------------------------------- /src/utils/deterministic_state.rs: -------------------------------------------------------------------------------- 1 | use std::collections::hash_map::DefaultHasher; 2 | use std::hash::BuildHasher; 3 | 4 | /// A hasher builder that creates `DefaultHasher` with default keys. 5 | #[derive(Default)] 6 | pub struct DeterministicState; 7 | 8 | impl DeterministicState { 9 | /// Creates a new `DeterministicState` that builds `DefaultHasher` with default keys. 10 | pub fn new() -> Self { 11 | Self::default() 12 | } 13 | } 14 | 15 | impl BuildHasher for DeterministicState { 16 | type Hasher = DefaultHasher; 17 | 18 | fn build_hasher(&self) -> DefaultHasher { 19 | DefaultHasher::new() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/generalized_cross.rs: -------------------------------------------------------------------------------- 1 | use na::RealField; 2 | use na::{Matrix2, Matrix3, RowVector2, Vector1, Vector2, Vector3}; 3 | 4 | /// This is a non-standard generalization of the cross product design exclusively to group the 5 | /// 3D cross product and the 2D perpendicular product behind the same interface. 6 | pub trait GeneralizedCross { 7 | /// The right-hand-side of this cross product. 8 | type Rhs; 9 | /// The result type of the this (non-standard) generalized cross product. 10 | type CrossVector; 11 | /// The matrix representation of this (non-standard) generalized cross product. 12 | type CrossMatrix; 13 | /// The transposed matrix representation of this (non-standard) generalized cross product. 14 | type CrossMatrixTr; 15 | 16 | /// Computes this (non-standard) generalized cross product. 17 | fn gcross(&self, rhs: &Self::Rhs) -> Self::CrossVector; 18 | 19 | /// Computes the matrix represenattion of this (non-standard) generalized cross product. 20 | fn gcross_matrix(&self) -> Self::CrossMatrix; 21 | 22 | /// Computes the transposed matrix represenattion of this (non-standard) generalized cross product. 23 | fn gcross_matrix_tr(&self) -> Self::CrossMatrixTr; 24 | } 25 | 26 | impl GeneralizedCross for Vector1 { 27 | type Rhs = Vector2; 28 | type CrossVector = Vector2; 29 | type CrossMatrix = Matrix2; 30 | type CrossMatrixTr = Matrix2; 31 | 32 | #[inline] 33 | fn gcross(&self, rhs: &Vector2) -> Vector2 { 34 | Vector2::new(-rhs.y * self.x, rhs.x * self.x) 35 | } 36 | 37 | #[inline] 38 | fn gcross_matrix(&self) -> Matrix2 { 39 | Matrix2::new(N::zero(), -self.x, self.x, N::zero()) 40 | } 41 | 42 | #[inline] 43 | fn gcross_matrix_tr(&self) -> Matrix2 { 44 | Matrix2::new(N::zero(), self.x, -self.x, N::zero()) 45 | } 46 | } 47 | 48 | impl GeneralizedCross for Vector2 { 49 | type Rhs = Vector2; 50 | type CrossVector = Vector1; 51 | type CrossMatrix = RowVector2; 52 | type CrossMatrixTr = Vector2; 53 | 54 | #[inline] 55 | fn gcross(&self, rhs: &Vector2) -> Vector1 { 56 | Vector1::new(self.x * rhs.y - self.y * rhs.x) 57 | } 58 | 59 | #[inline] 60 | fn gcross_matrix(&self) -> RowVector2 { 61 | RowVector2::new(-self.y, self.x) 62 | } 63 | 64 | #[inline] 65 | fn gcross_matrix_tr(&self) -> Vector2 { 66 | Vector2::new(-self.y, self.x) 67 | } 68 | } 69 | 70 | impl GeneralizedCross for Vector3 { 71 | type Rhs = Vector3; 72 | type CrossVector = Vector3; 73 | type CrossMatrix = Matrix3; 74 | type CrossMatrixTr = Matrix3; 75 | 76 | #[inline] 77 | fn gcross(&self, rhs: &Vector3) -> Vector3 { 78 | self.cross(rhs) 79 | } 80 | 81 | #[inline] 82 | fn gcross_matrix(&self) -> Matrix3 { 83 | self.cross_matrix() 84 | } 85 | 86 | #[inline] 87 | fn gcross_matrix_tr(&self) -> Matrix3 { 88 | Matrix3::new( 89 | N::zero(), 90 | self.z, 91 | -self.y, 92 | -self.z, 93 | N::zero(), 94 | self.x, 95 | self.y, 96 | -self.x, 97 | N::zero(), 98 | ) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/utils/index_mut2.rs: -------------------------------------------------------------------------------- 1 | use std::ops::IndexMut; 2 | 3 | /// Methods for simultaneously indexing a container with two distinct indices. 4 | pub trait IndexMut2: IndexMut { 5 | /// Gets mutable references to two distinct elements of the container. 6 | /// 7 | /// Panics if `i == j`. 8 | fn index_mut2(&mut self, i: usize, j: usize) -> (&mut Self::Output, &mut Self::Output); 9 | 10 | /// Gets a mutable reference to one element, and immutable reference to a second one. 11 | /// 12 | /// Panics if `i == j`. 13 | #[inline] 14 | fn index_mut_const(&mut self, i: usize, j: usize) -> (&mut Self::Output, &Self::Output) { 15 | let (a, b) = self.index_mut2(i, j); 16 | (a, &*b) 17 | } 18 | } 19 | 20 | impl IndexMut2 for Vec { 21 | #[inline] 22 | fn index_mut2(&mut self, i: usize, j: usize) -> (&mut T, &mut T) { 23 | assert!(i != j, "Unable to index the same element twice."); 24 | assert!(i < self.len() && j < self.len(), "Index out of bounds."); 25 | 26 | unsafe { 27 | let a = &mut *(self.get_unchecked_mut(i) as *mut _); 28 | let b = &mut *(self.get_unchecked_mut(j) as *mut _); 29 | (a, b) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | //! Miscellaneous utilities. 2 | 3 | pub use self::deterministic_state::DeterministicState; 4 | pub use self::generalized_cross::GeneralizedCross; 5 | pub use self::index_mut2::IndexMut2; 6 | pub use self::user_data::UserData; 7 | pub(crate) use self::user_data::UserDataBox; 8 | 9 | mod deterministic_state; 10 | mod generalized_cross; 11 | mod index_mut2; 12 | pub mod union_find; 13 | mod user_data; 14 | -------------------------------------------------------------------------------- /src/utils/union_find.rs: -------------------------------------------------------------------------------- 1 | //! The union find algorithm. 2 | 3 | /// An element used by the union-find algorithm. 4 | #[derive(Copy, Clone, Debug, Hash)] 5 | pub struct UnionFindSet { 6 | /// The parent of this union find element. 7 | parent: usize, 8 | /// The rank of this union find element. 9 | rank: usize, 10 | } 11 | 12 | impl UnionFindSet { 13 | /// Creates a new `UnionFindSet`. 14 | #[inline] 15 | pub fn new(key: usize) -> UnionFindSet { 16 | UnionFindSet { 17 | parent: key, 18 | rank: 0, 19 | } 20 | } 21 | 22 | /// Reinitialize this set. 23 | #[inline] 24 | pub fn reinit(&mut self, key: usize) { 25 | self.parent = key; 26 | self.rank = 0; 27 | } 28 | } 29 | 30 | /// Performs the `find` part of the union-find algorithm. 31 | pub fn find(x: usize, sets: &mut [UnionFindSet]) -> usize { 32 | if sets[x].parent != x { 33 | sets[x].parent = find(sets[x].parent, sets); 34 | } 35 | 36 | sets[x].parent 37 | } 38 | 39 | /// Performs the `union` part of the union-find algorithm. 40 | pub fn union(x: usize, y: usize, sets: &mut [UnionFindSet]) { 41 | let x_root = find(x, sets); 42 | let y_root = find(y, sets); 43 | 44 | if x_root == y_root { 45 | return; 46 | } 47 | 48 | let rankx = sets[x_root].rank; 49 | let ranky = sets[y_root].rank; 50 | 51 | if rankx < ranky { 52 | sets[x_root].parent = y_root 53 | } else if rankx > ranky { 54 | sets[y_root].parent = x_root 55 | } else { 56 | sets[y_root].parent = x_root; 57 | sets[x_root].rank = rankx + 1 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/utils/user_data.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | 3 | /// Trait to be implemented by user-defined data. 4 | pub trait UserData: Any + Send + Sync { 5 | /// Clone this trait-object. 6 | fn clone_boxed(&self) -> Box; 7 | /// Clone as its super-trait trait objects. 8 | fn to_any(&self) -> Box; 9 | /// Downcast to Any. 10 | fn as_any(&self) -> &(dyn Any + Send + Sync); 11 | } 12 | 13 | impl UserData for T { 14 | #[inline] 15 | fn clone_boxed(&self) -> Box { 16 | Box::new(self.clone()) 17 | } 18 | 19 | #[inline] 20 | fn to_any(&self) -> Box { 21 | Box::new(self.clone()) 22 | } 23 | 24 | #[inline] 25 | fn as_any(&self) -> &(dyn Any + Send + Sync) { 26 | self 27 | } 28 | } 29 | 30 | // We need this because we must not implement Clone for Box 31 | // directly otherwise Box would implement UserData too and 32 | // we want to avoid the user mistakenly nesting user-datas. 33 | pub(crate) struct UserDataBox(pub Box); 34 | 35 | impl Clone for UserDataBox { 36 | #[inline] 37 | fn clone(&self) -> Self { 38 | UserDataBox(self.0.clone_boxed()) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/volumetric/mod.rs: -------------------------------------------------------------------------------- 1 | //! Volume and inertia tensor computation. 2 | 3 | #[doc(inline)] 4 | pub use self::volumetric::{InertiaTensor, Volumetric}; 5 | 6 | pub use self::volumetric_ball::{ 7 | ball_area, ball_center_of_mass, ball_unit_angular_inertia, ball_volume, 8 | }; 9 | pub use self::volumetric_cone::{ 10 | cone_area, cone_center_of_mass, cone_unit_angular_inertia, cone_volume, 11 | }; 12 | pub use self::volumetric_cuboid::{ 13 | cuboid_area, cuboid_center_of_mass, cuboid_unit_angular_inertia, cuboid_volume, 14 | }; 15 | pub use self::volumetric_cylinder::{ 16 | cylinder_area, cylinder_center_of_mass, cylinder_unit_angular_inertia, cylinder_volume, 17 | }; 18 | 19 | #[cfg(feature = "dim2")] 20 | pub use self::volumetric_convex2::{ 21 | convex_hull_area, convex_hull_center_of_mass, convex_hull_unit_angular_inertia, 22 | convex_hull_volume, convex_polyline_area_and_center_of_mass_unchecked, 23 | convex_polyline_area_unchecked, convex_polyline_mass_properties_unchecked, 24 | }; 25 | #[cfg(feature = "dim3")] 26 | pub use self::volumetric_convex3::{ 27 | convex_hull_area, convex_hull_center_of_mass, convex_hull_unit_angular_inertia, 28 | convex_hull_volume, convex_mesh_area_unchecked, convex_mesh_mass_properties_unchecked, 29 | convex_mesh_volume_and_center_of_mass_unchecked, 30 | }; 31 | 32 | #[doc(hidden)] 33 | pub mod volumetric; 34 | 35 | mod volumetric_ball; 36 | mod volumetric_capsule; 37 | mod volumetric_compound; 38 | mod volumetric_cone; 39 | #[cfg(feature = "dim2")] 40 | mod volumetric_convex2; 41 | #[cfg(feature = "dim3")] 42 | mod volumetric_convex3; 43 | mod volumetric_cuboid; 44 | mod volumetric_cylinder; 45 | mod volumetric_shape; 46 | -------------------------------------------------------------------------------- /src/volumetric/volumetric_ball.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{AngularInertia, Point, DIM}; 2 | use crate::volumetric::Volumetric; 3 | use na; 4 | use na::RealField; 5 | use ncollide::shape::Ball; 6 | 7 | /// The volume of a ball. 8 | #[inline] 9 | pub fn ball_volume(radius: N) -> N { 10 | if DIM == 2 { 11 | let _pi = N::pi(); 12 | _pi * radius * radius 13 | } else { 14 | let _pi = N::pi(); 15 | _pi * radius * radius * radius * na::convert(4.0f64 / 3.0) 16 | } 17 | } 18 | 19 | /// The area of a ball. 20 | #[inline] 21 | pub fn ball_area(radius: N) -> N { 22 | if DIM == 2 { 23 | let _pi = N::pi(); 24 | _pi * radius * na::convert(2.0f64) 25 | } else { 26 | let _pi = N::pi(); 27 | _pi * radius * radius * na::convert(4.0f64) 28 | } 29 | } 30 | 31 | /// The center of mass of a ball. 32 | #[inline] 33 | pub fn ball_center_of_mass() -> Point { 34 | Point::origin() 35 | } 36 | 37 | /// The unit angular inertia of a ball. 38 | #[inline] 39 | pub fn ball_unit_angular_inertia(radius: N) -> AngularInertia { 40 | let diag = if DIM == 2 { 41 | radius * radius / na::convert(2.0f64) 42 | } else { 43 | radius * radius * na::convert(2.0f64 / 5.0) 44 | }; 45 | 46 | AngularInertia::from_diagonal_element(diag) 47 | } 48 | 49 | impl Volumetric for Ball { 50 | fn area(&self) -> N { 51 | ball_area(self.radius()) 52 | } 53 | 54 | fn volume(&self) -> N { 55 | ball_volume(self.radius()) 56 | } 57 | 58 | fn center_of_mass(&self) -> Point { 59 | ball_center_of_mass() 60 | } 61 | 62 | fn unit_angular_inertia(&self) -> AngularInertia { 63 | ball_unit_angular_inertia(self.radius()) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/volumetric/volumetric_capsule.rs: -------------------------------------------------------------------------------- 1 | use na::RealField; 2 | use ncollide::shape::Capsule; 3 | 4 | use crate::math::{AngularInertia, Point}; 5 | use crate::volumetric::{self, Volumetric}; 6 | 7 | /// Computes the volume of a capsule. 8 | pub fn capsule_volume(half_height: N, radius: N) -> N { 9 | volumetric::cylinder_volume(half_height, radius) + volumetric::ball_volume(radius) 10 | } 11 | 12 | /// Computes the area of a capsule. 13 | pub fn capsule_area(half_height: N, radius: N) -> N { 14 | volumetric::cylinder_area(half_height, radius) + volumetric::ball_area(radius) 15 | } 16 | 17 | /// Computes the unit angular inertia of a capsule. 18 | pub fn capsule_unit_angular_inertia(half_height: N, radius: N) -> AngularInertia { 19 | let mut res = volumetric::cylinder_unit_angular_inertia(half_height, radius); 20 | res += volumetric::ball_unit_angular_inertia(radius); 21 | 22 | // Parallel axis theorem for the hemispheres. 23 | if cfg!(feature = "dim3") { 24 | let h = half_height * na::convert(2.0); 25 | let extra = h * h * na::convert(0.5) + h * radius * na::convert(3.0 / 8.0); 26 | res[(0, 0)] += extra; 27 | res[(2, 2)] += extra; 28 | } else { 29 | let h = half_height * na::convert(2.0); 30 | let extra = h * h * na::convert(0.5) + h * radius * na::convert(3.0 / 8.0); 31 | res[(0, 0)] += extra; 32 | } 33 | 34 | res 35 | } 36 | 37 | impl Volumetric for Capsule { 38 | fn area(&self) -> N { 39 | capsule_area(self.half_height(), self.radius()) 40 | } 41 | 42 | fn volume(&self) -> N { 43 | capsule_volume(self.half_height(), self.radius()) 44 | } 45 | 46 | fn center_of_mass(&self) -> Point { 47 | Point::origin() 48 | } 49 | 50 | fn unit_angular_inertia(&self) -> AngularInertia { 51 | capsule_unit_angular_inertia(self.half_height(), self.radius()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/volumetric/volumetric_compound.rs: -------------------------------------------------------------------------------- 1 | use num::Zero; 2 | 3 | use crate::math::{AngularInertia, Point}; 4 | use crate::volumetric::{InertiaTensor, Volumetric}; 5 | use na::{self, RealField}; 6 | use ncollide::shape::Compound; 7 | 8 | impl Volumetric for Compound { 9 | fn area(&self) -> N { 10 | let mut stot: N = na::zero(); 11 | 12 | for &(_, ref s) in self.shapes().iter() { 13 | stot += s.area() 14 | } 15 | 16 | stot 17 | } 18 | 19 | fn volume(&self) -> N { 20 | let mut vtot: N = na::zero(); 21 | 22 | for &(_, ref s) in self.shapes().iter() { 23 | vtot += s.volume() 24 | } 25 | 26 | vtot 27 | } 28 | 29 | fn center_of_mass(&self) -> Point { 30 | let mut mtot = N::zero(); 31 | let mut ctot = Point::origin(); 32 | let mut gtot = Point::origin(); // geometric center. 33 | 34 | let shapes = self.shapes(); 35 | 36 | for &(ref m, ref s) in shapes.iter() { 37 | let (mpart, cpart, _) = s.mass_properties(na::one()); 38 | 39 | mtot += mpart; 40 | ctot += (*m * cpart * mpart).coords; 41 | gtot += (*m * cpart).coords; 42 | } 43 | 44 | if mtot.is_zero() { 45 | gtot 46 | } else { 47 | ctot / mtot 48 | } 49 | } 50 | 51 | fn unit_angular_inertia(&self) -> AngularInertia { 52 | let mut itot = AngularInertia::zero(); 53 | 54 | let com = self.center_of_mass(); 55 | let shapes = self.shapes(); 56 | 57 | for &(ref m, ref s) in shapes.iter() { 58 | let (mpart, cpart, ipart) = s.mass_properties(na::one()); 59 | 60 | itot += ipart 61 | .to_world_space(m) 62 | .to_relative_wrt_point(mpart, &(*m * cpart + (-com.coords))); 63 | } 64 | 65 | itot 66 | } 67 | 68 | /// The mass properties of this `CompoundData`. 69 | /// 70 | /// If `density` is not zero, it will be multiplied with the density of every object of the 71 | /// compound shape. 72 | fn mass_properties(&self, density: N) -> (N, Point, AngularInertia) { 73 | let mut itot = AngularInertia::zero(); 74 | let mut ctot = Point::origin(); 75 | let mut gtot = Point::origin(); // geometric center. 76 | let mut mtot = N::zero(); 77 | 78 | let shapes = self.shapes(); 79 | let props: Vec<_> = shapes 80 | .iter() 81 | .map(|&(_, ref s)| s.mass_properties(na::one())) 82 | .collect(); 83 | 84 | for (&(ref m, _), &(ref mpart, ref cpart, _)) in shapes.iter().zip(props.iter()) { 85 | mtot += *mpart; 86 | ctot += (*m * *cpart * *mpart).coords; 87 | gtot += (*m * *cpart).coords; 88 | } 89 | 90 | if mtot.is_zero() { 91 | ctot = gtot; 92 | } else { 93 | ctot /= mtot; 94 | } 95 | 96 | for (&(ref m, _), &(ref mpart, ref cpart, ref ipart)) in shapes.iter().zip(props.iter()) { 97 | itot += ipart 98 | .to_world_space(m) 99 | .to_relative_wrt_point(*mpart, &(*m * *cpart + (-ctot.coords))); 100 | } 101 | 102 | (mtot * density, ctot, itot * density) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/volumetric/volumetric_cone.rs: -------------------------------------------------------------------------------- 1 | use num::Zero; 2 | use std::ops::IndexMut; 3 | 4 | use na::{self, RealField}; 5 | use ncollide::math::Point; 6 | 7 | /// The volume of a cone. 8 | #[inline] 9 | pub fn cone_volume(dimension: usize, half_height: N, radius: N) -> N { 10 | assert!(dimension == 2 || dimension == 3); 11 | 12 | match dimension { 13 | 2 => radius * half_height * na::convert(2.0f64), 14 | 3 => radius * radius * N::pi() * half_height * na::convert(2.0f64 / 3.0), 15 | _ => unreachable!(), 16 | } 17 | } 18 | 19 | /// The area of a cone. 20 | #[inline] 21 | pub fn cone_area(dimension: usize, half_height: N, radius: N) -> N { 22 | assert!(dimension == 2 || dimension == 3); 23 | 24 | match dimension { 25 | 2 => { 26 | let height = half_height * na::convert(2.0f64); 27 | let side = (height * height + radius * radius).sqrt(); 28 | 29 | radius * na::convert(2.0f64) + side 30 | } 31 | 3 => { 32 | let _pi = N::pi(); 33 | let height = half_height + half_height; 34 | let side = (height * height + radius * radius).sqrt(); 35 | 36 | radius * radius * _pi + side * radius * _pi 37 | } 38 | _ => unreachable!(), 39 | } 40 | } 41 | 42 | /// The center of mass of a cone. 43 | #[inline] 44 | pub fn cone_center_of_mass(half_height: N) -> Point { 45 | let mut com = Point::origin(); 46 | com[1] = -half_height / na::convert(2.0f64); 47 | 48 | com 49 | } 50 | 51 | /// The unit angular inertia of a cone. 52 | #[inline] 53 | pub fn cone_unit_angular_inertia(dimension: usize, half_height: N, radius: N) -> I 54 | where 55 | N: RealField, 56 | I: Zero + IndexMut<(usize, usize), Output = N>, 57 | { 58 | assert!(dimension == 2 || dimension == 3); 59 | 60 | match dimension { 61 | 2 => { 62 | // FIXME: not sure about that… 63 | let mut res = I::zero(); 64 | 65 | res[(0, 0)] = radius * half_height * half_height * half_height / na::convert(3.0f64); 66 | 67 | res 68 | } 69 | 3 => { 70 | let sq_radius = radius * radius; 71 | let sq_height = half_height * half_height * na::convert(4.0f64); 72 | let off_principal = 73 | sq_radius * na::convert(3.0f64 / 20.0) + sq_height * na::convert(3.0f64 / 5.0); 74 | 75 | let principal = sq_radius * na::convert(3.0f64 / 10.0); 76 | 77 | let mut res = I::zero(); 78 | 79 | res[(0, 0)] = off_principal.clone(); 80 | res[(1, 1)] = principal; 81 | res[(2, 2)] = off_principal; 82 | 83 | res 84 | } 85 | _ => unreachable!(), 86 | } 87 | } 88 | 89 | //macro_rules! impl_volumetric_cone( 90 | // ($t: ident, $dimension: expr, $p: ident, $i: ident) => ( 91 | // impl Volumetric, $i> for $t { 92 | // fn area(&self) -> N { 93 | // cone_area($dimension, self.half_height(), self.radius()) 94 | // } 95 | // 96 | // fn volume(&self) -> N { 97 | // cone_volume($dimension, self.half_height(), self.radius()) 98 | // } 99 | // 100 | // fn center_of_mass(&self) -> $p { 101 | // cone_center_of_mass(self.half_height()) 102 | // } 103 | // 104 | // fn unit_angular_inertia(&self) -> $i { 105 | // cone_unit_angular_inertia($dimension, self.half_height(), self.radius()) 106 | // } 107 | // } 108 | // ) 109 | //); 110 | // 111 | //impl_volumetric_cone!(Cone2, 2, Point2, Matrix1); 112 | //impl_volumetric_cone!(Cone3, 3, Point3, Matrix3); 113 | -------------------------------------------------------------------------------- /src/volumetric/volumetric_cuboid.rs: -------------------------------------------------------------------------------- 1 | use num::Zero; 2 | 3 | use crate::math::{AngularInertia, Point, Vector, DIM}; 4 | use crate::volumetric::Volumetric; 5 | use na::{self, RealField}; 6 | use ncollide::shape::Cuboid; 7 | 8 | /// The volume of a cuboid. 9 | #[inline] 10 | pub fn cuboid_volume(half_extents: &Vector) -> N { 11 | let mut res = N::one(); 12 | 13 | for half_extent in half_extents.iter() { 14 | res = res * *half_extent * na::convert(2.0f64) 15 | } 16 | 17 | res 18 | } 19 | 20 | /// The area of a cuboid. 21 | #[inline] 22 | pub fn cuboid_area(half_extents: &Vector) -> N { 23 | if DIM == 2 { 24 | (half_extents[0] + half_extents[1]) * na::convert(4.0f64) 25 | } else { 26 | let he = half_extents; 27 | let xx = he[0] + he[0]; 28 | let yy = he[1] + he[1]; 29 | let zz = he[2] + he[2]; 30 | 31 | let side_xy = xx * yy; 32 | let side_xz = xx * zz; 33 | let side_yz = yy * zz; 34 | 35 | (side_xy + side_xz + side_yz) * na::convert(2.0f64) 36 | } 37 | } 38 | 39 | /// The center of mass of a cuboid. 40 | #[inline] 41 | pub fn cuboid_center_of_mass() -> Point { 42 | Point::origin() 43 | } 44 | 45 | /// The unit angular inertia of a cuboid. 46 | #[inline] 47 | pub fn cuboid_unit_angular_inertia(half_extents: &Vector) -> AngularInertia { 48 | if DIM == 2 { 49 | let _2: N = na::convert(2.0f64); 50 | let _i12: N = na::convert(1.0f64 / 12.0); 51 | let w = _i12 * _2 * _2; 52 | let ix = w * half_extents[0] * half_extents[0]; 53 | let iy = w * half_extents[1] * half_extents[1]; 54 | 55 | let mut res = AngularInertia::zero(); 56 | 57 | res[(0, 0)] = ix + iy; 58 | 59 | res 60 | } else { 61 | let _0: N = na::zero(); 62 | let _2: N = na::convert(2.0f64); 63 | let _i12: N = na::convert(1.0f64 / 12.0); 64 | let w = _i12 * _2 * _2; 65 | let ix = w * half_extents[0] * half_extents[0]; 66 | let iy = w * half_extents[1] * half_extents[1]; 67 | let iz = w * half_extents[2] * half_extents[2]; 68 | 69 | let mut res = AngularInertia::zero(); 70 | 71 | res[(0, 0)] = iy + iz; 72 | res[(1, 1)] = ix + iz; 73 | res[(2, 2)] = ix + iy; 74 | 75 | res 76 | } 77 | } 78 | 79 | impl Volumetric for Cuboid { 80 | fn area(&self) -> N { 81 | cuboid_area(self.half_extents()) 82 | } 83 | 84 | fn volume(&self) -> N { 85 | cuboid_volume(self.half_extents()) 86 | } 87 | 88 | fn center_of_mass(&self) -> Point { 89 | cuboid_center_of_mass() 90 | } 91 | 92 | fn unit_angular_inertia(&self) -> AngularInertia { 93 | cuboid_unit_angular_inertia(self.half_extents()) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/volumetric/volumetric_cylinder.rs: -------------------------------------------------------------------------------- 1 | use num::Zero; 2 | 3 | use crate::math::{AngularInertia, Point, DIM}; 4 | use na::{self, RealField}; 5 | 6 | /// The volume of a cylinder. 7 | #[inline] 8 | pub fn cylinder_volume(half_height: N, radius: N) -> N { 9 | if DIM == 2 { 10 | half_height * radius * na::convert(4.0f64) 11 | } else { 12 | half_height * radius * radius * N::pi() * na::convert(2.0f64) 13 | } 14 | } 15 | 16 | /// The area of a cylinder. 17 | #[inline] 18 | pub fn cylinder_area(half_height: N, radius: N) -> N { 19 | if DIM == 2 { 20 | (half_height + radius) * na::convert(2.0f64) 21 | } else { 22 | let _pi = N::pi(); 23 | let basis = radius * radius * _pi; 24 | let side = _pi * radius * (half_height + half_height) * na::convert(2.0f64); 25 | 26 | side + basis + basis 27 | } 28 | } 29 | 30 | /// The center of mass of a cylinder. 31 | #[inline] 32 | pub fn cylinder_center_of_mass() -> Point { 33 | Point::origin() 34 | } 35 | 36 | /// The unit angular inertia of a cylinder. 37 | #[inline] 38 | pub fn cylinder_unit_angular_inertia(half_height: N, radius: N) -> AngularInertia { 39 | if DIM == 2 { 40 | // Same a the rectangle. 41 | let _2: N = na::convert(2.0f64); 42 | let _i12: N = na::convert(1.0f64 / 12.0); 43 | let w = _i12 * _2 * _2; 44 | let ix = w * half_height * half_height; 45 | let iy = w * radius * radius; 46 | 47 | let mut res = AngularInertia::zero(); 48 | 49 | res[(0, 0)] = ix + iy; 50 | 51 | res 52 | } else { 53 | let sq_radius = radius * radius; 54 | let sq_height = half_height * half_height * na::convert(4.0f64); 55 | let off_principal = (sq_radius * na::convert(3.0f64) + sq_height) / na::convert(12.0f64); 56 | 57 | let mut res = AngularInertia::zero(); 58 | 59 | res[(0, 0)] = off_principal.clone(); 60 | res[(1, 1)] = sq_radius / na::convert(2.0f64); 61 | res[(2, 2)] = off_principal; 62 | 63 | res 64 | } 65 | } 66 | 67 | //impl Volumetric for Cylinder { 68 | // fn area(&self) -> N { 69 | // cylinder_area(self.half_height(), self.radius()) 70 | // } 71 | // 72 | // fn volume(&self) -> N { 73 | // cylinder_volume(self.half_height(), self.radius()) 74 | // } 75 | // 76 | // fn center_of_mass(&self) -> Point { 77 | // cylinder_center_of_mass() 78 | // } 79 | // 80 | // fn unit_angular_inertia(&self) -> AngularInertia { 81 | // cylinder_unit_angular_inertia(self.half_height(), self.radius()) 82 | // } 83 | //} 84 | -------------------------------------------------------------------------------- /src/volumetric/volumetric_shape.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{AngularInertia, Point}; 2 | use crate::volumetric::Volumetric; 3 | use na::RealField; 4 | #[cfg(feature = "dim3")] 5 | use ncollide::shape::ConvexHull; 6 | #[cfg(feature = "dim2")] 7 | use ncollide::shape::ConvexPolygon; 8 | use ncollide::shape::{Ball, Capsule, Compound, Cuboid, Shape}; 9 | 10 | macro_rules! dispatch( 11 | ($p: ty, $i: ty, $sself: ident.$name: ident($($argN: ident),*)) => { 12 | { 13 | if let Some(b) = $sself.as_shape::>() { 14 | return b.$name($($argN,)*) 15 | } 16 | if let Some(c) = $sself.as_shape::>() { 17 | return c.$name($($argN,)*) 18 | } 19 | // else if let Some(c) = $sself.as_shape::>() { 20 | // (c as &Volumetric).$name($($argN,)*) 21 | // } 22 | #[cfg(feature = "dim3")] 23 | { 24 | if let Some(c) = $sself.as_shape::>() { 25 | return c.$name($($argN,)*) 26 | } 27 | } 28 | #[cfg(feature = "dim2")] 29 | { 30 | if let Some(c) = $sself.as_shape::>() { 31 | return c.$name($($argN,)*) 32 | } 33 | } 34 | if let Some(c) = $sself.as_shape::>() { 35 | return c.$name($($argN,)*) 36 | } 37 | if let Some(c) = $sself.as_shape::>() { 38 | return c.$name($($argN,)*) 39 | } 40 | // if let Some(c) = $sself.as_shape::>() { 41 | // return c.$name($($argN,)*) 42 | // } 43 | 44 | /* 45 | * XXX: dispatch by custom type. 46 | */ 47 | panic!("The `Volumetric` is not implemented by the given shape.") 48 | } 49 | } 50 | ); 51 | 52 | impl Volumetric for dyn Shape { 53 | fn area(&self) -> N { 54 | dispatch!(Point, AngularInertia, self.area()) 55 | } 56 | 57 | fn volume(&self) -> N { 58 | dispatch!(Point, AngularInertia, self.volume()) 59 | } 60 | 61 | fn center_of_mass(&self) -> Point { 62 | dispatch!(Point, AngularInertia, self.center_of_mass()) 63 | } 64 | 65 | fn unit_angular_inertia(&self) -> AngularInertia { 66 | dispatch!(Point, AngularInertia, self.unit_angular_inertia()) 67 | } 68 | 69 | fn mass_properties(&self, density: N) -> (N, Point, AngularInertia) { 70 | dispatch!(Point, AngularInertia, self.mass_properties(density)) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/world/mod.rs: -------------------------------------------------------------------------------- 1 | //! The physics world. 2 | 3 | pub use self::geometrical_world::{ 4 | BroadPhasePairFilterSets, DefaultBroadPhasePairFilterSets, DefaultGeometricalWorld, 5 | GeometricalWorld, 6 | }; 7 | pub use self::mechanical_world::{DefaultMechanicalWorld, MechanicalWorld}; 8 | 9 | mod geometrical_world; 10 | mod mechanical_world; 11 | -------------------------------------------------------------------------------- /src_testbed/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate kiss3d; 3 | extern crate nalgebra as na; 4 | #[cfg(feature = "dim2")] 5 | extern crate ncollide2d as ncollide; 6 | #[cfg(feature = "dim3")] 7 | extern crate ncollide3d as ncollide; 8 | #[cfg(feature = "dim2")] 9 | extern crate nphysics2d as nphysics; 10 | #[cfg(feature = "dim3")] 11 | extern crate nphysics3d as nphysics; 12 | extern crate num_traits as num; 13 | #[cfg(all(feature = "dim2", feature = "fluids"))] 14 | extern crate salva2d as salva; 15 | #[cfg(all(feature = "dim3", feature = "fluids"))] 16 | extern crate salva3d as salva; 17 | #[macro_use] 18 | extern crate bitflags; 19 | 20 | #[cfg(feature = "log")] 21 | #[macro_use] 22 | extern crate log; 23 | 24 | pub use crate::engine::GraphicsManager; 25 | pub use crate::testbed::Testbed; 26 | 27 | #[cfg(feature = "box2d-backend")] 28 | mod box2d_world; 29 | mod engine; 30 | pub mod objects; 31 | mod testbed; 32 | mod ui; 33 | -------------------------------------------------------------------------------- /src_testbed/objects/ball.rs: -------------------------------------------------------------------------------- 1 | use crate::objects::node::{self, GraphicsNode}; 2 | use kiss3d::window::Window; 3 | use na::{Point3, RealField}; 4 | use nphysics::math::Isometry; 5 | use nphysics::object::{DefaultColliderHandle, DefaultColliderSet}; 6 | 7 | pub struct Ball { 8 | color: Point3, 9 | base_color: Point3, 10 | delta: Isometry, 11 | gfx: GraphicsNode, 12 | collider: DefaultColliderHandle, 13 | } 14 | 15 | impl Ball { 16 | pub fn new( 17 | collider: DefaultColliderHandle, 18 | colliders: &DefaultColliderSet, 19 | delta: Isometry, 20 | radius: f32, 21 | color: Point3, 22 | window: &mut Window, 23 | ) -> Ball { 24 | #[cfg(feature = "dim2")] 25 | let node = window.add_circle(radius); 26 | #[cfg(feature = "dim3")] 27 | let node = window.add_sphere(radius); 28 | 29 | let mut res = Ball { 30 | color, 31 | base_color: color, 32 | delta, 33 | gfx: node, 34 | collider, 35 | }; 36 | 37 | if colliders 38 | .get(collider) 39 | .unwrap() 40 | .query_type() 41 | .is_proximity_query() 42 | { 43 | res.gfx.set_surface_rendering_activation(false); 44 | res.gfx.set_lines_width(1.0); 45 | } 46 | 47 | let pos: Isometry = 48 | na::convert_unchecked(*colliders.get(collider).unwrap().position()); 49 | let pos: Isometry = na::convert(pos); 50 | 51 | // res.gfx.set_texture_from_file(&Path::new("media/kitten.png"), "kitten"); 52 | res.gfx.set_color(color.x, color.y, color.z); 53 | res.gfx.set_local_transformation(pos * res.delta); 54 | res.update(colliders); 55 | 56 | res 57 | } 58 | 59 | pub fn select(&mut self) { 60 | self.color = Point3::new(1.0, 0.0, 0.0); 61 | } 62 | 63 | pub fn unselect(&mut self) { 64 | self.color = self.base_color; 65 | } 66 | 67 | pub fn set_color(&mut self, color: Point3) { 68 | self.gfx.set_color(color.x, color.y, color.z); 69 | self.color = color; 70 | self.base_color = color; 71 | } 72 | 73 | pub fn update(&mut self, colliders: &DefaultColliderSet) { 74 | node::update_scene_node( 75 | &mut self.gfx, 76 | colliders, 77 | self.collider, 78 | &self.color, 79 | &self.delta, 80 | ); 81 | } 82 | 83 | pub fn scene_node(&self) -> &GraphicsNode { 84 | &self.gfx 85 | } 86 | 87 | pub fn scene_node_mut(&mut self) -> &mut GraphicsNode { 88 | &mut self.gfx 89 | } 90 | 91 | pub fn object(&self) -> DefaultColliderHandle { 92 | self.collider 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src_testbed/objects/box_node.rs: -------------------------------------------------------------------------------- 1 | use crate::objects::node::{self, GraphicsNode}; 2 | use kiss3d::window; 3 | use na::{Point3, RealField}; 4 | use nphysics::math::{Isometry, Vector}; 5 | use nphysics::object::{DefaultColliderHandle, DefaultColliderSet}; 6 | 7 | pub struct Box { 8 | color: Point3, 9 | base_color: Point3, 10 | delta: Isometry, 11 | gfx: GraphicsNode, 12 | collider: DefaultColliderHandle, 13 | } 14 | 15 | impl Box { 16 | pub fn new( 17 | collider: DefaultColliderHandle, 18 | colliders: &DefaultColliderSet, 19 | delta: Isometry, 20 | half_extents: Vector, 21 | color: Point3, 22 | window: &mut window::Window, 23 | ) -> Box { 24 | let extents = half_extents * 2.0; 25 | #[cfg(feature = "dim2")] 26 | let node = window.add_rectangle(extents.x, extents.y); 27 | #[cfg(feature = "dim3")] 28 | let node = window.add_cube(extents.x, extents.y, extents.z); 29 | 30 | let mut res = Box { 31 | color, 32 | base_color: color, 33 | delta, 34 | gfx: node, 35 | collider, 36 | }; 37 | 38 | if colliders 39 | .get(collider) 40 | .unwrap() 41 | .query_type() 42 | .is_proximity_query() 43 | { 44 | res.gfx.set_surface_rendering_activation(false); 45 | res.gfx.set_lines_width(1.0); 46 | } 47 | 48 | let pos: Isometry = 49 | na::convert_unchecked(*colliders.get(collider).unwrap().position()); 50 | let pos: Isometry = na::convert(pos); 51 | 52 | res.gfx.set_color(color.x, color.y, color.z); 53 | res.gfx.set_local_transformation(pos * res.delta); 54 | res.update(colliders); 55 | 56 | res 57 | } 58 | 59 | pub fn select(&mut self) { 60 | self.color = Point3::new(1.0, 0.0, 0.0); 61 | } 62 | 63 | pub fn unselect(&mut self) { 64 | self.color = self.base_color; 65 | } 66 | 67 | pub fn set_color(&mut self, color: Point3) { 68 | self.gfx.set_color(color.x, color.y, color.z); 69 | self.color = color; 70 | self.base_color = color; 71 | } 72 | 73 | pub fn update(&mut self, colliders: &DefaultColliderSet) { 74 | node::update_scene_node( 75 | &mut self.gfx, 76 | colliders, 77 | self.collider, 78 | &self.color, 79 | &self.delta, 80 | ); 81 | } 82 | 83 | pub fn scene_node(&self) -> &GraphicsNode { 84 | &self.gfx 85 | } 86 | 87 | pub fn scene_node_mut(&mut self) -> &mut GraphicsNode { 88 | &mut self.gfx 89 | } 90 | 91 | pub fn object(&self) -> DefaultColliderHandle { 92 | self.collider 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src_testbed/objects/capsule.rs: -------------------------------------------------------------------------------- 1 | use crate::objects::node::{self, GraphicsNode}; 2 | use kiss3d::window; 3 | use na::{Point3, RealField}; 4 | use nphysics::math::Isometry; 5 | use nphysics::object::{DefaultColliderHandle, DefaultColliderSet}; 6 | 7 | pub struct Capsule { 8 | color: Point3, 9 | base_color: Point3, 10 | delta: Isometry, 11 | gfx: GraphicsNode, 12 | collider: DefaultColliderHandle, 13 | } 14 | 15 | impl Capsule { 16 | pub fn new( 17 | collider: DefaultColliderHandle, 18 | colliders: &DefaultColliderSet, 19 | delta: Isometry, 20 | r: f32, 21 | h: f32, 22 | color: Point3, 23 | window: &mut window::Window, 24 | ) -> Capsule { 25 | #[cfg(feature = "dim2")] 26 | let node = window.add_planar_capsule(r, h); 27 | #[cfg(feature = "dim3")] 28 | let node = window.add_capsule(r, h); 29 | 30 | let mut res = Capsule { 31 | color, 32 | base_color: color, 33 | delta, 34 | gfx: node, 35 | collider, 36 | }; 37 | 38 | let pos: Isometry = 39 | na::convert_unchecked(*colliders.get(collider).unwrap().position()); 40 | let pos: Isometry = na::convert(pos); 41 | 42 | if colliders 43 | .get(collider) 44 | .unwrap() 45 | .query_type() 46 | .is_proximity_query() 47 | { 48 | res.gfx.set_surface_rendering_activation(false); 49 | res.gfx.set_lines_width(1.0); 50 | } 51 | res.gfx.set_color(color.x, color.y, color.z); 52 | res.gfx.set_local_transformation(pos * res.delta); 53 | res.update(colliders); 54 | 55 | res 56 | } 57 | 58 | pub fn select(&mut self) { 59 | self.color = Point3::new(1.0, 0.0, 0.0); 60 | } 61 | 62 | pub fn unselect(&mut self) { 63 | self.color = self.base_color; 64 | } 65 | 66 | pub fn update(&mut self, colliders: &DefaultColliderSet) { 67 | node::update_scene_node( 68 | &mut self.gfx, 69 | colliders, 70 | self.collider, 71 | &self.color, 72 | &self.delta, 73 | ); 74 | } 75 | 76 | pub fn set_color(&mut self, color: Point3) { 77 | self.gfx.set_color(color.x, color.y, color.z); 78 | self.color = color; 79 | self.base_color = color; 80 | } 81 | 82 | pub fn scene_node(&self) -> &GraphicsNode { 83 | &self.gfx 84 | } 85 | 86 | pub fn scene_node_mut(&mut self) -> &mut GraphicsNode { 87 | &mut self.gfx 88 | } 89 | 90 | pub fn object(&self) -> DefaultColliderHandle { 91 | self.collider 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src_testbed/objects/convex.rs: -------------------------------------------------------------------------------- 1 | use crate::objects::node::{self, GraphicsNode}; 2 | use kiss3d::window::Window; 3 | use na::{Point3, RealField}; 4 | #[cfg(feature = "dim3")] 5 | use ncollide::procedural::TriMesh; 6 | #[cfg(feature = "dim2")] 7 | use nphysics::math::Point; 8 | use nphysics::math::{Isometry, Vector}; 9 | use nphysics::object::{DefaultColliderHandle, DefaultColliderSet}; 10 | 11 | pub struct Convex { 12 | color: Point3, 13 | base_color: Point3, 14 | delta: Isometry, 15 | gfx: GraphicsNode, 16 | collider: DefaultColliderHandle, 17 | } 18 | 19 | impl Convex { 20 | #[cfg(feature = "dim2")] 21 | pub fn new( 22 | collider: DefaultColliderHandle, 23 | colliders: &DefaultColliderSet, 24 | delta: Isometry, 25 | vertices: Vec>, 26 | color: Point3, 27 | window: &mut Window, 28 | ) -> Convex { 29 | let mut res = Convex { 30 | color, 31 | base_color: color, 32 | delta, 33 | gfx: window.add_convex_polygon(vertices, Vector::from_element(1.0)), 34 | collider, 35 | }; 36 | 37 | if colliders 38 | .get(collider) 39 | .unwrap() 40 | .query_type() 41 | .is_proximity_query() 42 | { 43 | res.gfx.set_surface_rendering_activation(false); 44 | res.gfx.set_lines_width(1.0); 45 | } 46 | 47 | let pos: Isometry = 48 | na::convert_unchecked(*colliders.get(collider).unwrap().position()); 49 | let pos: Isometry = na::convert(pos); 50 | 51 | res.gfx.set_color(color.x, color.y, color.z); 52 | res.gfx.set_local_transformation(pos * res.delta); 53 | res.update(colliders); 54 | 55 | res 56 | } 57 | 58 | #[cfg(feature = "dim3")] 59 | pub fn new( 60 | collider: DefaultColliderHandle, 61 | colliders: &DefaultColliderSet, 62 | delta: Isometry, 63 | convex: &TriMesh, 64 | color: Point3, 65 | window: &mut Window, 66 | ) -> Convex { 67 | let mut res = Convex { 68 | color, 69 | base_color: color, 70 | delta, 71 | gfx: window.add_trimesh(convex.clone(), Vector::from_element(1.0)), 72 | collider, 73 | }; 74 | 75 | if colliders 76 | .get(collider) 77 | .unwrap() 78 | .query_type() 79 | .is_proximity_query() 80 | { 81 | res.gfx.set_surface_rendering_activation(false); 82 | res.gfx.set_lines_width(1.0); 83 | } 84 | 85 | let pos: Isometry = 86 | na::convert_unchecked(*colliders.get(collider).unwrap().position()); 87 | let pos: Isometry = na::convert(pos); 88 | 89 | res.gfx.set_color(color.x, color.y, color.z); 90 | res.gfx.set_local_transformation(pos * res.delta); 91 | res.update(colliders); 92 | 93 | res 94 | } 95 | 96 | pub fn select(&mut self) { 97 | self.color = Point3::new(1.0, 0.0, 0.0); 98 | } 99 | 100 | pub fn unselect(&mut self) { 101 | self.color = self.base_color; 102 | } 103 | 104 | pub fn set_color(&mut self, color: Point3) { 105 | self.gfx.set_color(color.x, color.y, color.z); 106 | self.color = color; 107 | self.base_color = color; 108 | } 109 | 110 | pub fn update(&mut self, colliders: &DefaultColliderSet) { 111 | node::update_scene_node( 112 | &mut self.gfx, 113 | colliders, 114 | self.collider, 115 | &self.color, 116 | &self.delta, 117 | ); 118 | } 119 | 120 | pub fn scene_node(&self) -> &GraphicsNode { 121 | &self.gfx 122 | } 123 | 124 | pub fn scene_node_mut(&mut self) -> &mut GraphicsNode { 125 | &mut self.gfx 126 | } 127 | 128 | pub fn object(&self) -> DefaultColliderHandle { 129 | self.collider 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src_testbed/objects/mesh.rs: -------------------------------------------------------------------------------- 1 | use crate::objects::node; 2 | use kiss3d::resource; 3 | use kiss3d::scene::SceneNode; 4 | use kiss3d::window::Window; 5 | use na::{self, Isometry3, Point3, RealField, Vector3}; 6 | use ncollide::shape::TriMesh; 7 | use nphysics::math::Isometry; 8 | use nphysics::object::{ColliderAnchor, DefaultColliderHandle, DefaultColliderSet}; 9 | use std::cell::RefCell; 10 | use std::rc::Rc; 11 | 12 | pub struct Mesh { 13 | color: Point3, 14 | base_color: Point3, 15 | delta: Isometry3, 16 | gfx: SceneNode, 17 | collider: DefaultColliderHandle, 18 | } 19 | 20 | impl Mesh { 21 | pub fn new( 22 | collider: DefaultColliderHandle, 23 | colliders: &DefaultColliderSet, 24 | delta: Isometry3, 25 | vertices: Vec>, 26 | indices: Vec>, 27 | color: Point3, 28 | window: &mut Window, 29 | ) -> Mesh { 30 | let vs = vertices; 31 | let is = indices.into_iter().map(na::convert).collect(); 32 | 33 | let mesh = resource::Mesh::new(vs, is, None, None, false); 34 | 35 | let mut res = Mesh { 36 | color, 37 | base_color: color, 38 | delta, 39 | gfx: window.add_mesh(Rc::new(RefCell::new(mesh)), Vector3::from_element(1.0)), 40 | collider, 41 | }; 42 | 43 | if colliders 44 | .get(collider) 45 | .unwrap() 46 | .query_type() 47 | .is_proximity_query() 48 | { 49 | res.gfx.set_surface_rendering_activation(false); 50 | res.gfx.set_lines_width(1.0); 51 | } 52 | 53 | let pos: Isometry = 54 | na::convert_unchecked(*colliders.get(collider).unwrap().position()); 55 | let pos: Isometry = na::convert(pos); 56 | 57 | res.gfx.enable_backface_culling(false); 58 | res.gfx.set_color(color.x, color.y, color.z); 59 | res.gfx.set_local_transformation(pos * res.delta); 60 | res.update(colliders); 61 | 62 | res 63 | } 64 | 65 | pub fn select(&mut self) { 66 | self.color = Point3::new(1.0, 0.0, 0.0); 67 | } 68 | 69 | pub fn unselect(&mut self) { 70 | self.color = self.base_color; 71 | } 72 | 73 | pub fn set_color(&mut self, color: Point3) { 74 | self.gfx.set_color(color.x, color.y, color.z); 75 | self.color = color; 76 | self.base_color = color; 77 | } 78 | 79 | pub fn update(&mut self, colliders: &DefaultColliderSet) { 80 | node::update_scene_node( 81 | &mut self.gfx, 82 | colliders, 83 | self.collider, 84 | &self.color, 85 | &self.delta, 86 | ); 87 | 88 | // Update if some deformation occurred. 89 | // FIXME: don't update if it did not move. 90 | if let Some(c) = colliders.get(self.collider) { 91 | if let ColliderAnchor::OnDeformableBody { .. } = c.anchor() { 92 | let shape = c.shape().as_shape::>().unwrap(); 93 | let vtx = shape.points(); 94 | 95 | self.gfx.modify_vertices(&mut |vertices| { 96 | for (v, new_v) in vertices.iter_mut().zip(vtx.iter()) { 97 | *v = na::convert::, Point3>(na::convert_unchecked(*new_v)); 98 | } 99 | }); 100 | self.gfx.recompute_normals(); 101 | } 102 | } 103 | } 104 | 105 | pub fn scene_node(&self) -> &SceneNode { 106 | &self.gfx 107 | } 108 | 109 | pub fn scene_node_mut(&mut self) -> &mut SceneNode { 110 | &mut self.gfx 111 | } 112 | 113 | pub fn object(&self) -> DefaultColliderHandle { 114 | self.collider 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src_testbed/objects/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "fluids")] 2 | pub use self::fluid::FluidRenderingMode; 3 | 4 | pub mod ball; 5 | pub mod box_node; 6 | pub mod capsule; 7 | pub mod convex; 8 | #[cfg(feature = "fluids")] 9 | pub mod fluid; 10 | pub mod heightfield; 11 | #[cfg(feature = "dim3")] 12 | pub mod mesh; 13 | pub mod node; 14 | pub mod plane; 15 | #[cfg(feature = "dim2")] 16 | pub mod polyline; 17 | -------------------------------------------------------------------------------- /src_testbed/objects/plane.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "dim3")] 2 | use crate::objects::node::GraphicsNode; 3 | use kiss3d::window::Window; 4 | #[cfg(feature = "dim3")] 5 | use na::Vector3; 6 | use na::{Point3, RealField}; 7 | #[cfg(feature = "dim2")] 8 | use nphysics::math::{Point, Vector}; 9 | use nphysics::object::{DefaultColliderHandle, DefaultColliderSet}; 10 | #[cfg(feature = "dim3")] 11 | use num::Zero; 12 | 13 | #[cfg(feature = "dim3")] 14 | pub struct Plane { 15 | gfx: GraphicsNode, 16 | collider: DefaultColliderHandle, 17 | } 18 | 19 | #[cfg(feature = "dim2")] 20 | pub struct Plane { 21 | color: Point3, 22 | base_color: Point3, 23 | position: Point, 24 | normal: na::Unit>, 25 | collider: DefaultColliderHandle, 26 | } 27 | 28 | impl Plane { 29 | #[cfg(feature = "dim2")] 30 | pub fn new( 31 | collider: DefaultColliderHandle, 32 | colliders: &DefaultColliderSet, 33 | position: &Point, 34 | normal: &Vector, 35 | color: Point3, 36 | _: &mut Window, 37 | ) -> Plane { 38 | let mut res = Plane { 39 | color, 40 | base_color: color, 41 | position: *position, 42 | normal: na::Unit::new_normalize(*normal), 43 | collider, 44 | }; 45 | 46 | res.update(colliders); 47 | res 48 | } 49 | 50 | #[cfg(feature = "dim3")] 51 | pub fn new( 52 | collider: DefaultColliderHandle, 53 | colliders: &DefaultColliderSet, 54 | world_pos: &Point3, 55 | world_normal: &Vector3, 56 | color: Point3, 57 | window: &mut Window, 58 | ) -> Plane { 59 | let mut res = Plane { 60 | gfx: window.add_quad(100.0, 100.0, 10, 10), 61 | collider, 62 | }; 63 | 64 | if colliders 65 | .get(collider) 66 | .unwrap() 67 | .query_type() 68 | .is_proximity_query() 69 | { 70 | res.gfx.set_surface_rendering_activation(false); 71 | res.gfx.set_lines_width(1.0); 72 | } 73 | 74 | res.gfx.set_color(color.x, color.y, color.z); 75 | 76 | let up = if world_normal.z.is_zero() && world_normal.y.is_zero() { 77 | Vector3::z() 78 | } else { 79 | Vector3::x() 80 | }; 81 | 82 | res.gfx 83 | .reorient(world_pos, &(*world_pos + *world_normal), &up); 84 | 85 | res.update(colliders); 86 | 87 | res 88 | } 89 | 90 | pub fn select(&mut self) {} 91 | 92 | pub fn unselect(&mut self) {} 93 | 94 | pub fn update(&mut self, _: &DefaultColliderSet) { 95 | // FIXME: atm we assume the plane does not move 96 | } 97 | 98 | #[cfg(feature = "dim3")] 99 | pub fn set_color(&mut self, color: Point3) { 100 | self.gfx.set_color(color.x, color.y, color.z); 101 | } 102 | 103 | #[cfg(feature = "dim2")] 104 | pub fn set_color(&mut self, color: Point3) { 105 | self.color = color; 106 | self.base_color = color; 107 | } 108 | 109 | #[cfg(feature = "dim3")] 110 | pub fn scene_node(&self) -> &GraphicsNode { 111 | &self.gfx 112 | } 113 | 114 | #[cfg(feature = "dim3")] 115 | pub fn scene_node_mut(&mut self) -> &mut GraphicsNode { 116 | &mut self.gfx 117 | } 118 | 119 | pub fn object(&self) -> DefaultColliderHandle { 120 | self.collider 121 | } 122 | 123 | #[cfg(feature = "dim2")] 124 | pub fn draw(&mut self, window: &mut Window) { 125 | let orth = Vector::new(-self.normal.y, self.normal.x); 126 | window.draw_planar_line( 127 | &(self.position - orth * 50.0), 128 | &(self.position + orth * 50.0), 129 | &self.color, 130 | ); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src_testbed/objects/polyline.rs: -------------------------------------------------------------------------------- 1 | use kiss3d::window::Window; 2 | use na::{Isometry2, Point2, Point3, RealField}; 3 | use ncollide2d::shape; 4 | use nphysics2d::object::{ColliderAnchor, DefaultColliderHandle, DefaultColliderSet}; 5 | 6 | pub struct Polyline { 7 | color: Point3, 8 | base_color: Point3, 9 | vertices: Vec>, 10 | indices: Vec>, 11 | collider: DefaultColliderHandle, 12 | pos: Isometry2, 13 | } 14 | 15 | impl Polyline { 16 | pub fn new( 17 | collider: DefaultColliderHandle, 18 | colliders: &DefaultColliderSet, 19 | _: Isometry2, 20 | vertices: Vec>, 21 | indices: Vec>, 22 | color: Point3, 23 | _: &mut Window, 24 | ) -> Polyline { 25 | let mut res = Polyline { 26 | color, 27 | pos: Isometry2::identity(), 28 | base_color: color, 29 | vertices, 30 | indices, 31 | collider, 32 | }; 33 | 34 | res.update(colliders); 35 | res 36 | } 37 | 38 | pub fn select(&mut self) { 39 | self.color = Point3::new(1.0, 0.0, 0.0); 40 | } 41 | 42 | pub fn unselect(&mut self) { 43 | self.color = self.base_color; 44 | } 45 | 46 | pub fn set_color(&mut self, color: Point3) { 47 | self.color = color; 48 | self.base_color = color; 49 | } 50 | 51 | pub fn update(&mut self, colliders: &DefaultColliderSet) { 52 | // Update if some deformation occurred. 53 | // FIXME: don't update if it did not move. 54 | if let Some(c) = colliders.get(self.collider) { 55 | self.pos = na::convert::, _>(na::convert_unchecked(*c.position())); 56 | 57 | if let ColliderAnchor::OnDeformableBody { .. } = c.anchor() { 58 | let shape = c.shape().as_shape::>().unwrap(); 59 | self.vertices = shape 60 | .points() 61 | .iter() 62 | .map(|p| na::convert::, Point2>(na::convert_unchecked(*p))) 63 | .collect(); 64 | self.indices.clear(); 65 | 66 | for e in shape.edges() { 67 | self.indices.push(e.indices); 68 | } 69 | } 70 | } 71 | } 72 | 73 | pub fn object(&self) -> DefaultColliderHandle { 74 | self.collider 75 | } 76 | 77 | pub fn draw(&mut self, window: &mut Window) { 78 | for idx in &self.indices { 79 | let p1 = self.pos * self.vertices[idx.x]; 80 | let p2 = self.pos * self.vertices[idx.y]; 81 | window.draw_planar_line(&p1, &p2, &self.color) 82 | } 83 | } 84 | } 85 | --------------------------------------------------------------------------------