├── .github ├── FUNDING.yml └── workflows │ └── parry-ci-build.yml ├── .gitignore ├── ARCHITECTURE.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── assets └── tests │ ├── bar.obj │ ├── center_cylinder.obj │ ├── low_poly_bunny.obj │ ├── offset_cylinder.obj │ ├── poly_cylinder.obj │ └── stairs.obj ├── crates ├── parry2d-f64 │ └── Cargo.toml ├── parry2d │ ├── Cargo.toml │ ├── examples │ │ ├── aabb2d.rs │ │ ├── ball2d.rs │ │ ├── bounding_sphere2d.rs │ │ ├── common_macroquad2d.rs │ │ ├── contact_query2d.rs │ │ ├── convex2d.rs │ │ ├── convex_hull2d.rs │ │ ├── convex_try_new2d.rs │ │ ├── cuboid2d.rs │ │ ├── distance_query2d.rs │ │ ├── plane2d.rs │ │ ├── point_in_poly2d.rs │ │ ├── polygons_intersection2d.rs │ │ ├── polyline2d.rs │ │ ├── project_point2d.rs │ │ ├── proximity_query2d.rs │ │ ├── raycasts_animated.rs │ │ ├── solid_point_query2d.rs │ │ ├── solid_ray_cast2d.rs │ │ └── time_of_impact_query2d.rs │ └── tests │ │ ├── geometry │ │ ├── aabb_scale.rs │ │ ├── ball_ball_toi.rs │ │ ├── ball_cuboid_contact.rs │ │ ├── convex_polygons_intersection.rs │ │ ├── epa2.rs │ │ ├── epa_convergence.rs │ │ ├── mod.rs │ │ ├── ray_cast.rs │ │ └── time_of_impact2.rs │ │ ├── lib.rs │ │ └── query │ │ ├── mod.rs │ │ ├── point_composite_shape.rs │ │ └── point_triangle.rs ├── parry3d-f64 │ └── Cargo.toml └── parry3d │ ├── Cargo.toml │ ├── benches │ ├── all.rs │ ├── bounding_volume │ │ └── mod.rs │ ├── common │ │ ├── default_gen.rs │ │ ├── generators.rs │ │ ├── macros.rs │ │ ├── mod.rs │ │ └── unref.rs │ ├── query │ │ ├── algorithm.rs │ │ ├── contacts.rs │ │ ├── mod.rs │ │ └── ray.rs │ └── support_map │ │ └── mod.rs │ ├── examples │ ├── aabb3d.rs │ ├── ball3d.rs │ ├── bounding_sphere3d.rs │ ├── capsule.rs │ ├── common_macroquad3d.rs │ ├── cone.rs │ ├── contact_query3d.rs │ ├── convex3d.rs │ ├── convex_decomposition.rs │ ├── convex_hull3d.rs │ ├── convex_try_new3d.rs │ ├── cuboid3d.rs │ ├── cylinder.rs │ ├── distance_query3d.rs │ ├── getting_started.rs │ ├── mesh3d.rs │ ├── plane3d.rs │ ├── plane_intersection.rs │ ├── polyline3d.rs │ ├── project_point3d.rs │ ├── proximity_query3d.rs │ ├── solid_point_query3d.rs │ ├── solid_ray_cast3d.rs │ └── time_of_impact_query3d.rs │ └── tests │ ├── geometry │ ├── aabb_scale.rs │ ├── ball_ball_toi.rs │ ├── ball_triangle_toi.rs │ ├── convex_hull.rs │ ├── cuboid_ray_cast.rs │ ├── cylinder_cuboid_contact.rs │ ├── epa3.rs │ ├── mod.rs │ ├── still_objects_toi.rs │ ├── time_of_impact3.rs │ ├── trimesh_connected_components.rs │ ├── trimesh_intersection.rs │ └── trimesh_trimesh_toi.rs │ └── lib.rs ├── publish.sh ├── rustfmt.toml ├── src ├── bounding_volume │ ├── aabb.rs │ ├── aabb_ball.rs │ ├── aabb_capsule.rs │ ├── aabb_convex_polygon.rs │ ├── aabb_convex_polyhedron.rs │ ├── aabb_cuboid.rs │ ├── aabb_halfspace.rs │ ├── aabb_heightfield.rs │ ├── aabb_support_map.rs │ ├── aabb_triangle.rs │ ├── aabb_utils.rs │ ├── aabb_voxels.rs │ ├── bounding_sphere.rs │ ├── bounding_sphere_ball.rs │ ├── bounding_sphere_capsule.rs │ ├── bounding_sphere_cone.rs │ ├── bounding_sphere_convex.rs │ ├── bounding_sphere_convex_polygon.rs │ ├── bounding_sphere_cuboid.rs │ ├── bounding_sphere_cylinder.rs │ ├── bounding_sphere_halfspace.rs │ ├── bounding_sphere_heightfield.rs │ ├── bounding_sphere_polyline.rs │ ├── bounding_sphere_segment.rs │ ├── bounding_sphere_triangle.rs │ ├── bounding_sphere_trimesh.rs │ ├── bounding_sphere_utils.rs │ ├── bounding_sphere_voxels.rs │ ├── bounding_volume.rs │ ├── mod.rs │ └── simd_aabb.rs ├── lib.rs ├── mass_properties │ ├── mass_properties.rs │ ├── mass_properties_ball.rs │ ├── mass_properties_capsule.rs │ ├── mass_properties_compound.rs │ ├── mass_properties_cone.rs │ ├── mass_properties_convex_polygon.rs │ ├── mass_properties_convex_polyhedron.rs │ ├── mass_properties_cuboid.rs │ ├── mass_properties_cylinder.rs │ ├── mass_properties_triangle.rs │ ├── mass_properties_trimesh2d.rs │ ├── mass_properties_trimesh3d.rs │ ├── mass_properties_voxels.rs │ └── mod.rs ├── partitioning │ ├── mod.rs │ ├── qbvh │ │ ├── build.rs │ │ ├── mod.rs │ │ ├── qbvh.rs │ │ ├── traversal.rs │ │ ├── update.rs │ │ ├── update │ │ │ └── tests.rs │ │ └── utils.rs │ └── visitor.rs ├── query │ ├── clip │ │ ├── clip_aabb_line.rs │ │ ├── clip_aabb_polygon.rs │ │ ├── clip_halfspace_polygon.rs │ │ ├── clip_segment_segment.rs │ │ └── mod.rs │ ├── closest_points │ │ ├── closest_points.rs │ │ ├── closest_points_ball_ball.rs │ │ ├── closest_points_ball_convex_polyhedron.rs │ │ ├── closest_points_composite_shape_shape.rs │ │ ├── closest_points_cuboid_cuboid.rs │ │ ├── closest_points_cuboid_triangle.rs │ │ ├── closest_points_halfspace_support_map.rs │ │ ├── closest_points_line_line.rs │ │ ├── closest_points_segment_segment.rs │ │ ├── closest_points_shape_shape.rs │ │ ├── closest_points_support_map_support_map.rs │ │ └── mod.rs │ ├── contact │ │ ├── contact.rs │ │ ├── contact_ball_ball.rs │ │ ├── contact_ball_convex_polyhedron.rs │ │ ├── contact_composite_shape_shape.rs │ │ ├── contact_cuboid_cuboid.rs │ │ ├── contact_halfspace_support_map.rs │ │ ├── contact_shape_shape.rs │ │ ├── contact_support_map_support_map.rs │ │ └── mod.rs │ ├── contact_manifolds │ │ ├── contact_manifold.rs │ │ ├── contact_manifolds_ball_ball.rs │ │ ├── contact_manifolds_capsule_capsule.rs │ │ ├── contact_manifolds_composite_shape_composite_shape.rs │ │ ├── contact_manifolds_composite_shape_shape.rs │ │ ├── contact_manifolds_convex_ball.rs │ │ ├── contact_manifolds_cuboid_capsule.rs │ │ ├── contact_manifolds_cuboid_cuboid.rs │ │ ├── contact_manifolds_cuboid_triangle.rs │ │ ├── contact_manifolds_halfspace_pfm.rs │ │ ├── contact_manifolds_heightfield_composite_shape.rs │ │ ├── contact_manifolds_heightfield_shape.rs │ │ ├── contact_manifolds_pfm_pfm.rs │ │ ├── contact_manifolds_trimesh_shape.rs │ │ ├── contact_manifolds_voxels_ball.rs │ │ ├── contact_manifolds_voxels_composite_shape.rs │ │ ├── contact_manifolds_voxels_shape.rs │ │ ├── contact_manifolds_voxels_voxels.rs │ │ ├── contact_manifolds_workspace.rs │ │ ├── mod.rs │ │ ├── normals_constraint.rs │ │ └── polygon_polygon_contact_generator.rs │ ├── default_query_dispatcher.rs │ ├── distance │ │ ├── distance.rs │ │ ├── distance_ball_ball.rs │ │ ├── distance_ball_convex_polyhedron.rs │ │ ├── distance_composite_shape_shape.rs │ │ ├── distance_cuboid_cuboid.rs │ │ ├── distance_halfspace_support_map.rs │ │ ├── distance_segment_segment.rs │ │ ├── distance_support_map_support_map.rs │ │ └── mod.rs │ ├── epa │ │ ├── epa2.rs │ │ ├── epa3.rs │ │ └── mod.rs │ ├── error.rs │ ├── gjk │ │ ├── cso_point.rs │ │ ├── gjk.rs │ │ ├── mod.rs │ │ ├── special_support_maps.rs │ │ ├── voronoi_simplex2.rs │ │ └── voronoi_simplex3.rs │ ├── intersection_test │ │ ├── intersection_test.rs │ │ ├── intersection_test_ball_ball.rs │ │ ├── intersection_test_ball_point_query.rs │ │ ├── intersection_test_composite_shape_shape.rs │ │ ├── intersection_test_cuboid_cuboid.rs │ │ ├── intersection_test_cuboid_segment.rs │ │ ├── intersection_test_cuboid_triangle.rs │ │ ├── intersection_test_halfspace_support_map.rs │ │ ├── intersection_test_polygon_polygon.rs │ │ ├── intersection_test_support_map_support_map.rs │ │ ├── intersection_test_voxels_shape.rs │ │ └── mod.rs │ ├── mod.rs │ ├── nonlinear_shape_cast │ │ ├── mod.rs │ │ ├── nonlinear_rigid_motion.rs │ │ ├── nonlinear_shape_cast.rs │ │ ├── nonlinear_shape_cast_composite_shape_shape.rs │ │ ├── nonlinear_shape_cast_halfspace_support_map.rs │ │ ├── nonlinear_shape_cast_support_map_support_map.rs │ │ └── nonlinear_shape_cast_voxels_shape.rs │ ├── point │ │ ├── mod.rs │ │ ├── point_aabb.rs │ │ ├── point_ball.rs │ │ ├── point_bounding_sphere.rs │ │ ├── point_capsule.rs │ │ ├── point_composite_shape.rs │ │ ├── point_cone.rs │ │ ├── point_cuboid.rs │ │ ├── point_cylinder.rs │ │ ├── point_halfspace.rs │ │ ├── point_heightfield.rs │ │ ├── point_query.rs │ │ ├── point_round_shape.rs │ │ ├── point_segment.rs │ │ ├── point_support_map.rs │ │ ├── point_tetrahedron.rs │ │ ├── point_triangle.rs │ │ └── point_voxels.rs │ ├── query_dispatcher.rs │ ├── ray │ │ ├── mod.rs │ │ ├── ray.rs │ │ ├── ray_aabb.rs │ │ ├── ray_ball.rs │ │ ├── ray_bounding_sphere.rs │ │ ├── ray_composite_shape.rs │ │ ├── ray_cuboid.rs │ │ ├── ray_halfspace.rs │ │ ├── ray_heightfield.rs │ │ ├── ray_round_shape.rs │ │ ├── ray_support_map.rs │ │ ├── ray_triangle.rs │ │ ├── ray_trimesh.rs │ │ ├── ray_voxels.rs │ │ └── simd_ray.rs │ ├── sat │ │ ├── mod.rs │ │ ├── sat_cuboid_cuboid.rs │ │ ├── sat_cuboid_point.rs │ │ ├── sat_cuboid_segment.rs │ │ ├── sat_cuboid_support_map.rs │ │ ├── sat_cuboid_triangle.rs │ │ ├── sat_support_map_support_map.rs │ │ └── sat_triangle_segment.rs │ ├── shape_cast │ │ ├── mod.rs │ │ ├── shape_cast.rs │ │ ├── shape_cast_ball_ball.rs │ │ ├── shape_cast_composite_shape_shape.rs │ │ ├── shape_cast_halfspace_support_map.rs │ │ ├── shape_cast_heightfield_shape.rs │ │ ├── shape_cast_support_map_support_map.rs │ │ └── shape_cast_voxels_shape.rs │ ├── split │ │ ├── mod.rs │ │ ├── split.rs │ │ ├── split_aabb.rs │ │ ├── split_segment.rs │ │ └── split_trimesh.rs │ └── visitors │ │ ├── aabb_sets_interferences_collector.rs │ │ ├── bounding_volume_intersections_simultaneous_visitor.rs │ │ ├── bounding_volume_intersections_visitor.rs │ │ ├── composite_closest_point_visitor.rs │ │ ├── composite_point_containment_test.rs │ │ ├── mod.rs │ │ ├── point_intersections_visitor.rs │ │ └── ray_intersections_visitor.rs ├── shape │ ├── ball.rs │ ├── capsule.rs │ ├── composite_shape.rs │ ├── compound.rs │ ├── cone.rs │ ├── convex_polygon.rs │ ├── convex_polyhedron.rs │ ├── cuboid.rs │ ├── cylinder.rs │ ├── feature_id.rs │ ├── half_space.rs │ ├── heightfield2.rs │ ├── heightfield3.rs │ ├── mod.rs │ ├── polygon.rs │ ├── polygonal_feature2d.rs │ ├── polygonal_feature3d.rs │ ├── polygonal_feature_map.rs │ ├── polyline.rs │ ├── round_shape.rs │ ├── segment.rs │ ├── shape.rs │ ├── shared_shape.rs │ ├── support_map.rs │ ├── tetrahedron.rs │ ├── triangle.rs │ ├── triangle_pseudo_normals.rs │ ├── trimesh.rs │ └── voxels.rs ├── transformation │ ├── convex_hull2.rs │ ├── convex_hull3 │ │ ├── convex_hull.rs │ │ ├── error.rs │ │ ├── initial_mesh.rs │ │ ├── mod.rs │ │ ├── triangle_facet.rs │ │ └── validation.rs │ ├── convex_hull_utils.rs │ ├── ear_clipping.rs │ ├── hertel_mehlhorn.rs │ ├── mesh_intersection │ │ ├── mesh_intersection.rs │ │ ├── mesh_intersection_error.rs │ │ ├── mod.rs │ │ └── triangle_triangle_intersection.rs │ ├── mod.rs │ ├── polygon_intersection.rs │ ├── to_outline │ │ ├── ball_to_outline.rs │ │ ├── capsule_to_outline.rs │ │ ├── cone_to_outline.rs │ │ ├── convex_polyhedron_to_outline.rs │ │ ├── cuboid_to_outline.rs │ │ ├── cylinder_to_outline.rs │ │ ├── heightfield_to_outline.rs │ │ ├── mod.rs │ │ ├── round_cone_to_outline.rs │ │ ├── round_convex_polyhedron_to_outline.rs │ │ ├── round_cuboid_to_outline.rs │ │ ├── round_cylinder_to_outline.rs │ │ ├── round_triangle_to_outline.rs │ │ └── voxels_to_outline.rs │ ├── to_polyline │ │ ├── ball_to_polyline.rs │ │ ├── capsule_to_polyline.rs │ │ ├── cuboid_to_polyline.rs │ │ ├── heightfield_to_polyline.rs │ │ ├── mod.rs │ │ ├── round_convex_polygon_to_polyline.rs │ │ ├── round_cuboid_to_polyline.rs │ │ └── voxels_to_polyline.rs │ ├── to_trimesh │ │ ├── ball_to_trimesh.rs │ │ ├── capsule_to_trimesh.rs │ │ ├── cone_to_trimesh.rs │ │ ├── convex_polyhedron_to_trimesh.rs │ │ ├── cuboid_to_trimesh.rs │ │ ├── cylinder_to_trimesh.rs │ │ ├── heightfield_to_trimesh.rs │ │ ├── mod.rs │ │ └── voxels_to_trimesh.rs │ ├── utils.rs │ ├── vhacd │ │ ├── mod.rs │ │ ├── parameters.rs │ │ └── vhacd.rs │ ├── voxelization │ │ ├── mod.rs │ │ ├── voxel_set.rs │ │ └── voxelized_volume.rs │ └── wavefront.rs └── utils │ ├── as_bytes.rs │ ├── ccw_face_normal.rs │ ├── center.rs │ ├── cleanup.rs │ ├── consts.rs │ ├── cov.rs │ ├── deterministic_state.rs │ ├── fx_hasher.rs │ ├── hashable_partial_eq.rs │ ├── hashmap.rs │ ├── hashset.rs │ ├── interval.rs │ ├── inv.rs │ ├── isometry_ops.rs │ ├── median.rs │ ├── mod.rs │ ├── obb.rs │ ├── point_cloud_support_point.rs │ ├── point_in_poly2d.rs │ ├── point_in_triangle.rs │ ├── sdp_matrix.rs │ ├── segments_intersection.rs │ ├── sort.rs │ ├── sorted_pair.rs │ ├── spade.rs │ ├── weighted_value.rs │ ├── wops.rs │ └── z_order.rs └── write_examples.sh /.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 | BENCH 5 | lib 6 | target 7 | private 8 | Cargo.lock 9 | Makefile 10 | .vscode 11 | .idea -------------------------------------------------------------------------------- /ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | ## Repository architecture 2 | 3 | The architecture of this repository is a bit unusual because we are using some tricks to have both 4 | the 2D and 3D version of Parry share the same code-base. Here are the main folders: 5 | - **`crates/`**: contains one folder per Parry crate (for the 2D, 3D, `f32`, and `f64` versions). Each 6 | crate has its own `Cargo.toml` file that adjusts some cargo features, and reference the `src` folder. 7 | - **`src/`**: contains the actual `.rs` source code of the Parry geometric library. 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Parry 2 | 3 | Thank you for wanting to contribute! Contribution can take many forms, including: 4 | - Reporting a bug. 5 | - Submitting a fix. 6 | - Fixing typos. 7 | - Improving the docs. 8 | - [Donations on GitHub Sponsors](https://github.com/sponsors/dimforge). 9 | 10 | It is strongly recommended to [open an issue](https://github.com/dimforge/parry/issues) or to discuss 11 | with us [on Discord][discord] before fixing complicated issues, or implementing new 12 | features. 13 | 14 | 15 | ## Contributing to the Rust code 16 | The Rust source code of the Parry geometric library is available on our `parry` repository 17 | [on GitHub](https://github.com/dimforge/parry.rs). 18 | 19 | 1. Fork our `parry` repository [on GitHub](https://github.com/dimforge/parry). 20 | 2. Clone the repository and make the necessary changes. 21 | 3. In order to debug your changes and check that it works, do the following: 22 | - Run the tests `cargo test` 23 | 4. Once you are satisfied with your changes, submit them by [opening a Pull Request](https://github.com/dimforge/parry/pulls) on GitHub. 24 | 5. If that Pull Request does something you need urgently, or if you think it has been forgotten, don't hesitate 25 | to ask **@sebcrozet** directly [on Discord][discord] for a review. 26 | 6. Iterate with the reviewer until the PR gets merged. 27 | 28 | 29 | [discord]: https://discord.gg/vt9DJSW -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "crates/parry2d", 4 | "crates/parry3d", 5 | "crates/parry2d-f64", 6 | "crates/parry3d-f64", 7 | ] 8 | resolver = "2" 9 | 10 | [workspace.lints] 11 | rust.unexpected_cfgs = { level = "warn", check-cfg = [ 12 | 'cfg(feature, values("dim2", "dim3", "f32", "f64"))', 13 | # "wavefront" is only used for 3D crates. 14 | 'cfg(feature, values("wavefront"))', 15 | ] } 16 | 17 | [workspace.lints.clippy] 18 | alloc_instead_of_core = "warn" 19 | std_instead_of_alloc = "warn" 20 | std_instead_of_core = "warn" 21 | 22 | [patch.crates-io] 23 | parry2d = { path = "crates/parry2d" } 24 | parry3d = { path = "crates/parry3d" } 25 | parry2d-f64 = { path = "crates/parry2d-f64" } 26 | parry3d-f64 = { path = "crates/parry3d-f64" } 27 | 28 | #simba = { path = "../simba" } 29 | #simba = { git = "https://github.com/dimforge/simba", rev = "45a5266eb36ed9d25907e9bf9130cd4ac846a748" } 30 | #nalgebra = { git = "https://github.com/dimforge/nalgebra", rev = "0cf79aef0e6155befc3279a3145f1940822b8377" } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | crates.io 3 |

4 |

5 | 6 | 7 | 8 | 9 | crates.io 10 | 11 | 12 | crates.io 13 | 14 |

15 | 16 | ----- 17 | 18 |

19 | 20 | 2D Documentation | 3D Documentation | User Guide 21 | 22 |

23 | 24 | ----- 25 | 26 | ## What is Parry? 27 | 28 | **Parry** is a set 2 and 3-dimensional geometric and collision detection libraries. 29 | These libraries are `parry2d`, `parry3d`, `parry2d-f64`, and `parry3d-f64`. They are written with the Rust 30 | programming language, by the [Dimforge](https://dimforge.com) organization. It is forever free 31 | and open-source! We regularly give updates about our progress on [our blog](https://www.dimforge.com/blog). 32 | 33 | ## Resources and discussions 34 | - [Dimforge](https://dimforge.com): See all the open-source projects we are working on! Follow our announcements 35 | on our [blog](https://www.dimforge.com/blog). 36 | - [User guide](https://www.parry.rs/docs/): (WIP) Learn to use Parry in your project by reading the official User Guide. 37 | - [Discord](https://discord.gg/vt9DJSW): Come chat with us, get help, suggest features, on Discord! 38 | -------------------------------------------------------------------------------- /assets/tests/stairs.obj: -------------------------------------------------------------------------------- 1 | # Author: Camilo Talero 2 | # License: CC0 3 | v -1.000000 -1.000000 1.000000 4 | v -1.000000 1.000000 1.000000 5 | v -1.000000 -1.000000 -1.000000 6 | v -1.000000 1.000000 -1.000000 7 | v 1.000000 -1.000000 1.000000 8 | v 1.000000 1.000000 1.000000 9 | v 1.000000 -1.000000 -1.000000 10 | v 1.000000 1.000000 -1.000000 11 | v -1.000000 -1.000000 4.915915 12 | v -1.000000 1.000000 4.915915 13 | v 1.000000 1.000000 4.915915 14 | v 1.000000 -1.000000 4.915915 15 | v -1.000000 4.391501 1.000000 16 | v -1.000000 4.391501 -1.000000 17 | v 1.000000 4.391501 -1.000000 18 | v 1.000000 4.391501 1.000000 19 | v -1.000000 1.000000 -7.119346 20 | v 1.000000 1.000000 -7.119346 21 | v -1.000000 4.391501 -7.119346 22 | v 1.000000 4.391501 -7.119346 23 | v -1.000000 11.844163 -1.000000 24 | v 1.000000 11.844163 -1.000000 25 | v -1.000000 11.844163 -7.119346 26 | v 1.000000 11.844163 -7.119346 27 | s off 28 | f 2 3 1 29 | f 4 7 3 30 | f 8 5 7 31 | f 2 11 6 32 | f 7 1 3 33 | f 2 14 4 34 | f 11 9 12 35 | f 6 12 5 36 | f 1 10 2 37 | f 5 9 1 38 | f 14 16 15 39 | f 6 13 2 40 | f 4 18 8 41 | f 8 16 6 42 | f 17 20 18 43 | f 8 20 15 44 | f 14 17 4 45 | f 14 23 19 46 | f 22 23 21 47 | f 19 24 20 48 | f 20 22 15 49 | f 15 21 14 50 | f 2 4 3 51 | f 4 8 7 52 | f 8 6 5 53 | f 2 10 11 54 | f 7 5 1 55 | f 2 13 14 56 | f 11 10 9 57 | f 6 11 12 58 | f 1 9 10 59 | f 5 12 9 60 | f 14 13 16 61 | f 6 16 13 62 | f 4 17 18 63 | f 8 15 16 64 | f 17 19 20 65 | f 8 18 20 66 | f 14 19 17 67 | f 14 21 23 68 | f 22 24 23 69 | f 19 23 24 70 | f 20 24 22 71 | f 15 22 21 72 | -------------------------------------------------------------------------------- /crates/parry2d/examples/ball2d.rs: -------------------------------------------------------------------------------- 1 | use parry2d::shape::Ball; 2 | 3 | fn main() { 4 | let ball = Ball::new(1.0); 5 | assert!(ball.radius == 1.0); 6 | } 7 | -------------------------------------------------------------------------------- /crates/parry2d/examples/contact_query2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry2, Vector2}; 4 | use parry2d::query; 5 | use parry2d::shape::{Ball, Cuboid}; 6 | 7 | fn main() { 8 | let cuboid = Cuboid::new(Vector2::new(1.0, 1.0)); 9 | let ball = Ball::new(1.0); 10 | let prediction = 1.0; 11 | 12 | let cuboid_pos = Isometry2::identity(); 13 | let ball_pos_penetrating = Isometry2::translation(1.0, 1.0); 14 | let ball_pos_in_prediction = Isometry2::translation(2.0, 2.0); 15 | let ball_pos_too_far = Isometry2::translation(3.0, 3.0); 16 | 17 | let ctct_penetrating = query::contact( 18 | &ball_pos_penetrating, 19 | &ball, 20 | &cuboid_pos, 21 | &cuboid, 22 | prediction, 23 | ) 24 | .unwrap(); 25 | let ctct_in_prediction = query::contact( 26 | &ball_pos_in_prediction, 27 | &ball, 28 | &cuboid_pos, 29 | &cuboid, 30 | prediction, 31 | ) 32 | .unwrap(); 33 | let ctct_too_far = 34 | query::contact(&ball_pos_too_far, &ball, &cuboid_pos, &cuboid, prediction).unwrap(); 35 | 36 | assert!(ctct_penetrating.unwrap().dist <= 0.0); 37 | assert!(ctct_in_prediction.unwrap().dist >= 0.0); 38 | assert_eq!(ctct_too_far, None); 39 | } 40 | -------------------------------------------------------------------------------- /crates/parry2d/examples/convex2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | extern crate num_traits as num; 3 | 4 | use na::Point2; 5 | use parry2d::shape::ConvexPolygon; 6 | 7 | fn main() { 8 | let points = [ 9 | Point2::new(-1.0, 1.0), 10 | Point2::new(-0.5, -0.5), 11 | Point2::new(0.0, 0.5), 12 | Point2::new(0.5, -0.5), 13 | Point2::new(1.0, 1.0), 14 | ]; 15 | 16 | let convex = ConvexPolygon::from_convex_hull(&points).expect("Invalid convex polygon."); 17 | assert!(convex.points().len() == 4); 18 | } 19 | -------------------------------------------------------------------------------- /crates/parry2d/examples/convex_hull2d.rs: -------------------------------------------------------------------------------- 1 | mod common_macroquad2d; 2 | 3 | use core::f32::consts::{FRAC_PI_2, FRAC_PI_4}; 4 | 5 | use common_macroquad2d::{draw_point, draw_polygon, lissajous_2d_with_params, na_from_mquad}; 6 | use macroquad::prelude::*; 7 | use nalgebra::Point2; 8 | use parry2d::transformation; 9 | 10 | const RENDER_SCALE: f32 = 30.0; 11 | 12 | #[macroquad::main("convex_hull2d")] 13 | async fn main() { 14 | let count = 9; 15 | let mut pts = vec![Point2::default(); count]; 16 | 17 | let render_pos = Point2::new(300.0, 300.0); 18 | 19 | loop { 20 | let elapsed_time = get_time() as f32; 21 | let elapsed_time_slow = elapsed_time * 0.2; 22 | clear_background(BLACK); 23 | 24 | for (i, pt) in pts.iter_mut().enumerate() { 25 | *pt = na_from_mquad(lissajous_2d_with_params( 26 | (i * i) as f32 + elapsed_time_slow, 27 | 2.0 + i as f32 / 3.0, 28 | (i as f32 / count as f32) + elapsed_time_slow.cos() * 0.1, 29 | (elapsed_time_slow as f32 + i as f32).cos() * 0.1 + FRAC_PI_2, 30 | FRAC_PI_4, 31 | )) * 5f32; 32 | draw_point(*pt, RENDER_SCALE, render_pos, RED); 33 | } 34 | 35 | /* 36 | * 37 | * Compute the convex hull. 38 | * 39 | */ 40 | let convex_hull = transformation::convex_hull(&pts); 41 | draw_polygon(&convex_hull, RENDER_SCALE, render_pos, WHITE); 42 | next_frame().await 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/parry2d/examples/convex_try_new2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Point2; 4 | use parry2d::shape::ConvexPolygon; 5 | 6 | fn main() { 7 | let points = vec![ 8 | Point2::new(-1.0, 1.0), 9 | Point2::new(-0.5, -0.5), 10 | Point2::new(0.5, -0.5), 11 | Point2::new(1.0, 1.0), 12 | ]; 13 | 14 | let convex = ConvexPolygon::from_convex_polyline(points).expect("Invalid convex polygon."); 15 | assert!(convex.points().len() == 4); 16 | } 17 | -------------------------------------------------------------------------------- /crates/parry2d/examples/cuboid2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Vector2; 4 | use parry2d::shape::Cuboid; 5 | 6 | fn main() { 7 | let cuboid = Cuboid::new(Vector2::new(2.0, 1.0)); 8 | 9 | assert!(cuboid.half_extents.x == 2.0); 10 | assert!(cuboid.half_extents.y == 1.0); 11 | } 12 | -------------------------------------------------------------------------------- /crates/parry2d/examples/distance_query2d.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate approx; // for relative_eq! 3 | extern crate nalgebra as na; 4 | 5 | use na::{Isometry2, Vector2}; 6 | use parry2d::query; 7 | use parry2d::shape::{Ball, Cuboid}; 8 | 9 | fn main() { 10 | let cuboid = Cuboid::new(Vector2::new(1.0, 1.0)); 11 | let ball = Ball::new(1.0); 12 | 13 | let cuboid_pos = Isometry2::identity(); 14 | let ball_pos_intersecting = Isometry2::translation(0.0, 1.0); 15 | let ball_pos_disjoint = Isometry2::translation(0.0, 3.0); 16 | 17 | let dist_intersecting = 18 | query::distance(&ball_pos_intersecting, &ball, &cuboid_pos, &cuboid).unwrap(); 19 | let dist_disjoint = query::distance(&ball_pos_disjoint, &ball, &cuboid_pos, &cuboid).unwrap(); 20 | 21 | assert_eq!(dist_intersecting, 0.0); 22 | assert!(relative_eq!(dist_disjoint, 1.0, epsilon = 1.0e-7)); 23 | } 24 | -------------------------------------------------------------------------------- /crates/parry2d/examples/plane2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Vector2; 4 | use parry2d::shape::HalfSpace; 5 | 6 | fn main() { 7 | let halfspace = HalfSpace::new(Vector2::::y_axis()); 8 | 9 | assert!(halfspace.normal.x == 0.0); 10 | assert!(halfspace.normal.y == 1.0); 11 | } 12 | -------------------------------------------------------------------------------- /crates/parry2d/examples/polyline2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Point2; 4 | use parry2d::shape::Polyline; 5 | 6 | fn main() { 7 | let points = vec![ 8 | Point2::new(0.0, 1.0), 9 | Point2::new(-1.0, -1.0), 10 | Point2::new(0.0, -0.5), 11 | Point2::new(1.0, -1.0), 12 | ]; 13 | 14 | let indices = vec![ 15 | [0, 1], 16 | [1, 2], 17 | [2, 3], 18 | [3, 0], // This forms a loop. 19 | ]; 20 | 21 | // Build the polyline. 22 | let polyline = Polyline::new(points, Some(indices)); 23 | 24 | assert_eq!(polyline.vertices().len(), 4); 25 | } 26 | -------------------------------------------------------------------------------- /crates/parry2d/examples/proximity_query2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry2, Vector2}; 4 | use parry2d::query; 5 | use parry2d::shape::{Ball, Cuboid}; 6 | 7 | fn main() { 8 | let cuboid = Cuboid::new(Vector2::new(1.0, 1.0)); 9 | let ball = Ball::new(1.0); 10 | 11 | let cuboid_pos = Isometry2::identity(); 12 | let ball_pos_intersecting = Isometry2::translation(1.0, 1.0); 13 | let ball_pos_disjoint = Isometry2::translation(3.0, 3.0); 14 | 15 | assert!(query::intersection_test(&ball_pos_intersecting, &ball, &cuboid_pos, &cuboid).unwrap()); 16 | assert!(!query::intersection_test(&ball_pos_disjoint, &ball, &cuboid_pos, &cuboid).unwrap()); 17 | } 18 | -------------------------------------------------------------------------------- /crates/parry2d/examples/solid_point_query2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry2, Point2, Vector2}; 4 | use parry2d::query::PointQuery; 5 | use parry2d::shape::Cuboid; 6 | 7 | fn main() { 8 | let cuboid = Cuboid::new(Vector2::new(1.0, 2.0)); 9 | let pt_inside = Point2::origin(); 10 | let pt_outside = Point2::new(2.0, 2.0); 11 | 12 | // Solid projection. 13 | assert_eq!( 14 | cuboid.distance_to_point(&Isometry2::identity(), &pt_inside, true), 15 | 0.0 16 | ); 17 | 18 | // Non-solid projection. 19 | assert_eq!( 20 | cuboid.distance_to_point(&Isometry2::identity(), &pt_inside, false), 21 | -1.0 22 | ); 23 | 24 | // The other point is outside of the cuboid so the `solid` flag has no effect. 25 | assert_eq!( 26 | cuboid.distance_to_point(&Isometry2::identity(), &pt_outside, false), 27 | 1.0 28 | ); 29 | assert_eq!( 30 | cuboid.distance_to_point(&Isometry2::identity(), &pt_outside, true), 31 | 1.0 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /crates/parry2d/examples/solid_ray_cast2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry2, Point2, Vector2}; 4 | use parry2d::query::{Ray, RayCast}; 5 | use parry2d::shape::Cuboid; 6 | 7 | fn main() { 8 | let cuboid = Cuboid::new(Vector2::new(1.0, 2.0)); 9 | let ray_inside = Ray::new(Point2::origin(), Vector2::y()); 10 | let ray_miss = Ray::new(Point2::new(2.0, 2.0), Vector2::new(1.0, 1.0)); 11 | 12 | // Solid cast. 13 | assert_eq!( 14 | cuboid 15 | .cast_ray(&Isometry2::identity(), &ray_inside, f32::MAX, true) 16 | .unwrap(), 17 | 0.0 18 | ); 19 | 20 | // Non-solid cast. 21 | assert_eq!( 22 | cuboid 23 | .cast_ray(&Isometry2::identity(), &ray_inside, f32::MAX, false) 24 | .unwrap(), 25 | 2.0 26 | ); 27 | 28 | // The other ray does not intersect this shape. 29 | assert!(cuboid 30 | .cast_ray(&Isometry2::identity(), &ray_miss, f32::MAX, false) 31 | .is_none()); 32 | assert!(cuboid 33 | .cast_ray(&Isometry2::identity(), &ray_miss, f32::MAX, true) 34 | .is_none()); 35 | } 36 | -------------------------------------------------------------------------------- /crates/parry2d/examples/time_of_impact_query2d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry2, Vector2}; 4 | use parry2d::query; 5 | use parry2d::query::ShapeCastOptions; 6 | use parry2d::shape::{Ball, Cuboid}; 7 | 8 | fn main() { 9 | let cuboid = Cuboid::new(Vector2::new(1.0, 1.0)); 10 | let ball = Ball::new(1.0); 11 | 12 | let cuboid_pos = Isometry2::identity(); 13 | let ball_pos_intersecting = Isometry2::translation(1.0, 1.0); 14 | let ball_pos_will_touch = Isometry2::translation(2.0, 2.0); 15 | let ball_pos_wont_touch = Isometry2::translation(3.0, 3.0); 16 | 17 | let box_vel1 = Vector2::new(-1.0, 1.0); 18 | let box_vel2 = Vector2::new(1.0, 1.0); 19 | 20 | let ball_vel1 = Vector2::new(2.0, 2.0); 21 | let ball_vel2 = Vector2::new(-0.5, -0.5); 22 | 23 | let toi_intersecting = query::cast_shapes( 24 | &ball_pos_intersecting, 25 | &ball_vel1, 26 | &ball, 27 | &cuboid_pos, 28 | &box_vel1, 29 | &cuboid, 30 | ShapeCastOptions::default(), 31 | ) 32 | .unwrap(); 33 | let toi_will_touch = query::cast_shapes( 34 | &ball_pos_will_touch, 35 | &ball_vel2, 36 | &ball, 37 | &cuboid_pos, 38 | &box_vel2, 39 | &cuboid, 40 | ShapeCastOptions::default(), 41 | ) 42 | .unwrap(); 43 | let toi_wont_touch = query::cast_shapes( 44 | &ball_pos_wont_touch, 45 | &ball_vel1, 46 | &ball, 47 | &cuboid_pos, 48 | &box_vel1, 49 | &cuboid, 50 | ShapeCastOptions::default(), 51 | ) 52 | .unwrap(); 53 | 54 | assert_eq!(toi_intersecting.map(|hit| hit.time_of_impact), Some(0.0)); 55 | println!("Toi: {:?}", toi_will_touch); 56 | assert!(toi_will_touch.is_some() && toi_will_touch.unwrap().time_of_impact > 0.0); 57 | assert_eq!(toi_wont_touch.map(|hit| hit.time_of_impact), None); 58 | } 59 | -------------------------------------------------------------------------------- /crates/parry2d/tests/geometry/aabb_scale.rs: -------------------------------------------------------------------------------- 1 | use na::{Point2, Vector2}; 2 | use parry2d::bounding_volume::Aabb; 3 | 4 | #[test] 5 | fn test_aabb_scale_wrt_center() { 6 | let aabb = Aabb::from_half_extents(Point2::new(1.0, 2.0), Vector2::new(4.0, 5.0)); 7 | let scale = Vector2::new(10.0, -20.0); 8 | let scaled_aabb = aabb.scaled_wrt_center(&scale); 9 | let scaled_aabb_neg = aabb.scaled_wrt_center(&-scale); 10 | let scaled_aabb_abs = aabb.scaled_wrt_center(&scale.abs()); 11 | 12 | assert_eq!(&scaled_aabb, &scaled_aabb_neg); 13 | assert_eq!(&scaled_aabb, &scaled_aabb_abs); 14 | assert_eq!(aabb.center(), scaled_aabb.center()); 15 | assert_eq!(scaled_aabb.half_extents(), Vector2::new(40.0, 100.0)); 16 | } 17 | -------------------------------------------------------------------------------- /crates/parry2d/tests/geometry/ball_ball_toi.rs: -------------------------------------------------------------------------------- 1 | // Issue #35 2 | 3 | use na::{self, Isometry2, Vector2}; 4 | use parry2d::query; 5 | use parry2d::query::details::ShapeCastOptions; 6 | use parry2d::shape::Ball; 7 | 8 | #[test] 9 | fn test_ball_ball_toi() { 10 | let b = Ball::new(0.5); 11 | let m1 = Isometry2::identity(); 12 | let m2 = Isometry2::translation(0.0, 10.0); 13 | let v1 = Vector2::new(0.0, 10.0); 14 | let v2 = Vector2::zeros(); 15 | 16 | let cast = query::cast_shapes(&m1, &v1, &b, &m2, &v2, &b, ShapeCastOptions::default()).unwrap(); 17 | 18 | assert_eq!(cast.unwrap().time_of_impact, 0.9); 19 | } 20 | -------------------------------------------------------------------------------- /crates/parry2d/tests/geometry/ball_cuboid_contact.rs: -------------------------------------------------------------------------------- 1 | use nalgebra::{Isometry2, Vector2}; 2 | use parry2d::query; 3 | use parry2d::shape::{Ball, Cuboid}; 4 | #[cfg(feature = "improved_fixed_point_support")] 5 | use simba::scalar::FixedI40F24; 6 | 7 | #[test] 8 | fn test_ball_cuboid_query_contact() { 9 | let cuboid = Cuboid::new(Vector2::new(0.5, 0.5)); 10 | let cuboid_pos = Isometry2::translation(0.0, 4.0); 11 | let ball = Ball::new(0.5); 12 | let ball_pos = Isometry2::translation(0.0517938, 3.05178815); 13 | let ct = query::contact(&cuboid_pos, &cuboid, &ball_pos, &ball, 0.0).unwrap(); 14 | assert!(ct.is_some()); 15 | let ct = query::contact(&ball_pos, &ball, &cuboid_pos, &cuboid, 0.0).unwrap(); 16 | assert!(ct.is_some()); 17 | } 18 | 19 | #[test] 20 | fn test_false_negative() { 21 | let contact = query::contact( 22 | &Isometry2::translation(1.0, 1.0), 23 | &Ball::new(1.0), 24 | &Isometry2::identity(), 25 | &Cuboid::new(Vector2::new(1.0, 1.0)), 26 | 1.0, 27 | ) 28 | .unwrap() 29 | .unwrap(); 30 | 31 | assert!(contact.dist < 0.0); 32 | } 33 | -------------------------------------------------------------------------------- /crates/parry2d/tests/geometry/epa_convergence.rs: -------------------------------------------------------------------------------- 1 | use na::Vector2; 2 | use parry2d::{ 3 | math::{Isometry, Point, Real}, 4 | query, 5 | shape::{Capsule, ConvexPolygon, SharedShape}, 6 | }; 7 | 8 | /// Original issue: https://github.com/dimforge/parry/issues/205 9 | #[test] 10 | fn capsule_convergence() { 11 | let shape1 = Capsule::new_y(5.0, 10.0); 12 | let mut vec = Vec::>::with_capacity(3); 13 | vec.push(Point::::new(64.0, 507.0)); 14 | vec.push(Point::::new(440.0, 326.0)); 15 | vec.push(Point::::new(1072.0, 507.0)); 16 | let shape2 = ConvexPolygon::from_convex_polyline(vec); 17 | let shape2 = shape2.unwrap(); 18 | let transform1 = Isometry::new(Vector2::new(381.592, 348.491), 0.0); 19 | let transform2 = Isometry::new(Vector2::new(0.0, 0.0), 0.0); 20 | 21 | let _res = query::details::contact_support_map_support_map( 22 | &transform1.inv_mul(&transform2), 23 | &shape1, 24 | &shape2, 25 | 10.0, 26 | ) 27 | .expect("Penetration not found."); 28 | let shared_shape1 = SharedShape::new(shape1); 29 | let shared_shape2 = SharedShape::new(shape2); 30 | 31 | if let Ok(Some(_contact)) = query::contact( 32 | &transform1, 33 | shared_shape1.as_ref(), 34 | &transform2, 35 | shared_shape2.as_ref(), 36 | 1.0, 37 | ) { 38 | println!("collision"); 39 | } else { 40 | panic!("no collision"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/parry2d/tests/geometry/mod.rs: -------------------------------------------------------------------------------- 1 | mod aabb_scale; 2 | mod ball_ball_toi; 3 | mod ball_cuboid_contact; 4 | mod epa2; 5 | mod epa_convergence; 6 | mod ray_cast; 7 | mod time_of_impact2; 8 | -------------------------------------------------------------------------------- /crates/parry2d/tests/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate approx; 3 | extern crate nalgebra as na; 4 | extern crate parry2d; 5 | 6 | mod geometry; 7 | mod query; 8 | -------------------------------------------------------------------------------- /crates/parry2d/tests/query/mod.rs: -------------------------------------------------------------------------------- 1 | mod point_composite_shape; 2 | mod point_triangle; 3 | -------------------------------------------------------------------------------- /crates/parry2d/tests/query/point_triangle.rs: -------------------------------------------------------------------------------- 1 | use parry2d::{math::Point, query::PointQuery, shape::Triangle}; 2 | 3 | #[test] 4 | fn project_local_point_point_on_ab() { 5 | let verts = [ 6 | Point::new(2.0, 1.0), 7 | Point::new(0.0, 1.0), 8 | Point::new(1.0, 0.0), 9 | ]; 10 | let tri1 = Triangle::new(verts[0], verts[1], verts[2]); 11 | let tri2 = Triangle::new(verts[2], verts[0], verts[1]); 12 | 13 | let query_pt = Point::new(1.4, 1.0); 14 | 15 | let proj1 = tri1.project_local_point(&query_pt, false); // Used to fail on 0.14 and earlier 16 | let proj2 = tri2.project_local_point(&query_pt, false); 17 | 18 | assert_eq!(proj1.point, proj2.point); 19 | assert_eq!(proj1.point, query_pt); 20 | } 21 | -------------------------------------------------------------------------------- /crates/parry3d/benches/all.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![allow(unused_macros)] 3 | 4 | extern crate nalgebra as na; 5 | extern crate parry3d; 6 | extern crate rand; 7 | extern crate test; 8 | 9 | mod bounding_volume; 10 | mod common; 11 | mod query; 12 | mod support_map; 13 | -------------------------------------------------------------------------------- /crates/parry3d/benches/common/generators.rs: -------------------------------------------------------------------------------- 1 | use na::Point3; 2 | use parry3d::shape::TriMesh; 3 | use rand::Rng; 4 | 5 | pub fn generate_trimesh_around_origin(rng: &mut R) -> TriMesh { 6 | let pts = (0..3000).map(|_| rng.gen::>() * 3.0).collect(); 7 | let indices = (0..1000).map(|i| [i * 3, i * 3 + 1, i * 3 + 2]).collect(); 8 | 9 | TriMesh::new(pts, indices).unwrap() 10 | } 11 | -------------------------------------------------------------------------------- /crates/parry3d/benches/common/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::default_gen::generate; 2 | pub use self::generators::generate_trimesh_around_origin; 3 | pub use self::unref::unref; 4 | 5 | mod default_gen; 6 | mod generators; 7 | mod unref; 8 | -------------------------------------------------------------------------------- /crates/parry3d/benches/common/unref.rs: -------------------------------------------------------------------------------- 1 | pub trait Unref { 2 | fn unref(a: Self) -> T; 3 | } 4 | 5 | impl<'a> Unref for &'a f32 { 6 | #[inline(always)] 7 | fn unref(a: &f32) -> f32 { 8 | *a 9 | } 10 | } 11 | 12 | impl<'a> Unref for &'a f64 { 13 | #[inline(always)] 14 | fn unref(a: &f64) -> f64 { 15 | *a 16 | } 17 | } 18 | 19 | impl<'a> Unref for &'a bool { 20 | #[inline(always)] 21 | fn unref(a: &bool) -> bool { 22 | *a 23 | } 24 | } 25 | 26 | impl<'a, T> Unref<&'a T> for &'a T { 27 | #[inline(always)] 28 | fn unref(a: &'a T) -> &'a T { 29 | a 30 | } 31 | } 32 | 33 | #[inline(always)] 34 | pub fn unref, O>(val: T) -> O { 35 | Unref::unref(val) 36 | } 37 | -------------------------------------------------------------------------------- /crates/parry3d/benches/query/algorithm.rs: -------------------------------------------------------------------------------- 1 | use na::Point3; 2 | use parry3d::query::gjk::{CSOPoint, VoronoiSimplex}; 3 | use test::Bencher; 4 | 5 | #[bench] 6 | fn bench_johnson_simplex(bh: &mut Bencher) { 7 | let a = CSOPoint::single_point(Point3::new(-0.5f32, -0.5, -0.5)); 8 | let b = CSOPoint::single_point(Point3::new(0.0, 0.5, 0.0)); 9 | let c = CSOPoint::single_point(Point3::new(0.5, -0.5, -0.5)); 10 | let d = CSOPoint::single_point(Point3::new(0.0, -0.5, -0.5)); 11 | 12 | bh.iter(|| { 13 | let mut spl = VoronoiSimplex::new(); 14 | 15 | spl.reset(a); 16 | 17 | spl.add_point(b); 18 | spl.add_point(c); 19 | spl.add_point(d); 20 | 21 | test::black_box(spl.project_origin_and_reduce()); 22 | }) 23 | } 24 | 25 | #[bench] 26 | fn bench_johnson_simplex_tls(bh: &mut Bencher) { 27 | let a = CSOPoint::single_point(Point3::new(-0.5f32, -0.5, -0.5)); 28 | let b = CSOPoint::single_point(Point3::new(0.0, 0.5, 0.0)); 29 | let c = CSOPoint::single_point(Point3::new(0.5, -0.5, -0.5)); 30 | let d = CSOPoint::single_point(Point3::new(0.0, -0.5, -0.5)); 31 | 32 | bh.iter(|| { 33 | let mut spl = VoronoiSimplex::new(); 34 | 35 | spl.reset(a); 36 | 37 | spl.add_point(b); 38 | spl.add_point(c); 39 | spl.add_point(d); 40 | 41 | test::black_box(spl.project_origin_and_reduce()); 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /crates/parry3d/benches/query/contacts.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{generate, unref}; 2 | use na::Isometry3; 3 | use parry3d::query; 4 | use parry3d::shape::{Ball, Capsule, Cone, Cuboid, Cylinder}; 5 | use rand::SeedableRng; 6 | use rand_isaac::IsaacRng; 7 | use test::Bencher; 8 | 9 | #[path = "../common/macros.rs"] 10 | #[macro_use] 11 | mod macros; 12 | 13 | bench_free_fn!( 14 | bench_ball_against_ball, 15 | query::contact, 16 | pos1: Isometry3, 17 | b1: Ball, 18 | pos2: Isometry3, 19 | b2: Ball, 20 | prediction: f32 21 | ); 22 | 23 | bench_free_fn!( 24 | bench_cuboid_against_cuboid, 25 | query::contact, 26 | pos1: Isometry3, 27 | b1: Cuboid, 28 | pos2: Isometry3, 29 | b2: Cuboid, 30 | prediction: f32 31 | ); 32 | 33 | bench_free_fn!( 34 | bench_capsule_against_capsule, 35 | query::contact, 36 | pos1: Isometry3, 37 | b1: Capsule, 38 | pos2: Isometry3, 39 | b2: Capsule, 40 | prediction: f32 41 | ); 42 | 43 | bench_free_fn!( 44 | bench_cone_against_cone, 45 | query::contact, 46 | pos1: Isometry3, 47 | b1: Cone, 48 | pos2: Isometry3, 49 | b2: Cone, 50 | prediction: f32 51 | ); 52 | 53 | bench_free_fn!( 54 | bench_cylinder_against_cylinder, 55 | query::contact, 56 | pos1: Isometry3, 57 | b1: Cylinder, 58 | pos2: Isometry3, 59 | b2: Cylinder, 60 | prediction: f32 61 | ); 62 | -------------------------------------------------------------------------------- /crates/parry3d/benches/query/mod.rs: -------------------------------------------------------------------------------- 1 | mod algorithm; 2 | mod contacts; 3 | mod ray; 4 | -------------------------------------------------------------------------------- /crates/parry3d/benches/support_map/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{generate, unref}; 2 | use na::{Isometry3, Vector3}; 3 | use parry3d::shape::ConvexPolyhedron; 4 | use parry3d::shape::SupportMap; 5 | use parry3d::shape::{Ball, Capsule, Cone, Cuboid, Cylinder, Segment, Triangle}; 6 | use rand::SeedableRng; 7 | use rand_isaac::IsaacRng; 8 | use test::Bencher; 9 | 10 | #[path = "../common/macros.rs"] 11 | #[macro_use] 12 | mod macros; 13 | 14 | bench_method!( 15 | bench_ball_support_map, 16 | support_point, 17 | c: Ball, 18 | m: Isometry3, 19 | dir: Vector3 20 | ); 21 | bench_method!( 22 | bench_cuboid_support_map, 23 | support_point, 24 | c: Cuboid, 25 | m: Isometry3, 26 | dir: Vector3 27 | ); 28 | bench_method!( 29 | bench_capsule_support_map, 30 | support_point, 31 | c: Capsule, 32 | m: Isometry3, 33 | dir: Vector3 34 | ); 35 | bench_method!( 36 | bench_cone_support_map, 37 | support_point, 38 | c: Cone, 39 | m: Isometry3, 40 | dir: Vector3 41 | ); 42 | bench_method!( 43 | bench_cylinder_support_map, 44 | support_point, 45 | c: Cylinder, 46 | m: Isometry3, 47 | dir: Vector3 48 | ); 49 | bench_method!( 50 | bench_segment_support_map, 51 | support_point, 52 | c: Segment, 53 | m: Isometry3, 54 | dir: Vector3 55 | ); 56 | bench_method!( 57 | bench_triangle_support_map, 58 | support_point, 59 | c: Triangle, 60 | m: Isometry3, 61 | dir: Vector3 62 | ); 63 | bench_method!( 64 | bench_convex_support_map, 65 | support_point, 66 | c: ConvexPolyhedron, 67 | m: Isometry3, 68 | dir: Vector3 69 | ); 70 | -------------------------------------------------------------------------------- /crates/parry3d/examples/ball3d.rs: -------------------------------------------------------------------------------- 1 | use parry3d::shape::Ball; 2 | 3 | fn main() { 4 | let ball = Ball::new(1.0f32); 5 | assert!(ball.radius == 1.0); 6 | } 7 | -------------------------------------------------------------------------------- /crates/parry3d/examples/capsule.rs: -------------------------------------------------------------------------------- 1 | use parry3d::shape::Capsule; 2 | 3 | fn main() { 4 | let capsule = Capsule::new_y(0.5f32, 0.75); 5 | 6 | assert!(capsule.half_height() == 0.5); 7 | assert!(capsule.radius == 0.75); 8 | } 9 | -------------------------------------------------------------------------------- /crates/parry3d/examples/cone.rs: -------------------------------------------------------------------------------- 1 | use parry3d::shape::Cone; 2 | 3 | fn main() { 4 | let cone = Cone::new(0.5f32, 0.75); 5 | 6 | assert!(cone.half_height == 0.5); 7 | assert!(cone.radius == 0.75); 8 | } 9 | -------------------------------------------------------------------------------- /crates/parry3d/examples/contact_query3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry3, Vector3}; 4 | use parry3d::query; 5 | use parry3d::shape::{Ball, Cuboid}; 6 | 7 | fn main() { 8 | let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0)); 9 | let ball = Ball::new(1.0); 10 | let prediction = 1.0; 11 | 12 | let cuboid_pos = Isometry3::identity(); 13 | let ball_pos_penetrating = Isometry3::translation(1.0, 1.0, 1.0); 14 | let ball_pos_in_prediction = Isometry3::translation(2.0, 2.0, 2.0); 15 | let ball_pos_too_far = Isometry3::translation(3.0, 3.0, 3.0); 16 | 17 | let ctct_penetrating = query::contact( 18 | &ball_pos_penetrating, 19 | &ball, 20 | &cuboid_pos, 21 | &cuboid, 22 | prediction, 23 | ) 24 | .unwrap(); 25 | let ctct_in_prediction = query::contact( 26 | &ball_pos_in_prediction, 27 | &ball, 28 | &cuboid_pos, 29 | &cuboid, 30 | prediction, 31 | ) 32 | .unwrap(); 33 | let ctct_too_far = 34 | query::contact(&ball_pos_too_far, &ball, &cuboid_pos, &cuboid, prediction).unwrap(); 35 | 36 | assert!(ctct_penetrating.unwrap().dist <= 0.0); 37 | assert!(ctct_in_prediction.unwrap().dist >= 0.0); 38 | assert_eq!(ctct_too_far, None); 39 | } 40 | -------------------------------------------------------------------------------- /crates/parry3d/examples/convex3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Point3; 4 | use parry3d::shape::ConvexPolyhedron; 5 | 6 | fn main() { 7 | let points = [ 8 | Point3::new(0.0f32, 0.0, 1.0), 9 | Point3::new(0.0, 0.0, -1.0), 10 | Point3::new(0.0, 1.0, 0.0), 11 | Point3::new(0.0, -1.0, 0.0), 12 | Point3::new(1.0, 0.0, 0.0), 13 | Point3::new(-1.0, 0.0, 0.0), 14 | Point3::new(0.0, 0.0, 0.0), 15 | ]; 16 | 17 | let convex = ConvexPolyhedron::from_convex_hull(&points).expect("Invalid convex shape."); 18 | convex.check_geometry(); 19 | } 20 | -------------------------------------------------------------------------------- /crates/parry3d/examples/convex_try_new3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Point3; 4 | use parry3d::shape::ConvexPolyhedron; 5 | 6 | fn main() { 7 | let points = vec![ 8 | Point3::new(0.0f32, 0.0, 1.0), 9 | Point3::new(0.0, 0.0, -1.0), 10 | Point3::new(0.0, 1.0, 0.0), 11 | Point3::new(0.0, -1.0, 0.0), 12 | Point3::new(1.0, 0.0, 0.0), 13 | Point3::new(-1.0, 0.0, 0.0), 14 | ]; 15 | 16 | let indices = vec![ 17 | [0, 4, 2], 18 | [0, 3, 4], 19 | [5, 0, 2], 20 | [5, 3, 0], 21 | [1, 5, 2], 22 | [1, 3, 5], 23 | [4, 1, 2], 24 | [4, 3, 1], 25 | ]; 26 | 27 | let convex = 28 | ConvexPolyhedron::from_convex_mesh(points, &indices).expect("Invalid convex shape."); 29 | convex.check_geometry(); 30 | } 31 | -------------------------------------------------------------------------------- /crates/parry3d/examples/cuboid3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Vector3; 4 | use parry3d::shape::Cuboid; 5 | 6 | fn main() { 7 | let cuboid = Cuboid::new(Vector3::new(2.0f32, 1.0, 3.0)); 8 | 9 | assert!(cuboid.half_extents.x == 2.0); 10 | assert!(cuboid.half_extents.y == 1.0); 11 | assert!(cuboid.half_extents.z == 3.0); 12 | } 13 | -------------------------------------------------------------------------------- /crates/parry3d/examples/cylinder.rs: -------------------------------------------------------------------------------- 1 | use parry3d::shape::Cylinder; 2 | 3 | fn main() { 4 | let cylinder = Cylinder::new(0.5f32, 1.0); 5 | 6 | assert!(cylinder.half_height == 0.5); 7 | assert!(cylinder.radius == 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /crates/parry3d/examples/distance_query3d.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate approx; // for relative_eq! 3 | extern crate nalgebra as na; 4 | 5 | use na::{Isometry3, Vector3}; 6 | use parry3d::query; 7 | use parry3d::shape::{Ball, Cuboid}; 8 | 9 | fn main() { 10 | let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0)); 11 | let ball = Ball::new(1.0); 12 | 13 | let cuboid_pos = Isometry3::identity(); 14 | let ball_pos_intersecting = Isometry3::translation(0.0, 1.0, 0.0); 15 | let ball_pos_disjoint = Isometry3::translation(0.0, 3.0, 0.0); 16 | 17 | let dist_intersecting = 18 | query::distance(&ball_pos_intersecting, &ball, &cuboid_pos, &cuboid).unwrap(); 19 | let dist_disjoint = query::distance(&ball_pos_disjoint, &ball, &cuboid_pos, &cuboid).unwrap(); 20 | 21 | assert_eq!(dist_intersecting, 0.0); 22 | assert!(relative_eq!(dist_disjoint, 1.0, epsilon = 1.0e-7)); 23 | } 24 | -------------------------------------------------------------------------------- /crates/parry3d/examples/getting_started.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry3, Point3, Vector3}; 4 | use parry3d::query::{Ray, RayCast}; 5 | use parry3d::shape::Cuboid; 6 | 7 | fn main() { 8 | let cube = Cuboid::new(Vector3::new(1.0f32, 1.0, 1.0)); 9 | let ray = Ray::new(Point3::new(0.0f32, 0.0, -1.0), Vector3::z()); 10 | 11 | assert!(cube.intersects_ray(&Isometry3::identity(), &ray, f32::MAX)); 12 | } 13 | -------------------------------------------------------------------------------- /crates/parry3d/examples/mesh3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Point3; 4 | use parry3d::shape::TriMesh; 5 | 6 | fn main() { 7 | let points = vec![ 8 | Point3::new(0.0, 1.0, 0.0), 9 | Point3::new(-1.0, -0.5, 0.0), 10 | Point3::new(0.0, -0.5, -1.0), 11 | Point3::new(1.0, -0.5, 0.0), 12 | ]; 13 | 14 | let indices = vec![[0u32, 1, 2], [0, 2, 3], [0, 3, 1]]; 15 | 16 | // Build the mesh. 17 | let mesh = TriMesh::new(points, indices).unwrap(); 18 | 19 | assert!(mesh.vertices().len() == 4); 20 | } 21 | -------------------------------------------------------------------------------- /crates/parry3d/examples/plane3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Vector3; 4 | use parry3d::shape::HalfSpace; 5 | 6 | fn main() { 7 | let halfspace = HalfSpace::new(Vector3::::y_axis()); 8 | 9 | assert!(halfspace.normal.x == 0.0); 10 | assert!(halfspace.normal.y == 1.0); 11 | assert!(halfspace.normal.z == 0.0); 12 | } 13 | -------------------------------------------------------------------------------- /crates/parry3d/examples/polyline3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::Point3; 4 | use parry3d::shape::Polyline; 5 | 6 | fn main() { 7 | let points = vec![ 8 | Point3::new(0.0, 1.0, 0.0), 9 | Point3::new(-1.0, -1.0, 1.0), 10 | Point3::new(0.0, -0.5, 0.0), 11 | Point3::new(1.0, -1.0, -1.0), 12 | Point3::new(0.0, 1.0, 0.0), // This forms a loop. 13 | ]; 14 | 15 | // Build the polyline. 16 | let polyline = Polyline::new(points, None); 17 | 18 | assert!(polyline.vertices().len() == 5); 19 | } 20 | -------------------------------------------------------------------------------- /crates/parry3d/examples/proximity_query3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry3, Vector3}; 4 | use parry3d::query; 5 | use parry3d::shape::{Ball, Cuboid}; 6 | 7 | fn main() { 8 | let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0)); 9 | let ball = Ball::new(1.0); 10 | let cuboid_pos = Isometry3::identity(); 11 | let ball_pos_intersecting = Isometry3::translation(1.0, 1.0, 1.0); 12 | let ball_pos_disjoint = Isometry3::translation(3.0, 3.0, 3.0); 13 | 14 | let intersecting = 15 | query::intersection_test(&ball_pos_intersecting, &ball, &cuboid_pos, &cuboid).unwrap(); 16 | let not_intersecting = 17 | !query::intersection_test(&ball_pos_disjoint, &ball, &cuboid_pos, &cuboid).unwrap(); 18 | 19 | assert!(intersecting); 20 | assert!(not_intersecting); 21 | } 22 | -------------------------------------------------------------------------------- /crates/parry3d/examples/solid_point_query3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry3, Point3, Vector3}; 4 | use parry3d::query::PointQuery; 5 | use parry3d::shape::Cuboid; 6 | 7 | fn main() { 8 | let cuboid = Cuboid::new(Vector3::new(1.0, 2.0, 2.0)); 9 | let pt_inside = Point3::origin(); 10 | let pt_outside = Point3::new(2.0, 2.0, 2.0); 11 | 12 | // Solid projection. 13 | assert_eq!( 14 | cuboid.distance_to_point(&Isometry3::identity(), &pt_inside, true), 15 | 0.0 16 | ); 17 | 18 | // Non-solid projection. 19 | assert_eq!( 20 | cuboid.distance_to_point(&Isometry3::identity(), &pt_inside, false), 21 | -1.0 22 | ); 23 | 24 | // The other point is outside of the cuboid so the `solid` flag has no effect. 25 | assert_eq!( 26 | cuboid.distance_to_point(&Isometry3::identity(), &pt_outside, false), 27 | 1.0 28 | ); 29 | assert_eq!( 30 | cuboid.distance_to_point(&Isometry3::identity(), &pt_outside, true), 31 | 1.0 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /crates/parry3d/examples/solid_ray_cast3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry3, Point3, Vector3}; 4 | use parry3d::query::{Ray, RayCast}; 5 | use parry3d::shape::Cuboid; 6 | 7 | fn main() { 8 | let cuboid = Cuboid::new(Vector3::new(1.0, 2.0, 1.0)); 9 | let ray_inside = Ray::new(Point3::origin(), Vector3::y()); 10 | let ray_miss = Ray::new(Point3::new(2.0, 2.0, 2.0), Vector3::new(1.0, 1.0, 1.0)); 11 | 12 | // Solid cast. 13 | assert_eq!( 14 | cuboid 15 | .cast_ray(&Isometry3::identity(), &ray_inside, f32::MAX, true) 16 | .unwrap(), 17 | 0.0 18 | ); 19 | 20 | // Non-solid cast. 21 | assert_eq!( 22 | cuboid 23 | .cast_ray(&Isometry3::identity(), &ray_inside, f32::MAX, false) 24 | .unwrap(), 25 | 2.0 26 | ); 27 | 28 | // The other ray does not intersect this shape. 29 | assert!(cuboid 30 | .cast_ray(&Isometry3::identity(), &ray_miss, f32::MAX, false) 31 | .is_none()); 32 | assert!(cuboid 33 | .cast_ray(&Isometry3::identity(), &ray_miss, f32::MAX, true) 34 | .is_none()); 35 | } 36 | -------------------------------------------------------------------------------- /crates/parry3d/examples/time_of_impact_query3d.rs: -------------------------------------------------------------------------------- 1 | extern crate nalgebra as na; 2 | 3 | use na::{Isometry3, Vector3}; 4 | use parry3d::query::{self, ShapeCastOptions}; 5 | use parry3d::shape::{Ball, Cuboid}; 6 | 7 | fn main() { 8 | let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0)); 9 | let ball = Ball::new(1.0); 10 | 11 | let cuboid_pos = Isometry3::identity(); 12 | let ball_pos_intersecting = Isometry3::translation(1.0, 1.0, 1.0); 13 | let ball_pos_will_touch = Isometry3::translation(2.0, 2.0, 2.0); 14 | let ball_pos_wont_touch = Isometry3::translation(3.0, 3.0, 3.0); 15 | 16 | let cuboid_vel1 = Vector3::new(-1.0, 1.0, 1.0); 17 | let cuboid_vel2 = Vector3::new(1.0, 1.0, 1.0); 18 | 19 | let ball_vel1 = Vector3::new(2.0, 2.0, 2.0); 20 | let ball_vel2 = Vector3::new(-0.5, -0.5, -0.5); 21 | 22 | let toi_intersecting = query::cast_shapes( 23 | &ball_pos_intersecting, 24 | &ball_vel1, 25 | &ball, 26 | &cuboid_pos, 27 | &cuboid_vel1, 28 | &cuboid, 29 | ShapeCastOptions::default(), 30 | ) 31 | .unwrap(); 32 | let toi_will_touch = query::cast_shapes( 33 | &ball_pos_will_touch, 34 | &ball_vel2, 35 | &ball, 36 | &cuboid_pos, 37 | &cuboid_vel2, 38 | &cuboid, 39 | ShapeCastOptions::default(), 40 | ) 41 | .unwrap(); 42 | let toi_wont_touch = query::cast_shapes( 43 | &ball_pos_wont_touch, 44 | &ball_vel1, 45 | &ball, 46 | &cuboid_pos, 47 | &cuboid_vel1, 48 | &cuboid, 49 | ShapeCastOptions::default(), 50 | ) 51 | .unwrap(); 52 | 53 | assert_eq!(toi_intersecting.map(|hit| hit.time_of_impact), Some(0.0)); 54 | assert!(toi_will_touch.is_some() && toi_will_touch.unwrap().time_of_impact > 0.0); 55 | assert_eq!(toi_wont_touch.map(|hit| hit.time_of_impact), None); 56 | } 57 | -------------------------------------------------------------------------------- /crates/parry3d/tests/geometry/aabb_scale.rs: -------------------------------------------------------------------------------- 1 | use na::{Point3, Vector3}; 2 | use parry3d::bounding_volume::Aabb; 3 | 4 | #[test] 5 | fn test_aabb_scale_wrt_center() { 6 | let aabb = Aabb::from_half_extents(Point3::new(1.0, 2.0, 3.0), Vector3::new(4.0, 5.0, 6.0)); 7 | let scale = Vector3::new(10.0, -20.0, 50.0); 8 | let scaled_aabb = aabb.scaled_wrt_center(&scale); 9 | let scaled_aabb_neg = aabb.scaled_wrt_center(&-scale); 10 | let scaled_aabb_abs = aabb.scaled_wrt_center(&scale.abs()); 11 | 12 | assert_eq!(&scaled_aabb, &scaled_aabb_neg); 13 | assert_eq!(&scaled_aabb, &scaled_aabb_abs); 14 | assert_eq!(aabb.center(), scaled_aabb.center()); 15 | assert_eq!(scaled_aabb.half_extents(), Vector3::new(40.0, 100.0, 300.0)); 16 | } 17 | -------------------------------------------------------------------------------- /crates/parry3d/tests/geometry/ball_ball_toi.rs: -------------------------------------------------------------------------------- 1 | // Issue #35 2 | 3 | use na::{self, Isometry3, Vector3}; 4 | use parry3d::query::{self, ShapeCastOptions}; 5 | use parry3d::shape::Ball; 6 | 7 | #[test] 8 | fn test_ball_ball_toi() { 9 | let b = Ball::new(0.5); 10 | let m1 = Isometry3::identity(); 11 | let m2 = Isometry3::translation(0.0, 10.0, 0.0); 12 | let vel1 = Vector3::new(0.0, 10.0, 0.0); 13 | let vel2 = Vector3::zeros(); 14 | 15 | let cast = 16 | query::cast_shapes(&m1, &vel1, &b, &m2, &vel2, &b, ShapeCastOptions::default()).unwrap(); 17 | 18 | assert_eq!(cast.unwrap().time_of_impact, 0.9); 19 | } 20 | -------------------------------------------------------------------------------- /crates/parry3d/tests/geometry/ball_triangle_toi.rs: -------------------------------------------------------------------------------- 1 | // Issue #123 2 | 3 | use na::{self, Isometry3, Point3, Vector3}; 4 | use parry3d::query::{self, ShapeCastOptions}; 5 | use parry3d::shape::{Ball, Triangle}; 6 | 7 | #[test] 8 | fn ball_triangle_toi_infinite_loop_issue() { 9 | let b = Ball::new(0.375f32); 10 | let t = Triangle::new( 11 | Point3::new(0.5, -0.5, 0.0), 12 | Point3::new(-0.5, -0.5, 0.0), 13 | Point3::new(-0.5, 0.5, 0.0), 14 | ); 15 | 16 | let m1 = Isometry3::translation(0.0, 0.0, 0.0); 17 | let m2 = Isometry3::translation(11.5, 5.5, 0.0); 18 | let vel1 = Vector3::new(0.0, 0.000000000000000000000000000000000000000006925, 0.0); 19 | let vel2 = Vector3::zeros(); 20 | 21 | let cast = 22 | query::cast_shapes(&m1, &vel1, &b, &m2, &vel2, &t, ShapeCastOptions::default()).unwrap(); 23 | 24 | println!("ShapeCastHit: {:?}", cast); 25 | assert!(cast.is_none()); // The provided velocity is too small. 26 | } 27 | -------------------------------------------------------------------------------- /crates/parry3d/tests/geometry/cylinder_cuboid_contact.rs: -------------------------------------------------------------------------------- 1 | use na::{self, Isometry3, Vector3}; 2 | use parry3d::query; 3 | use parry3d::shape::{Cuboid, Cylinder}; 4 | 5 | // Issue #157. 6 | #[test] 7 | fn cylinder_cuboid_contact() { 8 | let cyl = Cylinder::new(0.925, 0.5); 9 | let cyl_at = Isometry3::translation(10.97, 0.925, 61.02); 10 | let cuboid = Cuboid::new(Vector3::new(0.05, 0.75, 0.5)); 11 | let cuboid_at = Isometry3::translation(11.50, 0.75, 60.5); 12 | let distance = query::details::distance_support_map_support_map( 13 | &cyl_at.inv_mul(&cuboid_at), 14 | &cyl, 15 | &cuboid, 16 | ); 17 | 18 | let intersecting = query::details::intersection_test_support_map_support_map( 19 | &cyl_at.inv_mul(&cuboid_at), 20 | &cyl, 21 | &cuboid, 22 | ); 23 | 24 | let contact = query::details::contact_support_map_support_map( 25 | &cyl_at.inv_mul(&cuboid_at), 26 | &cyl, 27 | &cuboid, 28 | 10.0, 29 | ); 30 | 31 | assert!(distance == 0.0); 32 | assert!(intersecting); 33 | assert!(contact.is_some()); 34 | } 35 | -------------------------------------------------------------------------------- /crates/parry3d/tests/geometry/mod.rs: -------------------------------------------------------------------------------- 1 | mod aabb_scale; 2 | mod ball_ball_toi; 3 | mod ball_triangle_toi; 4 | mod convex_hull; 5 | mod cuboid_ray_cast; 6 | mod cylinder_cuboid_contact; 7 | mod epa3; 8 | mod still_objects_toi; 9 | mod time_of_impact3; 10 | mod trimesh_connected_components; 11 | mod trimesh_intersection; 12 | mod trimesh_trimesh_toi; 13 | -------------------------------------------------------------------------------- /crates/parry3d/tests/geometry/still_objects_toi.rs: -------------------------------------------------------------------------------- 1 | use na::{self, Isometry3, Vector3}; 2 | use parry3d::query::{cast_shapes, ShapeCastOptions}; 3 | use parry3d::shape::Cuboid; 4 | 5 | /** 6 | * Issue #141 7 | * Sets up a situation like this: 8 | * ```raw 9 | * +---+ 10 | * | 1 | 11 | * | | 12 | * +---+ 13 | * 14 | * +---+ 15 | * | 2 | 16 | * | | 17 | * +---+ 18 | * ``` 19 | * with box 1 having the provided v_y. 20 | */ 21 | fn collide(v_y: f32) -> Option { 22 | let pos1 = Isometry3::translation(0.0, 1.1, 0.0); 23 | let pos2 = Isometry3::identity(); 24 | let vel1 = Vector3::y() * v_y; 25 | let vel2 = Vector3::zeros(); 26 | let cuboid = Cuboid::new(Vector3::new(0.5, 0.5, 0.5)); 27 | 28 | cast_shapes( 29 | &pos1, 30 | &vel1, 31 | &cuboid, 32 | &pos2, 33 | &vel2, 34 | &cuboid, 35 | ShapeCastOptions::default(), 36 | ) 37 | .unwrap() 38 | .map(|hit| hit.time_of_impact) 39 | } 40 | 41 | #[test] 42 | fn no_movement() { 43 | assert_eq!(collide(0.0), None); 44 | } 45 | 46 | #[test] 47 | fn moving_up_misses() { 48 | assert_eq!(collide(1.0), None); 49 | } 50 | 51 | #[test] 52 | fn moving_down_hits() { 53 | assert!(collide(-1.0).is_some()); 54 | } 55 | -------------------------------------------------------------------------------- /crates/parry3d/tests/geometry/trimesh_connected_components.rs: -------------------------------------------------------------------------------- 1 | use parry3d::math::Point; 2 | use parry3d::shape::{TriMesh, TriMeshFlags}; 3 | 4 | #[test] 5 | // From https://github.com/dimforge/parry/issues/115 6 | fn mesh_connected_components_grouped_faces() { 7 | let verts = vec![ 8 | // Face 0 9 | Point::new(15.82, 6.455, -0.15), // <- Vertex shared with face 1. 10 | Point::new(9.915, 6.455, -0.15), 11 | Point::new(9.915, 6.4, 0.0), // <- Vertex shared with face 1. 12 | // Face1 13 | Point::new(15.82, 6.455, -0.15), // <- Vertex shared with face 0. 14 | Point::new(9.915, 6.4, 0.0), // <- Vertex shared with face 0. 15 | Point::new(15.82, 6.4, 0.0), 16 | ]; 17 | 18 | let mut roof = TriMesh::new(verts, vec![[0, 1, 2], [3, 4, 5]]).unwrap(); 19 | 20 | if let Err(e) = 21 | roof.set_flags(TriMeshFlags::MERGE_DUPLICATE_VERTICES | TriMeshFlags::CONNECTED_COMPONENTS) 22 | { 23 | dbg!(e); 24 | assert!(false); 25 | } 26 | 27 | let components = roof.connected_components().unwrap(); 28 | println!("components: {:?}", components); 29 | assert_eq!(components.ranges.len(), 2); // Only one connected-component (two range values). 30 | assert_eq!(components.grouped_faces.len(), 2); // Only two faces in the connected-component. 31 | } 32 | -------------------------------------------------------------------------------- /crates/parry3d/tests/geometry/trimesh_trimesh_toi.rs: -------------------------------------------------------------------------------- 1 | // Issue #194 2 | 3 | use na::{zero, Isometry3, Point3, Vector3}; 4 | use parry3d::math::Real; 5 | use parry3d::query::{self, ShapeCastOptions}; 6 | use parry3d::shape::TriMesh; 7 | 8 | fn build_pyramid() -> TriMesh { 9 | let points = vec![ 10 | Point3::new(0.0, 1.0, 0.0), 11 | Point3::new(-1.0, -0.5, 0.0), 12 | Point3::new(0.0, -0.5, -1.0), 13 | Point3::new(1.0, -0.5, 0.0), 14 | ]; 15 | 16 | let indices = vec![[0u32, 1, 2], [0, 2, 3], [0, 3, 1]]; 17 | 18 | TriMesh::new(points, indices).unwrap() 19 | } 20 | 21 | fn do_toi_test() -> Option { 22 | const SPEED: Real = 100000.0; 23 | 24 | let shape_one = build_pyramid(); 25 | let shape_two = build_pyramid(); 26 | 27 | let pos_one = Vector3::new(0.0, 0.0, 0.0); 28 | let pos_two = Vector3::new(1000.0, 0.0, 0.0); 29 | 30 | let transform_one = Isometry3::new(pos_one, zero()); 31 | let transform_two = Isometry3::new(pos_two, zero()); 32 | 33 | let vel_one = Vector3::new(SPEED, 0.0, 0.0); 34 | let vel_two = Vector3::new(0.0, 0.0, 0.0); 35 | 36 | query::cast_shapes( 37 | &transform_one, 38 | &vel_one, 39 | &shape_one, 40 | &transform_two, 41 | &vel_two, 42 | &shape_two, 43 | ShapeCastOptions::default(), 44 | ) 45 | .unwrap() 46 | .map(|hit| hit.time_of_impact) 47 | } 48 | 49 | #[test] 50 | fn trimesh_trimesh_toi() { 51 | let time_of_impact = do_toi_test(); 52 | assert_eq!(time_of_impact, Some(0.00998)); 53 | } 54 | -------------------------------------------------------------------------------- /crates/parry3d/tests/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate approx; 3 | extern crate nalgebra as na; 4 | extern crate parry3d; 5 | 6 | mod geometry; 7 | -------------------------------------------------------------------------------- /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' crates/parry2d/Cargo.toml > $tmp/Cargo.toml 12 | rm -rf $tmp/examples 13 | cp -r crates/parry2d/examples $tmp/examples 14 | currdir=`pwd` 15 | cd $tmp && cargo publish 16 | cd $currdir 17 | 18 | ### Publish the 2D f64 version. 19 | sed 's#\.\./\.\./src#src#g' crates/parry2d-f64/Cargo.toml > $tmp/Cargo.toml 20 | cd $tmp && cargo publish 21 | cd $currdir 22 | 23 | 24 | ### Publish the 3D version. 25 | sed 's#\.\./\.\./src#src#g' crates/parry3d/Cargo.toml > $tmp/Cargo.toml 26 | rm -rf $tmp/examples 27 | cp -r crates/parry3d/examples $tmp/examples 28 | cp -r LICENSE README.md $tmp/. 29 | cd $tmp && cargo publish 30 | cd $currdir 31 | 32 | ### Publish the 3D f64 version. 33 | sed 's#\.\./\.\./src#src#g' crates/parry3d-f64/Cargo.toml > $tmp/Cargo.toml 34 | cp -r LICENSE README.md $tmp/. 35 | cd $tmp && cargo publish 36 | 37 | rm -rf $tmp 38 | 39 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | #unstable_features = true 2 | #indent_style = "Block" 3 | #where_single_line = true 4 | #brace_style = "PreferSameLine" -------------------------------------------------------------------------------- /src/bounding_volume/aabb_ball.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Point, Real, Vector}; 3 | use crate::shape::Ball; 4 | 5 | /// Computes the Axis-Aligned Bounding Box of a ball transformed by `center`. 6 | #[inline] 7 | pub fn ball_aabb(center: &Point, radius: Real) -> Aabb { 8 | Aabb::new( 9 | *center + Vector::repeat(-radius), 10 | *center + Vector::repeat(radius), 11 | ) 12 | } 13 | 14 | /// Computes the Axis-Aligned Bounding Box of a ball. 15 | #[inline] 16 | pub fn local_ball_aabb(radius: Real) -> Aabb { 17 | let half_extents = Point::from(Vector::repeat(radius)); 18 | 19 | Aabb::new(-half_extents, half_extents) 20 | } 21 | 22 | impl Ball { 23 | /// Computes the world-space [`Aabb`] of this ball transformed by `pos`. 24 | #[inline] 25 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 26 | ball_aabb(&Point::::from(pos.translation.vector), self.radius) 27 | } 28 | 29 | /// Computes the local-space [`Aabb`] of this ball. 30 | #[inline] 31 | pub fn local_aabb(&self) -> Aabb { 32 | local_ball_aabb(self.radius) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/bounding_volume/aabb_capsule.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Real, Vector}; 3 | use crate::shape::Capsule; 4 | 5 | impl Capsule { 6 | /// The axis-aligned bounding box of this capsule. 7 | #[inline] 8 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 9 | self.transform_by(pos).local_aabb() 10 | } 11 | 12 | /// The axis-aligned bounding box of this capsule. 13 | #[inline] 14 | pub fn local_aabb(&self) -> Aabb { 15 | let a = self.segment.a; 16 | let b = self.segment.b; 17 | let mins = a.coords.inf(&b.coords) - Vector::repeat(self.radius); 18 | let maxs = a.coords.sup(&b.coords) + Vector::repeat(self.radius); 19 | Aabb::new(mins.into(), maxs.into()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/bounding_volume/aabb_convex_polygon.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Real}; 3 | use crate::shape::ConvexPolygon; 4 | 5 | impl ConvexPolygon { 6 | /// Computes the world-space [`Aabb`] of this convex polygon, transformed by `pos`. 7 | #[inline] 8 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 9 | super::details::point_cloud_aabb(pos, self.points()) 10 | } 11 | 12 | /// Computes the local-space [`Aabb`] of this convex polygon. 13 | #[inline] 14 | pub fn local_aabb(&self) -> Aabb { 15 | super::details::local_point_cloud_aabb(self.points()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/bounding_volume/aabb_convex_polyhedron.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Real}; 3 | use crate::shape::ConvexPolyhedron; 4 | 5 | impl ConvexPolyhedron { 6 | /// Computes the world-space [`Aabb`] of this convex polyhedron, transformed by `pos`. 7 | #[inline] 8 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 9 | super::details::point_cloud_aabb(pos, self.points()) 10 | } 11 | 12 | /// Computes the local-space [`Aabb`] of this convex polyhedron. 13 | #[inline] 14 | pub fn local_aabb(&self) -> Aabb { 15 | super::details::local_point_cloud_aabb(self.points()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/bounding_volume/aabb_cuboid.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Point, Real}; 3 | use crate::shape::Cuboid; 4 | use crate::utils::IsometryOps; 5 | 6 | impl Cuboid { 7 | /// Computes the world-space [`Aabb`] of this cuboid, transformed by `pos`. 8 | #[inline] 9 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 10 | let center = Point::from(pos.translation.vector); 11 | let ws_half_extents = pos.absolute_transform_vector(&self.half_extents); 12 | 13 | Aabb::from_half_extents(center, ws_half_extents) 14 | } 15 | 16 | /// Computes the local-space [`Aabb`] of this cuboid. 17 | #[inline] 18 | pub fn local_aabb(&self) -> Aabb { 19 | let half_extents = Point::from(self.half_extents); 20 | 21 | Aabb::new(-half_extents, half_extents) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/bounding_volume/aabb_halfspace.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Point, Real}; 3 | use crate::num::Bounded; 4 | use crate::shape::HalfSpace; 5 | use na; 6 | 7 | impl HalfSpace { 8 | /// Computes the world-space [`Aabb`] of this half-space. 9 | #[inline] 10 | pub fn aabb(&self, _pos: &Isometry) -> Aabb { 11 | self.local_aabb() 12 | } 13 | 14 | /// Computes the local-space [`Aabb`] of this half-space. 15 | #[inline] 16 | pub fn local_aabb(&self) -> Aabb { 17 | // We divide by 2.0 so that we can still make some operations with it (like loosening) 18 | // without breaking the box. 19 | let max = Point::max_value() * na::convert::(0.5f64); 20 | Aabb::new(-max, max) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/bounding_volume/aabb_heightfield.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Real}; 3 | use crate::shape::HeightField; 4 | 5 | impl HeightField { 6 | /// Computes the world-space [`Aabb`] of this heightfield, transformed by `pos`. 7 | #[inline] 8 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 9 | self.root_aabb().transform_by(pos) 10 | } 11 | 12 | /// Computes the local-space [`Aabb`] of this heightfield. 13 | #[inline] 14 | pub fn local_aabb(&self) -> Aabb { 15 | *self.root_aabb() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/bounding_volume/aabb_support_map.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume; 2 | use crate::bounding_volume::Aabb; 3 | use crate::math::{Isometry, Real}; 4 | use crate::shape::Segment; 5 | #[cfg(feature = "dim3")] 6 | use crate::shape::{Cone, Cylinder}; 7 | 8 | #[cfg(feature = "dim3")] 9 | impl Cone { 10 | /// Computes the world-space [`Aabb`] of this cone, transformed by `pos`. 11 | #[inline] 12 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 13 | bounding_volume::details::support_map_aabb(pos, self) 14 | } 15 | 16 | /// Computes the local-space [`Aabb`] of this cone. 17 | #[inline] 18 | pub fn local_aabb(&self) -> Aabb { 19 | bounding_volume::details::local_support_map_aabb(self) 20 | } 21 | } 22 | 23 | #[cfg(feature = "dim3")] 24 | impl Cylinder { 25 | /// Computes the world-space [`Aabb`] of this cylinder, transformed by `pos`. 26 | #[inline] 27 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 28 | bounding_volume::details::support_map_aabb(pos, self) 29 | } 30 | 31 | /// Computes the local-space [`Aabb`] of this cylinder. 32 | #[inline] 33 | pub fn local_aabb(&self) -> Aabb { 34 | bounding_volume::details::local_support_map_aabb(self) 35 | } 36 | } 37 | 38 | impl Segment { 39 | /// Computes the world-space [`Aabb`] of this segment, transformed by `pos`. 40 | #[inline] 41 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 42 | self.transformed(pos).local_aabb() 43 | } 44 | 45 | /// Computes the local-space [`Aabb`] of this segment. 46 | #[inline] 47 | pub fn local_aabb(&self) -> Aabb { 48 | bounding_volume::details::local_support_map_aabb(self) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/bounding_volume/aabb_triangle.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bounding_volume::Aabb, 3 | math::{Isometry, Point, Real, DIM}, 4 | shape::Triangle, 5 | }; 6 | 7 | impl Triangle { 8 | /// Computes the world-space [`Aabb`] of this triangle, transformed by `pos`. 9 | #[inline] 10 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 11 | self.transformed(pos).local_aabb() 12 | } 13 | 14 | /// Computes the local-space [`Aabb`] of this triangle. 15 | #[inline] 16 | pub fn local_aabb(&self) -> Aabb { 17 | let a = self.a.coords; 18 | let b = self.b.coords; 19 | let c = self.c.coords; 20 | 21 | let mut min = Point::origin(); 22 | let mut max = Point::origin(); 23 | 24 | for d in 0..DIM { 25 | min.coords[d] = a[d].min(b[d]).min(c[d]); 26 | max.coords[d] = a[d].max(b[d]).max(c[d]); 27 | } 28 | 29 | Aabb::new(min, max) 30 | } 31 | } 32 | 33 | #[cfg(test)] 34 | #[cfg(feature = "dim3")] 35 | mod test { 36 | use crate::{ 37 | bounding_volume::details::support_map_aabb, 38 | math::{Isometry, Point, Real, Translation}, 39 | shape::Triangle, 40 | }; 41 | use na::{RealField, UnitQuaternion}; 42 | 43 | #[test] 44 | fn triangle_aabb_matches_support_map_aabb() { 45 | let t = Triangle::new( 46 | Point::new(0.3, -0.1, 0.2), 47 | Point::new(-0.7, 1.0, 0.0), 48 | Point::new(-0.7, 1.5, 0.0), 49 | ); 50 | 51 | let m = Isometry::from_parts( 52 | Translation::new(-0.2, 5.0, 0.2), 53 | UnitQuaternion::from_euler_angles(0.0, Real::frac_pi_2(), 0.0), 54 | ); 55 | 56 | assert_eq!(t.aabb(&m), support_map_aabb(&m, &t)); 57 | 58 | // TODO: also test local Aabb once support maps have a local Aabb 59 | // function too 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/bounding_volume/aabb_voxels.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Real, Translation}; 3 | use crate::shape::{Cuboid, Voxels}; 4 | 5 | impl Voxels { 6 | /// Computes the world-space Aabb of this set of voxels, transformed by `pos`. 7 | #[inline] 8 | pub fn aabb(&self, pos: &Isometry) -> Aabb { 9 | let shift = Translation::from(self.domain_center()); 10 | Cuboid::new(self.extents() / 2.0).aabb(&(pos * shift)) 11 | } 12 | 13 | /// Computes the local-space Aabb of this set of voxels. 14 | #[inline] 15 | pub fn local_aabb(&self) -> Aabb { 16 | Cuboid::new(self.extents() / 2.0) 17 | .local_aabb() 18 | .translated(&self.domain_center().coords) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_ball.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Point, Real}; 3 | use crate::shape::Ball; 4 | 5 | impl Ball { 6 | /// Computes the world-space bounding sphere of this ball, transformed by `pos`. 7 | #[inline] 8 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 9 | let bv: BoundingSphere = self.local_bounding_sphere(); 10 | bv.transform_by(pos) 11 | } 12 | 13 | /// Computes the local-space Aabb of this ball. 14 | #[inline] 15 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 16 | BoundingSphere::new(Point::origin(), self.radius) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_capsule.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Real}; 3 | use crate::shape::Capsule; 4 | 5 | impl Capsule { 6 | /// Computes the world-space bounding sphere of this capsule, transformed by `pos`. 7 | #[inline] 8 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 9 | self.local_bounding_sphere().transform_by(pos) 10 | } 11 | 12 | /// Computes the world-space bounding sphere of this capsule. 13 | #[inline] 14 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 15 | let radius = self.radius + self.half_height(); 16 | BoundingSphere::new(self.center(), radius) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_cone.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Point, Real}; 3 | use crate::shape::Cone; 4 | use na::ComplexField; 5 | 6 | impl Cone { 7 | /// Computes the world-space bounding sphere of this cone, transformed by `pos`. 8 | #[inline] 9 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 10 | let bv: BoundingSphere = self.local_bounding_sphere(); 11 | bv.transform_by(pos) 12 | } 13 | 14 | /// Computes the local-space bounding sphere of this cone. 15 | #[inline] 16 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 17 | let radius = 18 | ComplexField::sqrt(self.radius * self.radius + self.half_height * self.half_height); 19 | 20 | BoundingSphere::new(Point::origin(), radius) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_convex.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume; 2 | use crate::bounding_volume::BoundingSphere; 3 | use crate::math::{Isometry, Real}; 4 | use crate::shape::ConvexPolyhedron; 5 | 6 | impl ConvexPolyhedron { 7 | /// Computes the world-space bounding sphere of this convex polyhedron, transformed by `pos`. 8 | #[inline] 9 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 10 | let bv: BoundingSphere = self.local_bounding_sphere(); 11 | bv.transform_by(pos) 12 | } 13 | 14 | /// Computes the local-space bounding sphere of this convex polyhedron. 15 | #[inline] 16 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 17 | bounding_volume::details::point_cloud_bounding_sphere(self.points()) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_convex_polygon.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume; 2 | use crate::bounding_volume::BoundingSphere; 3 | use crate::math::{Isometry, Real}; 4 | use crate::shape::ConvexPolygon; 5 | 6 | impl ConvexPolygon { 7 | /// Computes the world-space bounding sphere of this convex polygon, transformed by `pos`. 8 | #[inline] 9 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 10 | let bv: BoundingSphere = self.local_bounding_sphere(); 11 | bv.transform_by(pos) 12 | } 13 | 14 | /// Computes the local-space bounding sphere of this convex polygon. 15 | #[inline] 16 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 17 | bounding_volume::details::point_cloud_bounding_sphere(self.points()) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_cuboid.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Point, Real}; 3 | use crate::shape::Cuboid; 4 | 5 | impl Cuboid { 6 | /// Computes the world-space bounding sphere of this cuboid, transformed by `pos`. 7 | #[inline] 8 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 9 | let bv: BoundingSphere = self.local_bounding_sphere(); 10 | bv.transform_by(pos) 11 | } 12 | 13 | /// Computes the local-space bounding sphere of this cuboid. 14 | #[inline] 15 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 16 | let radius = self.half_extents.norm(); 17 | BoundingSphere::new(Point::origin(), radius) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_cylinder.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Point, Real}; 3 | use crate::shape::Cylinder; 4 | use na::ComplexField; 5 | 6 | impl Cylinder { 7 | /// Computes the world-space bounding sphere of this cylinder, transformed by `pos`. 8 | #[inline] 9 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 10 | let bv: BoundingSphere = self.local_bounding_sphere(); 11 | bv.transform_by(pos) 12 | } 13 | 14 | /// Computes the local-space bounding sphere of this cylinder. 15 | #[inline] 16 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 17 | let radius = 18 | ComplexField::sqrt(self.radius * self.radius + self.half_height * self.half_height); 19 | 20 | BoundingSphere::new(Point::origin(), radius) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_halfspace.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Point, Real}; 3 | use crate::shape::HalfSpace; 4 | 5 | use num::Bounded; 6 | 7 | impl HalfSpace { 8 | /// Computes the world-space bounding sphere of this half-space, transformed by `pos`. 9 | #[inline] 10 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 11 | let bv: BoundingSphere = self.local_bounding_sphere(); 12 | bv.transform_by(pos) 13 | } 14 | 15 | /// Computes the local-space bounding sphere of this half-space. 16 | #[inline] 17 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 18 | let radius = Real::max_value(); 19 | 20 | BoundingSphere::new(Point::origin(), radius) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_heightfield.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Real}; 3 | use crate::shape::HeightField; 4 | 5 | impl HeightField { 6 | /// Computes the world-space bounding sphere of this height-field, transformed by `pos`. 7 | #[inline] 8 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 9 | self.local_aabb().bounding_sphere().transform_by(pos) 10 | } 11 | 12 | /// Computes the local-space bounding sphere of this height-field. 13 | #[inline] 14 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 15 | self.local_aabb().bounding_sphere() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_polyline.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Real}; 3 | use crate::shape::Polyline; 4 | 5 | impl Polyline { 6 | /// Computes the world-space bounding sphere of this polyline, transformed by `pos`. 7 | #[inline] 8 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 9 | self.local_aabb().bounding_sphere().transform_by(pos) 10 | } 11 | 12 | /// Computes the local-space bounding sphere of this polyline. 13 | #[inline] 14 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 15 | self.local_aabb().bounding_sphere() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_segment.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume; 2 | use crate::bounding_volume::BoundingSphere; 3 | use crate::math::{Isometry, Real}; 4 | use crate::shape::Segment; 5 | 6 | impl Segment { 7 | /// Computes the world-space bounding sphere of this segment, transformed by `pos`. 8 | #[inline] 9 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 10 | let bv: BoundingSphere = self.local_bounding_sphere(); 11 | bv.transform_by(pos) 12 | } 13 | 14 | /// Computes the local-space bounding sphere of this segment. 15 | #[inline] 16 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 17 | let pts = [self.a, self.b]; 18 | bounding_volume::details::point_cloud_bounding_sphere(&pts[..]) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_triangle.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume; 2 | use crate::bounding_volume::BoundingSphere; 3 | use crate::math::{Isometry, Real}; 4 | use crate::shape::Triangle; 5 | 6 | impl Triangle { 7 | /// Computes the world-space bounding sphere of this triangle, transformed by `pos`. 8 | #[inline] 9 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 10 | let bv: BoundingSphere = self.local_bounding_sphere(); 11 | bv.transform_by(pos) 12 | } 13 | 14 | /// Computes the local-space bounding sphere of this triangle. 15 | #[inline] 16 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 17 | let pts = [self.a, self.b, self.c]; 18 | bounding_volume::details::point_cloud_bounding_sphere(&pts[..]) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_trimesh.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Real}; 3 | use crate::shape::TriMesh; 4 | 5 | impl TriMesh { 6 | /// Computes the world-space bounding sphere of this triangle mesh, transformed by `pos`. 7 | #[inline] 8 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 9 | self.local_aabb().bounding_sphere().transform_by(pos) 10 | } 11 | 12 | /// Computes the local-space bounding sphere of this triangle mesh. 13 | #[inline] 14 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 15 | self.local_aabb().bounding_sphere() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real}; 2 | use crate::utils; 3 | use na::{self, ComplexField}; 4 | 5 | use super::BoundingSphere; 6 | 7 | /// Computes the bounding sphere of a set of point, given its center. 8 | #[inline] 9 | pub fn point_cloud_bounding_sphere_with_center( 10 | pts: &[Point], 11 | center: Point, 12 | ) -> BoundingSphere { 13 | let mut sqradius = 0.0; 14 | 15 | for pt in pts.iter() { 16 | let distance_squared = na::distance_squared(pt, ¢er); 17 | 18 | if distance_squared > sqradius { 19 | sqradius = distance_squared 20 | } 21 | } 22 | BoundingSphere::new(center, ComplexField::sqrt(sqradius)) 23 | } 24 | 25 | /// Computes a bounding sphere of the specified set of point. 26 | #[inline] 27 | pub fn point_cloud_bounding_sphere(pts: &[Point]) -> BoundingSphere { 28 | point_cloud_bounding_sphere_with_center(pts, utils::center(pts)) 29 | } 30 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_sphere_voxels.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Isometry, Real, Translation}; 3 | use crate::shape::{Cuboid, Voxels}; 4 | 5 | impl Voxels { 6 | /// Computes the world-space bounding sphere of this set of voxels, transformed by `pos`. 7 | #[inline] 8 | pub fn bounding_sphere(&self, pos: &Isometry) -> BoundingSphere { 9 | let shift = Translation::from(self.domain_center().coords); 10 | Cuboid::new(self.extents() / 2.0).bounding_sphere(&(pos * shift)) 11 | } 12 | 13 | /// Computes the local-space bounding sphere of this set of voxels. 14 | #[inline] 15 | pub fn local_bounding_sphere(&self) -> BoundingSphere { 16 | Cuboid::new(self.extents() / 2.0) 17 | .local_bounding_sphere() 18 | .translated(&(self.domain_center().coords)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/bounding_volume/bounding_volume.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real}; 2 | 3 | /// Trait of bounding volumes. 4 | /// 5 | /// Bounding volumes are coarse approximations of shapes. It usually have constant time 6 | /// intersection, inclusion test. Two bounding volume must also be mergeable into a bigger bounding 7 | /// volume. 8 | pub trait BoundingVolume { 9 | // TODO: keep that ? What about non-spacial bounding volumes (e.g. bounding cones, curvature 10 | // bounds, etc.) ? 11 | /// Returns a point inside of this bounding volume. This is ideally its center. 12 | fn center(&self) -> Point; 13 | 14 | /// Checks if this bounding volume intersect with another one. 15 | fn intersects(&self, _: &Self) -> bool; 16 | 17 | /// Checks if this bounding volume contains another one. 18 | fn contains(&self, _: &Self) -> bool; 19 | 20 | /// Merges this bounding volume with another one. The merge is done in-place. 21 | fn merge(&mut self, _: &Self); 22 | 23 | /// Merges this bounding volume with another one. 24 | fn merged(&self, _: &Self) -> Self; 25 | 26 | /// Enlarges this bounding volume. 27 | fn loosen(&mut self, _: Real); 28 | 29 | /// Creates a new, enlarged version, of this bounding volume. 30 | fn loosened(&self, _: Real) -> Self; 31 | 32 | /// Tighten this bounding volume. 33 | fn tighten(&mut self, _: Real); 34 | 35 | /// Creates a new, tightened version, of this bounding volume. 36 | fn tightened(&self, _: Real) -> Self; 37 | } 38 | -------------------------------------------------------------------------------- /src/mass_properties/mass_properties_ball.rs: -------------------------------------------------------------------------------- 1 | use crate::mass_properties::MassProperties; 2 | #[cfg(feature = "dim3")] 3 | use crate::math::Vector; 4 | use crate::math::{Point, PrincipalAngularInertia, Real}; 5 | use na::RealField; 6 | 7 | impl MassProperties { 8 | pub(crate) fn ball_volume_unit_angular_inertia( 9 | radius: Real, 10 | ) -> (Real, PrincipalAngularInertia) { 11 | #[cfg(feature = "dim2")] 12 | { 13 | let volume = Real::pi() * radius * radius; 14 | let i = radius * radius / 2.0; 15 | (volume, i) 16 | } 17 | #[cfg(feature = "dim3")] 18 | { 19 | let volume = Real::pi() * radius * radius * radius * 4.0 / 3.0; 20 | let i = radius * radius * 2.0 / 5.0; 21 | 22 | (volume, Vector::repeat(i)) 23 | } 24 | } 25 | 26 | /// Computes the mass properties of a ball. 27 | pub fn from_ball(density: Real, radius: Real) -> Self { 28 | let (vol, unit_i) = Self::ball_volume_unit_angular_inertia(radius); 29 | let mass = vol * density; 30 | Self::new(Point::origin(), mass, unit_i * mass) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/mass_properties/mass_properties_capsule.rs: -------------------------------------------------------------------------------- 1 | use crate::mass_properties::MassProperties; 2 | use crate::math::{Point, Real}; 3 | #[cfg(feature = "dim3")] 4 | use crate::shape::Capsule; 5 | 6 | impl MassProperties { 7 | /// Computes the mass properties of a capsule. 8 | pub fn from_capsule(density: Real, a: Point, b: Point, radius: Real) -> Self { 9 | let half_height = (b - a).norm() / 2.0; 10 | let (cyl_vol, cyl_unit_i) = Self::cylinder_y_volume_unit_inertia(half_height, radius); 11 | let (ball_vol, ball_unit_i) = Self::ball_volume_unit_angular_inertia(radius); 12 | let cap_vol = cyl_vol + ball_vol; 13 | let cap_mass = cap_vol * density; 14 | let mut cap_i = (cyl_unit_i * cyl_vol + ball_unit_i * ball_vol) * density; 15 | let local_com = na::center(&a, &b); 16 | 17 | #[cfg(feature = "dim2")] 18 | { 19 | let h = half_height * 2.0; 20 | let extra = (h * h * 0.25 + h * radius * 3.0 / 8.0) * ball_vol * density; 21 | cap_i += extra; 22 | Self::new(local_com, cap_mass, cap_i) 23 | } 24 | 25 | #[cfg(feature = "dim3")] 26 | { 27 | let h = half_height * 2.0; 28 | let extra = (h * h * 0.25 + h * radius * 3.0 / 8.0) * ball_vol * density; 29 | cap_i.x += extra; 30 | cap_i.z += extra; 31 | let local_frame = Capsule::new(a, b, radius).rotation_wrt_y(); 32 | Self::with_principal_inertia_frame(local_com, cap_mass, cap_i, local_frame) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/mass_properties/mass_properties_compound.rs: -------------------------------------------------------------------------------- 1 | use crate::mass_properties::MassProperties; 2 | use crate::math::{Isometry, Real}; 3 | use crate::shape::SharedShape; 4 | 5 | impl MassProperties { 6 | /// Computes the mass properties of a compound shape. 7 | pub fn from_compound(density: Real, shapes: &[(Isometry, SharedShape)]) -> Self { 8 | shapes 9 | .iter() 10 | .map(|s| s.1.mass_properties(density).transform_by(&s.0)) 11 | .sum() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/mass_properties/mass_properties_cone.rs: -------------------------------------------------------------------------------- 1 | use crate::mass_properties::MassProperties; 2 | use crate::math::{Point, PrincipalAngularInertia, Real, Rotation, Vector}; 3 | use na::RealField; 4 | 5 | impl MassProperties { 6 | pub(crate) fn cone_y_volume_unit_inertia( 7 | half_height: Real, 8 | radius: Real, 9 | ) -> (Real, PrincipalAngularInertia) { 10 | let volume = radius * radius * Real::pi() * half_height * 2.0 / 3.0; 11 | let sq_radius = radius * radius; 12 | let sq_height = half_height * half_height * 4.0; 13 | let off_principal = sq_radius * 3.0 / 20.0 + sq_height * 3.0 / 80.0; 14 | let principal = sq_radius * 3.0 / 10.0; 15 | 16 | (volume, Vector::new(off_principal, principal, off_principal)) 17 | } 18 | 19 | /// Computes the mass properties of a cone. 20 | pub fn from_cone(density: Real, half_height: Real, radius: Real) -> Self { 21 | let (cyl_vol, cyl_unit_i) = Self::cone_y_volume_unit_inertia(half_height, radius); 22 | let cyl_mass = cyl_vol * density; 23 | 24 | Self::with_principal_inertia_frame( 25 | Point::new(0.0, -half_height / 2.0, 0.0), 26 | cyl_mass, 27 | cyl_unit_i * cyl_mass, 28 | Rotation::identity(), 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/mass_properties/mass_properties_convex_polyhedron.rs: -------------------------------------------------------------------------------- 1 | use crate::mass_properties::MassProperties; 2 | use crate::math::{Point, Real, DIM}; 3 | 4 | impl MassProperties { 5 | /// Computes the mass properties of a convex polyhedron. 6 | pub fn from_convex_polyhedron( 7 | density: Real, 8 | vertices: &[Point], 9 | indices: &[[u32; DIM]], 10 | ) -> MassProperties { 11 | Self::from_trimesh(density, vertices, indices) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/mass_properties/mass_properties_cuboid.rs: -------------------------------------------------------------------------------- 1 | use crate::mass_properties::MassProperties; 2 | use crate::math::{Point, PrincipalAngularInertia, Real, Vector}; 3 | 4 | impl MassProperties { 5 | pub(crate) fn cuboid_volume_unit_inertia( 6 | half_extents: Vector, 7 | ) -> (Real, PrincipalAngularInertia) { 8 | #[cfg(feature = "dim2")] 9 | { 10 | let volume = half_extents.x * half_extents.y * 4.0; 11 | let ix = (half_extents.x * half_extents.x) / 3.0; 12 | let iy = (half_extents.y * half_extents.y) / 3.0; 13 | 14 | (volume, ix + iy) 15 | } 16 | 17 | #[cfg(feature = "dim3")] 18 | { 19 | let volume = half_extents.x * half_extents.y * half_extents.z * 8.0; 20 | let ix = (half_extents.x * half_extents.x) / 3.0; 21 | let iy = (half_extents.y * half_extents.y) / 3.0; 22 | let iz = (half_extents.z * half_extents.z) / 3.0; 23 | 24 | (volume, Vector::new(iy + iz, ix + iz, ix + iy)) 25 | } 26 | } 27 | 28 | /// Computes the mass properties of a cuboid. 29 | pub fn from_cuboid(density: Real, half_extents: Vector) -> Self { 30 | let (vol, unit_i) = Self::cuboid_volume_unit_inertia(half_extents); 31 | let mass = vol * density; 32 | Self::new(Point::origin(), mass, unit_i * mass) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/mass_properties/mass_properties_cylinder.rs: -------------------------------------------------------------------------------- 1 | use crate::mass_properties::MassProperties; 2 | use crate::math::{PrincipalAngularInertia, Real, Vector}; 3 | #[cfg(feature = "dim3")] 4 | use { 5 | crate::math::{Point, Rotation}, 6 | na::RealField, 7 | }; 8 | 9 | impl MassProperties { 10 | pub(crate) fn cylinder_y_volume_unit_inertia( 11 | half_height: Real, 12 | radius: Real, 13 | ) -> (Real, PrincipalAngularInertia) { 14 | #[cfg(feature = "dim2")] 15 | { 16 | Self::cuboid_volume_unit_inertia(Vector::new(radius, half_height)) 17 | } 18 | 19 | #[cfg(feature = "dim3")] 20 | { 21 | let volume = half_height * radius * radius * Real::pi() * 2.0; 22 | let sq_radius = radius * radius; 23 | let sq_height = half_height * half_height * 4.0; 24 | let off_principal = (sq_radius * 3.0 + sq_height) / 12.0; 25 | 26 | let inertia = Vector::new(off_principal, sq_radius / 2.0, off_principal); 27 | (volume, inertia) 28 | } 29 | } 30 | 31 | /// Computes the mass properties of a cylinder. 32 | #[cfg(feature = "dim3")] 33 | pub fn from_cylinder(density: Real, half_height: Real, radius: Real) -> Self { 34 | let (cyl_vol, cyl_unit_i) = Self::cylinder_y_volume_unit_inertia(half_height, radius); 35 | let cyl_mass = cyl_vol * density; 36 | 37 | Self::with_principal_inertia_frame( 38 | Point::origin(), 39 | cyl_mass, 40 | cyl_unit_i * cyl_mass, 41 | Rotation::identity(), 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/mass_properties/mass_properties_triangle.rs: -------------------------------------------------------------------------------- 1 | use crate::mass_properties::MassProperties; 2 | use crate::math::{Point, Real}; 3 | use crate::shape::Triangle; 4 | 5 | impl MassProperties { 6 | /// Computes the mass properties of a triangle. 7 | pub fn from_triangle( 8 | density: Real, 9 | a: &Point, 10 | b: &Point, 11 | c: &Point, 12 | ) -> MassProperties { 13 | let triangle = Triangle::new(*a, *b, *c); 14 | let area = triangle.area(); 15 | let com = triangle.center(); 16 | 17 | if area == 0.0 { 18 | return MassProperties::new(com, 0.0, 0.0); 19 | } 20 | 21 | let ipart = triangle.unit_angular_inertia(); 22 | 23 | Self::new(com, area * density, ipart * area * density) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/mass_properties/mass_properties_voxels.rs: -------------------------------------------------------------------------------- 1 | use crate::mass_properties::MassProperties; 2 | use crate::math::{Point, Real}; 3 | use crate::shape::Voxels; 4 | 5 | impl MassProperties { 6 | /// Computes the mass properties of a set of voxels. 7 | pub fn from_voxels(density: Real, voxels: &Voxels) -> Self { 8 | let mut com = Point::origin(); 9 | let mut num_not_empty = 0; 10 | let mut angular_inertia = na::zero(); 11 | let block_ref_mprops = MassProperties::from_cuboid(density, voxels.voxel_size() / 2.0); 12 | 13 | for vox in voxels.voxels() { 14 | if !vox.state.is_empty() { 15 | com += vox.center.coords; 16 | num_not_empty += 1; 17 | } 18 | } 19 | 20 | com.coords /= num_not_empty as Real; 21 | 22 | for vox in voxels.voxels() { 23 | if !vox.state.is_empty() { 24 | angular_inertia += 25 | block_ref_mprops.construct_shifted_inertia_matrix(vox.center - com); 26 | } 27 | } 28 | 29 | let mass = block_ref_mprops.mass() * num_not_empty as Real; 30 | 31 | #[cfg(feature = "dim2")] 32 | return Self::new(com, mass, angular_inertia); 33 | #[cfg(feature = "dim3")] 34 | return Self::with_inertia_matrix(com, mass, angular_inertia); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/mass_properties/mod.rs: -------------------------------------------------------------------------------- 1 | //! Mass properties (mass, inertia, center-of-mass) of shapes. 2 | 3 | pub use self::mass_properties::MassProperties; 4 | 5 | mod mass_properties; 6 | mod mass_properties_ball; 7 | mod mass_properties_capsule; 8 | #[cfg(feature = "alloc")] 9 | mod mass_properties_compound; 10 | #[cfg(feature = "dim3")] 11 | mod mass_properties_cone; 12 | #[cfg(feature = "dim2")] 13 | #[cfg(feature = "alloc")] 14 | mod mass_properties_convex_polygon; 15 | #[cfg(feature = "dim3")] 16 | #[cfg(feature = "alloc")] 17 | mod mass_properties_convex_polyhedron; 18 | mod mass_properties_cuboid; 19 | mod mass_properties_cylinder; 20 | #[cfg(feature = "dim2")] 21 | mod mass_properties_triangle; 22 | #[cfg(feature = "dim2")] 23 | #[cfg(feature = "alloc")] 24 | mod mass_properties_trimesh2d; 25 | #[cfg(feature = "dim3")] 26 | #[cfg(feature = "alloc")] 27 | mod mass_properties_trimesh3d; 28 | 29 | #[cfg(feature = "alloc")] 30 | mod mass_properties_voxels; 31 | 32 | /// Free functions for some special-cases of mass-properties computation. 33 | pub mod details { 34 | #[cfg(feature = "dim2")] 35 | #[cfg(feature = "alloc")] 36 | pub use super::mass_properties_convex_polygon::convex_polygon_area_and_center_of_mass; 37 | #[cfg(feature = "dim2")] 38 | #[cfg(feature = "alloc")] 39 | pub use super::mass_properties_trimesh2d::trimesh_area_and_center_of_mass; 40 | #[cfg(feature = "dim3")] 41 | #[cfg(feature = "alloc")] 42 | pub use super::mass_properties_trimesh3d::{ 43 | tetrahedron_unit_inertia_tensor_wrt_point, trimesh_signed_volume_and_center_of_mass, 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/partitioning/mod.rs: -------------------------------------------------------------------------------- 1 | //! Spatial partitioning tools. 2 | 3 | #[cfg(feature = "alloc")] 4 | pub use self::qbvh::{ 5 | CenterDataSplitter, IndexedData, NodeIndex, Qbvh, QbvhDataGenerator, QbvhNode, 6 | QbvhNonOverlappingDataSplitter, QbvhProxy, QbvhUpdateWorkspace, SimdNodeIndex, 7 | }; 8 | #[cfg(feature = "parallel")] 9 | pub use self::visitor::{ParallelSimdSimultaneousVisitor, ParallelSimdVisitor}; 10 | pub use self::visitor::{ 11 | SimdBestFirstVisitStatus, SimdBestFirstVisitor, SimdSimultaneousVisitStatus, 12 | SimdSimultaneousVisitor, SimdVisitStatus, SimdVisitor, SimdVisitorWithContext, 13 | }; 14 | 15 | /// A quaternary bounding-volume-hierarchy. 16 | #[deprecated(note = "Renamed to Qbvh")] 17 | #[cfg(feature = "alloc")] 18 | pub type SimdQbvh = Qbvh; 19 | 20 | #[cfg(feature = "alloc")] 21 | mod qbvh; 22 | mod visitor; 23 | -------------------------------------------------------------------------------- /src/partitioning/qbvh/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::{ 2 | build::{CenterDataSplitter, QbvhDataGenerator, QbvhNonOverlappingDataSplitter}, 3 | update::QbvhUpdateWorkspace, 4 | }; 5 | 6 | pub use self::qbvh::{ 7 | IndexedData, NodeIndex, Qbvh, QbvhNode, QbvhNodeFlags, QbvhProxy, SimdNodeIndex, 8 | }; 9 | 10 | mod qbvh; 11 | mod utils; 12 | 13 | mod build; 14 | mod traversal; 15 | mod update; 16 | -------------------------------------------------------------------------------- /src/partitioning/qbvh/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Point, Real}; 3 | 4 | pub fn split_indices_wrt_dim<'a>( 5 | indices: &'a mut [usize], 6 | aabbs: &[Aabb], 7 | split_point: &Point, 8 | dim: usize, 9 | enable_fallback_split: bool, 10 | ) -> (&'a mut [usize], &'a mut [usize]) { 11 | let mut icurr = 0; 12 | let mut ilast = indices.len(); 13 | 14 | // The loop condition we can just do 0..indices.len() 15 | // instead of the test icurr < ilast because we know 16 | // we will iterate exactly once per index. 17 | for _ in 0..indices.len() { 18 | let i = indices[icurr]; 19 | let center = aabbs[i].center(); 20 | 21 | if center[dim] > split_point[dim] { 22 | ilast -= 1; 23 | indices.swap(icurr, ilast); 24 | } else { 25 | icurr += 1; 26 | } 27 | } 28 | 29 | if enable_fallback_split && (icurr == 0 || icurr == indices.len()) { 30 | // We don't want to return one empty set. But 31 | // this can happen if all the coordinates along the 32 | // given dimension are equal. 33 | // In this is the case, we just split in the middle. 34 | let half = indices.len() / 2; 35 | indices.split_at_mut(half) 36 | } else { 37 | indices.split_at_mut(icurr) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/query/clip/clip_aabb_polygon.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Point, Real, Vector}; 3 | use alloc::vec::Vec; 4 | 5 | impl Aabb { 6 | /// Computes the intersections between this Aabb and the given polygon. 7 | /// 8 | /// The results is written into `points` directly. The input points are 9 | /// assumed to form a convex polygon where all points lie on the same plane. 10 | /// In order to avoid internal allocations, uses `self.clip_polygon_with_workspace` 11 | /// instead. 12 | #[inline] 13 | pub fn clip_polygon(&self, points: &mut Vec>) { 14 | let mut workspace = Vec::new(); 15 | self.clip_polygon_with_workspace(points, &mut workspace) 16 | } 17 | 18 | /// Computes the intersections between this Aabb and the given polygon. 19 | /// 20 | /// The results is written into `points` directly. The input points are 21 | /// assumed to form a convex polygon where all points lie on the same plane. 22 | #[inline] 23 | pub fn clip_polygon_with_workspace( 24 | &self, 25 | points: &mut Vec>, 26 | workspace: &mut Vec>, 27 | ) { 28 | super::clip_halfspace_polygon(&self.mins, &-Vector::x(), points, workspace); 29 | super::clip_halfspace_polygon(&self.maxs, &Vector::x(), workspace, points); 30 | 31 | super::clip_halfspace_polygon(&self.mins, &-Vector::y(), points, workspace); 32 | super::clip_halfspace_polygon(&self.maxs, &Vector::y(), workspace, points); 33 | 34 | #[cfg(feature = "dim3")] 35 | { 36 | super::clip_halfspace_polygon(&self.mins, &-Vector::z(), points, workspace); 37 | super::clip_halfspace_polygon(&self.maxs, &Vector::z(), workspace, points); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/query/clip/clip_halfspace_polygon.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real, Vector}; 2 | use crate::query::{self, Ray}; 3 | use alloc::vec::Vec; 4 | 5 | /// Cuts a polygon with the given half-space. 6 | /// 7 | /// Given the half-space `center` and outward `normal`, 8 | /// this computes the intersecting between the half-space and 9 | /// the polygon. (Note that a point `pt` is considered as inside of 10 | /// the half-space if `normal.dot(&(pt - center)) <= 0.0`. 11 | pub fn clip_halfspace_polygon( 12 | center: &Point, 13 | normal: &Vector, 14 | polygon: &[Point], 15 | result: &mut Vec>, 16 | ) { 17 | result.clear(); 18 | 19 | if polygon.is_empty() { 20 | return; 21 | } 22 | 23 | let keep_point = |pt: &Point| (pt - center).dot(normal) <= 0.0; 24 | let last_pt = polygon.last().unwrap(); 25 | let mut last_keep = keep_point(last_pt); 26 | 27 | if last_keep { 28 | result.push(*last_pt); 29 | } 30 | 31 | for i in 0..polygon.len() { 32 | let pt = &polygon[i]; 33 | let keep = keep_point(pt); 34 | 35 | if keep != last_keep { 36 | // We crossed the plane, so we need 37 | // to cut the edge. 38 | let prev_i = if i == 0 { polygon.len() - 1 } else { i - 1 }; 39 | let prev_pt = &polygon[prev_i]; 40 | let ray = Ray::new(*prev_pt, pt - prev_pt); 41 | 42 | if let Some(time_of_impact) = 43 | query::details::ray_toi_with_halfspace(center, normal, &ray) 44 | { 45 | if time_of_impact > 0.0 && time_of_impact < 1.0 { 46 | result.push(ray.origin + ray.dir * time_of_impact) 47 | } 48 | } 49 | 50 | last_keep = keep; 51 | } 52 | 53 | if keep && i != polygon.len() - 1 { 54 | result.push(*pt); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/query/clip/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::clip_aabb_line::clip_aabb_line; 2 | #[cfg(feature = "alloc")] 3 | pub use self::clip_halfspace_polygon::clip_halfspace_polygon; 4 | pub use self::clip_segment_segment::clip_segment_segment; 5 | #[cfg(feature = "dim2")] 6 | pub use self::clip_segment_segment::clip_segment_segment_with_normal; 7 | 8 | mod clip_aabb_line; 9 | #[cfg(feature = "alloc")] 10 | mod clip_aabb_polygon; 11 | #[cfg(feature = "alloc")] 12 | mod clip_halfspace_polygon; 13 | mod clip_segment_segment; 14 | -------------------------------------------------------------------------------- /src/query/closest_points/closest_points.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Point, Real}; 2 | 3 | use core::mem; 4 | 5 | /// Closest points information. 6 | #[derive(Debug, PartialEq, Clone, Copy)] 7 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 8 | #[cfg_attr( 9 | feature = "rkyv", 10 | derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize), 11 | archive(check_bytes) 12 | )] 13 | pub enum ClosestPoints { 14 | /// The two objects are intersecting. 15 | Intersecting, 16 | /// The two objects are non-intersecting but closer than a given user-defined distance. 17 | WithinMargin(Point, Point), 18 | /// The two objects are non-intersecting and further than a given user-defined distance. 19 | Disjoint, 20 | } 21 | 22 | impl ClosestPoints { 23 | /// Swaps the two points. 24 | pub fn flip(&mut self) { 25 | if let ClosestPoints::WithinMargin(ref mut p1, ref mut p2) = *self { 26 | mem::swap(p1, p2) 27 | } 28 | } 29 | 30 | /// Returns the result of swapping the two points if `self` is `WithinMargin`. 31 | #[must_use] 32 | pub fn flipped(&self) -> Self { 33 | if let ClosestPoints::WithinMargin(p1, p2) = *self { 34 | ClosestPoints::WithinMargin(p2, p1) 35 | } else { 36 | *self 37 | } 38 | } 39 | 40 | /// Transform the points in `self` by `pos1` and `pos2`. 41 | #[must_use] 42 | pub fn transform_by(self, pos1: &Isometry, pos2: &Isometry) -> Self { 43 | if let ClosestPoints::WithinMargin(p1, p2) = self { 44 | ClosestPoints::WithinMargin(pos1 * p1, pos2 * p2) 45 | } else { 46 | self 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/query/closest_points/closest_points_ball_ball.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Point, Real}; 2 | use crate::query::ClosestPoints; 3 | use crate::shape::Ball; 4 | 5 | /// Closest points between balls. 6 | /// 7 | /// Each returned point is expressed on the local-space of the corresponding shape. 8 | #[inline] 9 | pub fn closest_points_ball_ball( 10 | pos12: &Isometry, 11 | b1: &Ball, 12 | b2: &Ball, 13 | margin: Real, 14 | ) -> ClosestPoints { 15 | assert!( 16 | margin >= 0.0, 17 | "The proximity margin must be positive or null." 18 | ); 19 | 20 | let r1 = b1.radius; 21 | let r2 = b2.radius; 22 | let delta_pos = pos12.translation.vector; 23 | let distance = delta_pos.norm(); 24 | let sum_radius = r1 + r2; 25 | 26 | if distance - margin <= sum_radius { 27 | if distance <= sum_radius { 28 | ClosestPoints::Intersecting 29 | } else { 30 | let normal = delta_pos.normalize(); 31 | let p1 = Point::from(normal * r1); 32 | let p2 = Point::from(pos12.inverse_transform_vector(&normal) * -r2); 33 | ClosestPoints::WithinMargin(p1, p2) 34 | } 35 | } else { 36 | ClosestPoints::Disjoint 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/query/closest_points/closest_points_ball_convex_polyhedron.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::query::ClosestPoints; 3 | use crate::shape::{Ball, Shape}; 4 | 5 | /// ClosestPoints between a ball and a convex polyhedron. 6 | /// 7 | /// This function panics if the input shape does not implement 8 | /// both the ConvexPolyhedron and PointQuery traits. 9 | #[inline] 10 | pub fn closest_points_ball_convex_polyhedron( 11 | pos12: &Isometry, 12 | ball1: &Ball, 13 | shape2: &(impl Shape + ?Sized), 14 | prediction: Real, 15 | ) -> ClosestPoints { 16 | match crate::query::details::contact_ball_convex_polyhedron(pos12, ball1, shape2, prediction) { 17 | Some(contact) => { 18 | if contact.dist <= 0.0 { 19 | ClosestPoints::Intersecting 20 | } else { 21 | ClosestPoints::WithinMargin(contact.point1, contact.point2) 22 | } 23 | } 24 | None => ClosestPoints::Disjoint, 25 | } 26 | } 27 | 28 | /// ClosestPoints between a convex polyhedron and a ball. 29 | /// 30 | /// This function panics if the input shape does not implement 31 | /// both the ConvexPolyhedron and PointQuery traits. 32 | #[inline] 33 | pub fn closest_points_convex_polyhedron_ball( 34 | pos12: &Isometry, 35 | shape1: &(impl Shape + ?Sized), 36 | ball2: &Ball, 37 | prediction: Real, 38 | ) -> ClosestPoints { 39 | match crate::query::details::contact_convex_polyhedron_ball(pos12, shape1, ball2, prediction) { 40 | Some(contact) => { 41 | if contact.dist <= 0.0 { 42 | ClosestPoints::Intersecting 43 | } else { 44 | ClosestPoints::WithinMargin(contact.point1, contact.point2) 45 | } 46 | } 47 | None => ClosestPoints::Disjoint, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/query/closest_points/closest_points_halfspace_support_map.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::query::ClosestPoints; 3 | use crate::shape::HalfSpace; 4 | use crate::shape::SupportMap; 5 | 6 | /// Closest points between a halfspace and a support-mapped shape (Cuboid, ConvexHull, etc.) 7 | pub fn closest_points_halfspace_support_map( 8 | pos12: &Isometry, 9 | halfspace: &HalfSpace, 10 | other: &G, 11 | margin: Real, 12 | ) -> ClosestPoints { 13 | assert!( 14 | margin >= 0.0, 15 | "The proximity margin must be positive or null." 16 | ); 17 | 18 | let deepest = other.support_point(pos12, &-halfspace.normal); 19 | let distance = halfspace.normal.dot(&(-deepest.coords)); 20 | 21 | if distance >= -margin { 22 | if distance >= 0.0 { 23 | ClosestPoints::Intersecting 24 | } else { 25 | let p1 = deepest + *halfspace.normal * distance; 26 | let p2 = pos12.inverse_transform_point(&deepest); 27 | ClosestPoints::WithinMargin(p1, p2) 28 | } 29 | } else { 30 | ClosestPoints::Disjoint 31 | } 32 | } 33 | 34 | /// Closest points between a support-mapped shape (Cuboid, ConvexHull, etc.) and a halfspace. 35 | pub fn closest_points_support_map_halfspace( 36 | pos12: &Isometry, 37 | other: &G, 38 | halfspace: &HalfSpace, 39 | margin: Real, 40 | ) -> ClosestPoints { 41 | closest_points_halfspace_support_map(&pos12.inverse(), halfspace, other, margin).flipped() 42 | } 43 | -------------------------------------------------------------------------------- /src/query/closest_points/closest_points_shape_shape.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::query::{ClosestPoints, DefaultQueryDispatcher, QueryDispatcher, Unsupported}; 3 | use crate::shape::Shape; 4 | 5 | /// Computes the pair of closest points between two shapes. 6 | /// 7 | /// Returns `ClosestPoints::Disjoint` if the objects are separated by a distance greater than `max_dist`. 8 | /// The result points in `ClosestPoints::WithinMargin` are expressed in world-space. 9 | pub fn closest_points( 10 | pos1: &Isometry, 11 | g1: &dyn Shape, 12 | pos2: &Isometry, 13 | g2: &dyn Shape, 14 | max_dist: Real, 15 | ) -> Result { 16 | let pos12 = pos1.inv_mul(pos2); 17 | DefaultQueryDispatcher 18 | .closest_points(&pos12, g1, g2, max_dist) 19 | .map(|res| res.transform_by(pos1, pos2)) 20 | } 21 | -------------------------------------------------------------------------------- /src/query/contact/contact_ball_ball.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Point, Real, Vector}; 2 | use crate::query::Contact; 3 | use crate::shape::Ball; 4 | use na::{self, ComplexField, Unit}; 5 | use num::Zero; 6 | 7 | /// Contact between balls. 8 | #[inline] 9 | pub fn contact_ball_ball( 10 | pos12: &Isometry, 11 | b1: &Ball, 12 | b2: &Ball, 13 | prediction: Real, 14 | ) -> Option { 15 | let r1 = b1.radius; 16 | let r2 = b2.radius; 17 | let center2_1 = pos12.translation.vector; 18 | let distance_squared = center2_1.norm_squared(); 19 | let sum_radius = r1 + r2; 20 | let sum_radius_with_error = sum_radius + prediction; 21 | 22 | if distance_squared < sum_radius_with_error * sum_radius_with_error { 23 | let normal1 = if !distance_squared.is_zero() { 24 | Unit::new_normalize(center2_1) 25 | } else { 26 | Vector::x_axis() 27 | }; 28 | let normal2 = -pos12.inverse_transform_unit_vector(&normal1); 29 | let point1 = Point::from(*normal1 * r1); 30 | let point2 = Point::from(*normal2 * r2); 31 | 32 | Some(Contact::new( 33 | point1, 34 | point2, 35 | normal1, 36 | normal2, 37 | ComplexField::sqrt(distance_squared) - sum_radius, 38 | )) 39 | } else { 40 | None 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/query/contact/contact_halfspace_support_map.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::query::Contact; 3 | use crate::shape::{HalfSpace, SupportMap}; 4 | 5 | /// Contact between a halfspace and a support-mapped shape (Cuboid, ConvexHull, etc.) 6 | pub fn contact_halfspace_support_map( 7 | pos12: &Isometry, 8 | halfspace: &HalfSpace, 9 | other: &G, 10 | prediction: Real, 11 | ) -> Option { 12 | let deepest = other.support_point_toward(pos12, &-halfspace.normal); 13 | let distance = halfspace.normal.dot(&deepest.coords); 14 | 15 | if distance <= prediction { 16 | let point1 = deepest - halfspace.normal.into_inner() * distance; 17 | let point2 = pos12.inverse_transform_point(&deepest); 18 | let normal2 = pos12.inverse_transform_unit_vector(&-halfspace.normal); 19 | 20 | Some(Contact::new( 21 | point1, 22 | point2, 23 | halfspace.normal, 24 | normal2, 25 | distance, 26 | )) 27 | } else { 28 | None 29 | } 30 | } 31 | 32 | /// Contact between a support-mapped shape (Cuboid, ConvexHull, etc.) and a halfspace. 33 | pub fn contact_support_map_halfspace( 34 | pos12: &Isometry, 35 | other: &G, 36 | halfspace: &HalfSpace, 37 | prediction: Real, 38 | ) -> Option { 39 | contact_halfspace_support_map(pos12, halfspace, other, prediction).map(|c| c.flipped()) 40 | } 41 | -------------------------------------------------------------------------------- /src/query/contact/contact_shape_shape.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::query::{Contact, DefaultQueryDispatcher, QueryDispatcher, Unsupported}; 3 | use crate::shape::Shape; 4 | 5 | /// Computes one pair of contact points point between two shapes. 6 | /// 7 | /// Returns `None` if the objects are separated by a distance greater than `prediction`. 8 | /// The result is given in world-space. 9 | pub fn contact( 10 | pos1: &Isometry, 11 | g1: &dyn Shape, 12 | pos2: &Isometry, 13 | g2: &dyn Shape, 14 | prediction: Real, 15 | ) -> Result, Unsupported> { 16 | let pos12 = pos1.inv_mul(pos2); 17 | let mut result = DefaultQueryDispatcher.contact(&pos12, g1, g2, prediction); 18 | 19 | if let Ok(Some(contact)) = &mut result { 20 | contact.transform_by_mut(pos1, pos2); 21 | } 22 | 23 | result 24 | } 25 | -------------------------------------------------------------------------------- /src/query/contact/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementation details of the `contact` and `contacts` functions. 2 | 3 | pub use self::contact::Contact; 4 | pub use self::contact_ball_ball::contact_ball_ball; 5 | pub use self::contact_ball_convex_polyhedron::{ 6 | contact_ball_convex_polyhedron, contact_convex_polyhedron_ball, 7 | }; 8 | #[cfg(feature = "alloc")] 9 | pub use self::contact_composite_shape_shape::{ 10 | contact_composite_shape_shape, contact_shape_composite_shape, 11 | }; 12 | pub use self::contact_cuboid_cuboid::contact_cuboid_cuboid; 13 | pub use self::contact_halfspace_support_map::{ 14 | contact_halfspace_support_map, contact_support_map_halfspace, 15 | }; 16 | pub use self::contact_shape_shape::contact; 17 | #[cfg(feature = "alloc")] 18 | pub use self::contact_support_map_support_map::{ 19 | contact_support_map_support_map, contact_support_map_support_map_with_params, 20 | }; 21 | 22 | mod contact; 23 | mod contact_ball_ball; 24 | mod contact_ball_convex_polyhedron; 25 | #[cfg(feature = "alloc")] 26 | mod contact_composite_shape_shape; 27 | mod contact_cuboid_cuboid; 28 | mod contact_halfspace_support_map; 29 | mod contact_shape_shape; 30 | #[cfg(feature = "alloc")] 31 | mod contact_support_map_support_map; 32 | -------------------------------------------------------------------------------- /src/query/distance/distance.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | 3 | use crate::query::{DefaultQueryDispatcher, QueryDispatcher, Unsupported}; 4 | use crate::shape::Shape; 5 | 6 | /// Computes the minimum distance separating two shapes. 7 | /// 8 | /// Returns `0.0` if the objects are touching or penetrating. 9 | pub fn distance( 10 | pos1: &Isometry, 11 | g1: &dyn Shape, 12 | pos2: &Isometry, 13 | g2: &dyn Shape, 14 | ) -> Result { 15 | let pos12 = pos1.inv_mul(pos2); 16 | DefaultQueryDispatcher.distance(&pos12, g1, g2) 17 | } 18 | -------------------------------------------------------------------------------- /src/query/distance/distance_ball_ball.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real}; 2 | use crate::shape::Ball; 3 | use na::{self, ComplexField}; 4 | 5 | /// Distance between balls. 6 | #[inline] 7 | pub fn distance_ball_ball(b1: &Ball, center2: &Point, b2: &Ball) -> Real { 8 | let r1 = b1.radius; 9 | let r2 = b2.radius; 10 | let distance_squared = center2.coords.norm_squared(); 11 | let sum_radius = r1 + r2; 12 | 13 | if distance_squared <= sum_radius * sum_radius { 14 | 0.0 15 | } else { 16 | ComplexField::sqrt(distance_squared) - sum_radius 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/query/distance/distance_ball_convex_polyhedron.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Point, Real}; 2 | use crate::shape::{Ball, Shape}; 3 | 4 | /// Distance between a ball and a convex polyhedron. 5 | /// 6 | /// This function panics if the input shape does not implement 7 | /// both the ConvexPolyhedron and PointQuery traits. 8 | #[inline] 9 | pub fn distance_ball_convex_polyhedron( 10 | pos12: &Isometry, 11 | ball1: &Ball, 12 | shape2: &(impl Shape + ?Sized), 13 | ) -> Real { 14 | distance_convex_polyhedron_ball(&pos12.inverse(), shape2, ball1) 15 | } 16 | 17 | /// Distance between a convex polyhedron and a ball. 18 | /// 19 | /// This function panics if the input shape does not implement 20 | /// both the ConvexPolyhedron and PointQuery traits. 21 | #[inline] 22 | pub fn distance_convex_polyhedron_ball( 23 | pos12: &Isometry, 24 | shape1: &(impl Shape + ?Sized), 25 | ball2: &Ball, 26 | ) -> Real { 27 | let center2_1 = Point::from(pos12.translation.vector); 28 | let proj = shape1.project_local_point(¢er2_1, true); 29 | (na::distance(&proj.point, ¢er2_1) - ball2.radius).max(0.0) 30 | } 31 | -------------------------------------------------------------------------------- /src/query/distance/distance_cuboid_cuboid.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::query::ClosestPoints; 3 | use crate::shape::Cuboid; 4 | 5 | /// Distance between two cuboids. 6 | #[inline] 7 | pub fn distance_cuboid_cuboid(pos12: &Isometry, cuboid1: &Cuboid, cuboid2: &Cuboid) -> Real { 8 | match crate::query::details::closest_points_cuboid_cuboid(pos12, cuboid1, cuboid2, Real::MAX) { 9 | ClosestPoints::WithinMargin(p1, p2) => na::distance(&p1, &(pos12 * p2)), 10 | _ => 0.0, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/query/distance/distance_halfspace_support_map.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::shape::HalfSpace; 3 | use crate::shape::SupportMap; 4 | use na; 5 | 6 | /// Distance between a halfspace and a support-mapped shape. 7 | pub fn distance_halfspace_support_map( 8 | pos12: &Isometry, 9 | halfspace: &HalfSpace, 10 | other: &G, 11 | ) -> Real { 12 | let deepest = other.support_point_toward(pos12, &-halfspace.normal); 13 | halfspace.normal.dot(&deepest.coords).max(na::zero()) 14 | } 15 | 16 | /// Distance between a support-mapped shape and a halfspace. 17 | pub fn distance_support_map_halfspace( 18 | pos12: &Isometry, 19 | other: &G, 20 | halfspace: &HalfSpace, 21 | ) -> Real { 22 | distance_halfspace_support_map(&pos12.inverse(), halfspace, other) 23 | } 24 | -------------------------------------------------------------------------------- /src/query/distance/distance_segment_segment.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::query::ClosestPoints; 3 | use crate::shape::Segment; 4 | 5 | /// Distance between two segments. 6 | #[inline] 7 | pub fn distance_segment_segment( 8 | pos12: &Isometry, 9 | segment1: &Segment, 10 | segment2: &Segment, 11 | ) -> Real { 12 | match crate::query::details::closest_points_segment_segment( 13 | pos12, 14 | segment1, 15 | segment2, 16 | Real::MAX, 17 | ) { 18 | ClosestPoints::WithinMargin(p1, p2) => na::distance(&p1, &(pos12 * p2)), 19 | _ => 0.0, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/query/distance/distance_support_map_support_map.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real, Vector}; 2 | use crate::query::gjk::{self, CSOPoint, GJKResult, VoronoiSimplex}; 3 | use crate::shape::SupportMap; 4 | 5 | use na::{self, Unit}; 6 | use num::Bounded; 7 | 8 | /// Distance between support-mapped shapes. 9 | pub fn distance_support_map_support_map(pos12: &Isometry, g1: &G1, g2: &G2) -> Real 10 | where 11 | G1: ?Sized + SupportMap, 12 | G2: ?Sized + SupportMap, 13 | { 14 | distance_support_map_support_map_with_params(pos12, g1, g2, &mut VoronoiSimplex::new(), None) 15 | } 16 | 17 | /// Distance between support-mapped shapes. 18 | /// 19 | /// This allows a more fine grained control other the underlying GJK algorigtm. 20 | pub fn distance_support_map_support_map_with_params( 21 | pos12: &Isometry, 22 | g1: &G1, 23 | g2: &G2, 24 | simplex: &mut VoronoiSimplex, 25 | init_dir: Option>, 26 | ) -> Real 27 | where 28 | G1: ?Sized + SupportMap, 29 | G2: ?Sized + SupportMap, 30 | { 31 | // TODO: or m2.translation - m1.translation ? 32 | let dir = init_dir.unwrap_or_else(|| -pos12.translation.vector); 33 | 34 | if let Some(dir) = Unit::try_new(dir, crate::math::DEFAULT_EPSILON) { 35 | simplex.reset(CSOPoint::from_shapes(pos12, g1, g2, &dir)); 36 | } else { 37 | simplex.reset(CSOPoint::from_shapes( 38 | pos12, 39 | g1, 40 | g2, 41 | &Vector::::x_axis(), 42 | )); 43 | } 44 | 45 | match gjk::closest_points(pos12, g1, g2, Real::max_value(), true, simplex) { 46 | GJKResult::Intersection => 0.0, 47 | GJKResult::ClosestPoints(p1, p2, _) => na::distance(&p1, &p2), 48 | GJKResult::Proximity(_) => unreachable!(), 49 | GJKResult::NoIntersection(_) => 0.0, // TODO: GJK did not converge. 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/query/distance/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementation details of the `distance` function. 2 | 3 | pub use self::distance::distance; 4 | pub use self::distance_ball_ball::distance_ball_ball; 5 | pub use self::distance_ball_convex_polyhedron::{ 6 | distance_ball_convex_polyhedron, distance_convex_polyhedron_ball, 7 | }; 8 | #[cfg(feature = "alloc")] 9 | pub use self::distance_composite_shape_shape::{ 10 | distance_composite_shape_shape, distance_shape_composite_shape, 11 | CompositeShapeAgainstAnyDistanceVisitor, 12 | }; 13 | pub use self::distance_cuboid_cuboid::distance_cuboid_cuboid; 14 | pub use self::distance_halfspace_support_map::{ 15 | distance_halfspace_support_map, distance_support_map_halfspace, 16 | }; 17 | pub use self::distance_segment_segment::distance_segment_segment; 18 | pub use self::distance_support_map_support_map::{ 19 | distance_support_map_support_map, distance_support_map_support_map_with_params, 20 | }; 21 | 22 | mod distance; 23 | mod distance_ball_ball; 24 | mod distance_ball_convex_polyhedron; 25 | #[cfg(feature = "alloc")] 26 | mod distance_composite_shape_shape; 27 | mod distance_cuboid_cuboid; 28 | mod distance_halfspace_support_map; 29 | mod distance_segment_segment; 30 | mod distance_support_map_support_map; 31 | -------------------------------------------------------------------------------- /src/query/epa/mod.rs: -------------------------------------------------------------------------------- 1 | //! The EPA algorithm for penetration depth computation. 2 | //! 3 | #[cfg(feature = "dim2")] 4 | pub use self::epa2::EPA; 5 | #[cfg(feature = "dim3")] 6 | pub use self::epa3::EPA; 7 | 8 | #[cfg(feature = "dim2")] 9 | pub mod epa2; 10 | #[cfg(feature = "dim3")] 11 | pub mod epa3; 12 | -------------------------------------------------------------------------------- /src/query/error.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | /// Error indicating that a query is not supported between certain shapes 4 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 5 | pub struct Unsupported; 6 | 7 | impl fmt::Display for Unsupported { 8 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 9 | f.pad("query not supported between these shapes") 10 | } 11 | } 12 | 13 | #[cfg(feature = "alloc")] 14 | impl core::error::Error for Unsupported {} 15 | -------------------------------------------------------------------------------- /src/query/gjk/mod.rs: -------------------------------------------------------------------------------- 1 | //! The GJK algorithm for distance computation. 2 | 3 | pub use self::cso_point::CSOPoint; 4 | #[cfg(feature = "dim2")] 5 | pub use self::voronoi_simplex2::VoronoiSimplex; 6 | #[cfg(feature = "dim3")] 7 | pub use self::voronoi_simplex3::VoronoiSimplex; 8 | pub use gjk::*; 9 | pub use special_support_maps::*; 10 | 11 | mod cso_point; 12 | mod gjk; 13 | mod special_support_maps; 14 | #[cfg(feature = "dim2")] 15 | mod voronoi_simplex2; 16 | #[cfg(feature = "dim3")] 17 | mod voronoi_simplex3; 18 | -------------------------------------------------------------------------------- /src/query/intersection_test/intersection_test.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::query::{DefaultQueryDispatcher, QueryDispatcher, Unsupported}; 3 | use crate::shape::Shape; 4 | 5 | /// Tests whether two shapes are intersecting. 6 | pub fn intersection_test( 7 | pos1: &Isometry, 8 | g1: &dyn Shape, 9 | pos2: &Isometry, 10 | g2: &dyn Shape, 11 | ) -> Result { 12 | let pos12 = pos1.inv_mul(pos2); 13 | DefaultQueryDispatcher.intersection_test(&pos12, g1, g2) 14 | } 15 | -------------------------------------------------------------------------------- /src/query/intersection_test/intersection_test_ball_ball.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real}; 2 | use crate::shape::Ball; 3 | 4 | /// Intersection test between balls. 5 | #[inline] 6 | pub fn intersection_test_ball_ball(center12: &Point, b1: &Ball, b2: &Ball) -> bool { 7 | let r1 = b1.radius; 8 | let r2 = b2.radius; 9 | let distance_squared = center12.coords.norm_squared(); 10 | let sum_radius = r1 + r2; 11 | distance_squared <= sum_radius * sum_radius 12 | } 13 | -------------------------------------------------------------------------------- /src/query/intersection_test/intersection_test_ball_point_query.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Point, Real}; 2 | use crate::query::PointQuery; 3 | use crate::shape::Ball; 4 | 5 | /// Intersection test between a ball and a shape implementing the `PointQuery` trait. 6 | pub fn intersection_test_ball_point_query( 7 | pos12: &Isometry, 8 | ball1: &Ball, 9 | point_query2: &P, 10 | ) -> bool { 11 | intersection_test_point_query_ball(&pos12.inverse(), point_query2, ball1) 12 | } 13 | 14 | /// Intersection test between a shape implementing the `PointQuery` trait and a ball. 15 | pub fn intersection_test_point_query_ball( 16 | pos12: &Isometry, 17 | point_query1: &P, 18 | ball2: &Ball, 19 | ) -> bool { 20 | let local_p2_1 = Point::from(pos12.translation.vector); 21 | let proj = point_query1.project_local_point(&local_p2_1, true); 22 | proj.is_inside || (local_p2_1 - proj.point).norm_squared() <= ball2.radius * ball2.radius 23 | } 24 | -------------------------------------------------------------------------------- /src/query/intersection_test/intersection_test_cuboid_cuboid.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::query::sat; 3 | use crate::shape::Cuboid; 4 | 5 | /// Intersection test between cuboids. 6 | #[inline] 7 | pub fn intersection_test_cuboid_cuboid( 8 | pos12: &Isometry, 9 | cuboid1: &Cuboid, 10 | cuboid2: &Cuboid, 11 | ) -> bool { 12 | let sep1 = sat::cuboid_cuboid_find_local_separating_normal_oneway(cuboid1, cuboid2, pos12).0; 13 | 14 | if sep1 > 0.0 { 15 | return false; 16 | } 17 | 18 | let pos21 = pos12.inverse(); 19 | let sep2 = sat::cuboid_cuboid_find_local_separating_normal_oneway(cuboid2, cuboid1, &pos21).0; 20 | if sep2 > 0.0 { 21 | return false; 22 | } 23 | 24 | #[cfg(feature = "dim2")] 25 | return true; // This case does not exist in 2D. 26 | #[cfg(feature = "dim3")] 27 | { 28 | let sep3 = sat::cuboid_cuboid_find_local_separating_edge_twoway(cuboid1, cuboid2, pos12).0; 29 | sep3 <= 0.0 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/query/intersection_test/intersection_test_cuboid_segment.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Real}; 3 | use crate::query::sat; 4 | use crate::shape::{Cuboid, Segment}; 5 | 6 | /// Test if a segment intersects an Aabb. 7 | pub fn intersection_test_aabb_segment(aabb1: &Aabb, segment2: &Segment) -> bool { 8 | let cuboid1 = Cuboid::new(aabb1.half_extents()); 9 | let pos12 = Isometry::from_parts((-aabb1.center().coords).into(), na::one()); 10 | intersection_test_cuboid_segment(&pos12, &cuboid1, segment2) 11 | } 12 | 13 | /// Test if a segment intersects a cuboid. 14 | #[inline] 15 | pub fn intersection_test_segment_cuboid( 16 | pos12: &Isometry, 17 | segment1: &Segment, 18 | cuboid2: &Cuboid, 19 | ) -> bool { 20 | intersection_test_cuboid_segment(&pos12.inverse(), cuboid2, segment1) 21 | } 22 | 23 | /// Test if a segment intersects a cuboid. 24 | #[inline] 25 | pub fn intersection_test_cuboid_segment( 26 | pos12: &Isometry, 27 | cube1: &Cuboid, 28 | segment2: &Segment, 29 | ) -> bool { 30 | let sep1 = 31 | sat::cuboid_support_map_find_local_separating_normal_oneway(cube1, segment2, pos12).0; 32 | if sep1 > 0.0 { 33 | return false; 34 | } 35 | 36 | // This case does not exist in 3D. 37 | #[cfg(feature = "dim2")] 38 | { 39 | let sep2 = sat::segment_cuboid_find_local_separating_normal_oneway( 40 | segment2, 41 | cube1, 42 | &pos12.inverse(), 43 | ) 44 | .0; 45 | if sep2 > 0.0 { 46 | return false; 47 | } 48 | } 49 | 50 | #[cfg(feature = "dim2")] 51 | return true; // This case does not exist in 2D. 52 | #[cfg(feature = "dim3")] 53 | { 54 | let sep3 = sat::cuboid_segment_find_local_separating_edge_twoway(cube1, segment2, pos12).0; 55 | sep3 <= 0.0 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/query/intersection_test/intersection_test_cuboid_triangle.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Isometry, Real}; 3 | use crate::query::sat; 4 | use crate::shape::{Cuboid, Triangle}; 5 | 6 | /// Tests if a triangle intersects an Aabb. 7 | pub fn intersection_test_aabb_triangle(aabb1: &Aabb, triangle2: &Triangle) -> bool { 8 | let cuboid1 = Cuboid::new(aabb1.half_extents()); 9 | let pos12 = Isometry::from_parts((-aabb1.center().coords).into(), na::one()); 10 | intersection_test_cuboid_triangle(&pos12, &cuboid1, triangle2) 11 | } 12 | 13 | /// Tests if a triangle intersects a cuboid. 14 | #[inline] 15 | pub fn intersection_test_triangle_cuboid( 16 | pos12: &Isometry, 17 | triangle1: &Triangle, 18 | cuboid2: &Cuboid, 19 | ) -> bool { 20 | intersection_test_cuboid_triangle(&pos12.inverse(), cuboid2, triangle1) 21 | } 22 | 23 | /// Tests if a triangle intersects an cuboid. 24 | #[inline] 25 | pub fn intersection_test_cuboid_triangle( 26 | pos12: &Isometry, 27 | cube1: &Cuboid, 28 | triangle2: &Triangle, 29 | ) -> bool { 30 | let sep1 = 31 | sat::cuboid_support_map_find_local_separating_normal_oneway(cube1, triangle2, pos12).0; 32 | if sep1 > 0.0 { 33 | return false; 34 | } 35 | 36 | let pos21 = pos12.inverse(); 37 | let sep2 = sat::triangle_cuboid_find_local_separating_normal_oneway(triangle2, cube1, &pos21).0; 38 | if sep2 > 0.0 { 39 | return false; 40 | } 41 | 42 | #[cfg(feature = "dim2")] 43 | return true; // This case does not exist in 2D. 44 | #[cfg(feature = "dim3")] 45 | { 46 | let sep3 = 47 | sat::cuboid_triangle_find_local_separating_edge_twoway(cube1, triangle2, pos12).0; 48 | sep3 <= 0.0 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/query/intersection_test/intersection_test_halfspace_support_map.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real}; 2 | use crate::shape::HalfSpace; 3 | use crate::shape::SupportMap; 4 | 5 | /// Intersection test between a halfspace and a support-mapped shape (Cuboid, ConvexHull, etc.) 6 | pub fn intersection_test_halfspace_support_map( 7 | pos12: &Isometry, 8 | halfspace: &HalfSpace, 9 | other: &G, 10 | ) -> bool { 11 | let deepest = other.support_point_toward(pos12, &-halfspace.normal); 12 | halfspace.normal.dot(&deepest.coords) <= 0.0 13 | } 14 | 15 | /// Intersection test between a support-mapped shape (Cuboid, ConvexHull, etc.) and a halfspace. 16 | pub fn intersection_test_support_map_halfspace( 17 | pos12: &Isometry, 18 | other: &G, 19 | halfspace: &HalfSpace, 20 | ) -> bool { 21 | intersection_test_halfspace_support_map(&pos12.inverse(), halfspace, other) 22 | } 23 | -------------------------------------------------------------------------------- /src/query/intersection_test/intersection_test_polygon_polygon.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use crate::geometry::proximity_detector::PrimitiveProximityDetectionContext; 4 | use crate::geometry::{sat, Polygon, Proximity}; 5 | use crate::math::{Isometry, Real}; 6 | 7 | pub fn detect_proximity_polygon_polygon( 8 | _ctxt: &mut PrimitiveProximityDetectionContext, 9 | ) -> Proximity { 10 | unimplemented!() 11 | // if let (Some(polygon1), Some(polygon2)) = (ctxt.shape1.as_polygon(), ctxt.shape2.as_polygon()) { 12 | // detect_proximity( 13 | // ctxt.prediction_distance, 14 | // polygon1, 15 | // &ctxt.position1, 16 | // polygon2, 17 | // &ctxt.position2, 18 | // ) 19 | // } else { 20 | // unreachable!() 21 | // } 22 | } 23 | 24 | fn detect_proximity<'a>( 25 | prediction_distance: Real, 26 | p1: &'a Polygon, 27 | m1: &'a Isometry, 28 | p2: &'a Polygon, 29 | m2: &'a Isometry, 30 | ) -> Proximity { 31 | let m12 = m1.inv_mul(&m2); 32 | let m21 = m12.inverse(); 33 | 34 | let sep1 = sat::polygon_polygon_compute_separation_features(p1, p2, &m12); 35 | if sep1.0 > prediction_distance { 36 | return Proximity::Disjoint; 37 | } 38 | 39 | let sep2 = sat::polygon_polygon_compute_separation_features(p2, p1, &m21); 40 | if sep2.0 > prediction_distance { 41 | return Proximity::Disjoint; 42 | } 43 | 44 | if sep2.0 > sep1.0 { 45 | if sep2.0 > 0.0 { 46 | Proximity::WithinMargin 47 | } else { 48 | Proximity::Intersecting 49 | } 50 | } else { 51 | if sep1.0 > 0.0 { 52 | Proximity::WithinMargin 53 | } else { 54 | Proximity::Intersecting 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/query/nonlinear_shape_cast/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementation details of the `cast_shapes_nonlinear` function. 2 | 3 | #[cfg(feature = "alloc")] 4 | pub use self::nonlinear_shape_cast_composite_shape_shape::{ 5 | cast_shapes_nonlinear_composite_shape_shape, cast_shapes_nonlinear_shape_composite_shape, 6 | NonlinearTOICompositeShapeShapeBestFirstVisitor, 7 | }; 8 | #[cfg(feature = "alloc")] 9 | pub use self::nonlinear_shape_cast_voxels_shape::{ 10 | cast_shapes_nonlinear_shape_voxels, cast_shapes_nonlinear_voxels_shape, 11 | }; 12 | //pub use self::nonlinear_shape_cast_halfspace_support_map::{cast_shapes_nonlinear_halfspace_support_map, cast_shapes_nonlinear_support_map_halfspace}; 13 | pub use self::nonlinear_rigid_motion::NonlinearRigidMotion; 14 | pub use self::nonlinear_shape_cast::cast_shapes_nonlinear; 15 | pub use self::nonlinear_shape_cast_support_map_support_map::{ 16 | cast_shapes_nonlinear_support_map_support_map, NonlinearShapeCastMode, 17 | }; 18 | 19 | #[cfg(feature = "alloc")] 20 | mod nonlinear_shape_cast_composite_shape_shape; 21 | #[cfg(feature = "alloc")] 22 | mod nonlinear_shape_cast_voxels_shape; 23 | //mod cast_shapes_nonlinear_halfspace_support_map; 24 | mod nonlinear_rigid_motion; 25 | mod nonlinear_shape_cast; 26 | mod nonlinear_shape_cast_support_map_support_map; 27 | -------------------------------------------------------------------------------- /src/query/nonlinear_shape_cast/nonlinear_shape_cast_halfspace_support_map.rs: -------------------------------------------------------------------------------- 1 | use na::RealField; 2 | 3 | use crate::math::{Isometry, Real, Vector}; 4 | use crate::query::{Ray, RayCast}; 5 | use crate::shape::HalfSpace; 6 | use crate::shape::SupportMap; 7 | 8 | /// Time Of Impact of a halfspace with a support-mapped shape under a rigid motion (translation + rotation). 9 | pub fn cast_shapes_nonlinear_halfspace_support_map( 10 | pos12: &Isometry, 11 | vel12: &Vector, 12 | halfspace: &HalfSpace, 13 | other: &G, 14 | ) -> Option { 15 | /* 16 | let halfspace_normal = mhalfspace * halfspace.normal(); 17 | let mut curr = 0.0; 18 | 19 | 20 | loop { 21 | let curr_mother = mother.advance(dvel); 22 | let closest_point = other.support_point(curr_mother, &-halfspace_normal); 23 | 24 | } 25 | 26 | let vel = *vel_other - *vel_halfspace; 27 | let closest_point = other.support_point(mother, &-halfspace_normal); 28 | 29 | halfspace.cast_ray(mhalfspace, &Ray::new(closest_point, vel), true) 30 | */ 31 | unimplemented!() 32 | } 33 | 34 | /// Time Of Impact of a halfspace with a support-mapped shape under a rigid motion (translation + rotation). 35 | pub fn cast_shapes_nonlinear_support_map_halfspace( 36 | pos12: &Isometry, 37 | vel12: &Vector, 38 | other: &G, 39 | halfspace: &HalfSpace, 40 | ) -> Option { 41 | cast_shapes_nonlinear_halfspace_support_map(pos12, vel12, halfspace, other) 42 | } 43 | -------------------------------------------------------------------------------- /src/query/point/mod.rs: -------------------------------------------------------------------------------- 1 | //! Point inclusion and projection. 2 | 3 | #[cfg(feature = "alloc")] 4 | pub use self::point_composite_shape::{ 5 | PointCompositeShapeProjBestFirstVisitor, PointCompositeShapeProjWithFeatureBestFirstVisitor, 6 | PointCompositeShapeProjWithLocationBestFirstVisitor, 7 | }; 8 | #[doc(inline)] 9 | pub use self::point_query::{PointProjection, PointQuery, PointQueryWithLocation}; 10 | #[cfg(feature = "alloc")] 11 | pub use self::point_support_map::local_point_projection_on_support_map; 12 | 13 | mod point_aabb; 14 | mod point_ball; 15 | mod point_bounding_sphere; 16 | mod point_capsule; 17 | #[cfg(feature = "alloc")] 18 | mod point_composite_shape; 19 | #[cfg(feature = "dim3")] 20 | mod point_cone; 21 | mod point_cuboid; 22 | #[cfg(feature = "dim3")] 23 | mod point_cylinder; 24 | mod point_halfspace; 25 | #[cfg(feature = "alloc")] 26 | mod point_heightfield; 27 | #[doc(hidden)] 28 | pub mod point_query; 29 | mod point_round_shape; 30 | mod point_segment; 31 | #[cfg(feature = "alloc")] 32 | mod point_support_map; 33 | #[cfg(feature = "dim3")] 34 | mod point_tetrahedron; 35 | mod point_triangle; 36 | #[cfg(feature = "alloc")] 37 | mod point_voxels; 38 | -------------------------------------------------------------------------------- /src/query/point/point_ball.rs: -------------------------------------------------------------------------------- 1 | use na::{self, ComplexField}; 2 | 3 | use crate::math::{Point, Real}; 4 | use crate::query::{PointProjection, PointQuery}; 5 | use crate::shape::{Ball, FeatureId}; 6 | 7 | impl PointQuery for Ball { 8 | #[inline] 9 | fn project_local_point(&self, pt: &Point, solid: bool) -> PointProjection { 10 | let distance_squared = pt.coords.norm_squared(); 11 | 12 | let inside = distance_squared <= self.radius * self.radius; 13 | 14 | if inside && solid { 15 | PointProjection::new(true, *pt) 16 | } else { 17 | let proj = 18 | Point::from(pt.coords * (self.radius / ComplexField::sqrt(distance_squared))); 19 | PointProjection::new(inside, proj) 20 | } 21 | } 22 | 23 | #[inline] 24 | fn project_local_point_and_get_feature( 25 | &self, 26 | pt: &Point, 27 | ) -> (PointProjection, FeatureId) { 28 | (self.project_local_point(pt, false), FeatureId::Face(0)) 29 | } 30 | 31 | #[inline] 32 | fn distance_to_local_point(&self, pt: &Point, solid: bool) -> Real { 33 | let dist = pt.coords.norm() - self.radius; 34 | 35 | if solid && dist < 0.0 { 36 | 0.0 37 | } else { 38 | dist 39 | } 40 | } 41 | 42 | #[inline] 43 | fn contains_local_point(&self, pt: &Point) -> bool { 44 | pt.coords.norm_squared() <= self.radius * self.radius 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/query/point/point_bounding_sphere.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::{Point, Real}; 3 | use crate::query::{PointProjection, PointQuery}; 4 | use crate::shape::{Ball, FeatureId}; 5 | 6 | impl PointQuery for BoundingSphere { 7 | #[inline] 8 | fn project_local_point(&self, pt: &Point, solid: bool) -> PointProjection { 9 | let centered_pt = pt - self.center().coords; 10 | let mut proj = Ball::new(self.radius()).project_local_point(¢ered_pt, solid); 11 | 12 | proj.point += self.center().coords; 13 | proj 14 | } 15 | 16 | #[inline] 17 | fn project_local_point_and_get_feature( 18 | &self, 19 | pt: &Point, 20 | ) -> (PointProjection, FeatureId) { 21 | (self.project_local_point(pt, false), FeatureId::Face(0)) 22 | } 23 | 24 | #[inline] 25 | fn distance_to_local_point(&self, pt: &Point, solid: bool) -> Real { 26 | let centered_pt = pt - self.center().coords; 27 | Ball::new(self.radius()).distance_to_local_point(¢ered_pt, solid) 28 | } 29 | 30 | #[inline] 31 | fn contains_local_point(&self, pt: &Point) -> bool { 32 | let centered_pt = pt - self.center().coords; 33 | Ball::new(self.radius()).contains_local_point(¢ered_pt) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/query/point/point_cuboid.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Point, Real}; 3 | use crate::query::{PointProjection, PointQuery}; 4 | use crate::shape::{Cuboid, FeatureId}; 5 | 6 | impl PointQuery for Cuboid { 7 | #[inline] 8 | fn project_local_point(&self, pt: &Point, solid: bool) -> PointProjection { 9 | let dl = Point::from(-self.half_extents); 10 | let ur = Point::from(self.half_extents); 11 | Aabb::new(dl, ur).project_local_point(pt, solid) 12 | } 13 | 14 | #[inline] 15 | fn project_local_point_and_get_feature( 16 | &self, 17 | pt: &Point, 18 | ) -> (PointProjection, FeatureId) { 19 | let dl = Point::from(-self.half_extents); 20 | let ur = Point::from(self.half_extents); 21 | Aabb::new(dl, ur).project_local_point_and_get_feature(pt) 22 | } 23 | 24 | #[inline] 25 | fn distance_to_local_point(&self, pt: &Point, solid: bool) -> Real { 26 | let dl = Point::from(-self.half_extents); 27 | let ur = Point::from(self.half_extents); 28 | Aabb::new(dl, ur).distance_to_local_point(pt, solid) 29 | } 30 | 31 | #[inline] 32 | fn contains_local_point(&self, pt: &Point) -> bool { 33 | let dl = Point::from(-self.half_extents); 34 | let ur = Point::from(self.half_extents); 35 | Aabb::new(dl, ur).contains_local_point(pt) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/query/point/point_halfspace.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real}; 2 | use crate::query::{PointProjection, PointQuery}; 3 | use crate::shape::{FeatureId, HalfSpace}; 4 | 5 | impl PointQuery for HalfSpace { 6 | #[inline] 7 | fn project_local_point(&self, pt: &Point, solid: bool) -> PointProjection { 8 | let d = self.normal.dot(&pt.coords); 9 | let inside = d <= 0.0; 10 | 11 | if inside && solid { 12 | PointProjection::new(true, *pt) 13 | } else { 14 | PointProjection::new(inside, *pt + (-*self.normal * d)) 15 | } 16 | } 17 | 18 | #[inline] 19 | fn project_local_point_and_get_feature( 20 | &self, 21 | pt: &Point, 22 | ) -> (PointProjection, FeatureId) { 23 | (self.project_local_point(pt, false), FeatureId::Face(0)) 24 | } 25 | 26 | #[inline] 27 | fn distance_to_local_point(&self, pt: &Point, solid: bool) -> Real { 28 | let dist = self.normal.dot(&pt.coords); 29 | 30 | if dist < 0.0 && solid { 31 | 0.0 32 | } else { 33 | // This will automatically be negative if the point is inside. 34 | dist 35 | } 36 | } 37 | 38 | #[inline] 39 | fn contains_local_point(&self, pt: &Point) -> bool { 40 | self.normal.dot(&pt.coords) <= 0.0 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/query/point/point_round_shape.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real}; 2 | use crate::query::gjk::VoronoiSimplex; 3 | use crate::query::{PointProjection, PointQuery}; 4 | use crate::shape::{FeatureId, RoundShape, SupportMap}; 5 | 6 | // TODO: if PointQuery had a `project_point_with_normal` method, we could just 7 | // call this and adjust the projected point accordingly. 8 | impl PointQuery for RoundShape { 9 | #[inline] 10 | fn project_local_point(&self, point: &Point, solid: bool) -> PointProjection { 11 | #[cfg(not(feature = "alloc"))] 12 | return unimplemented!( 13 | "The projection of points on a round shape isn't supported without alloc yet." 14 | ); 15 | 16 | #[cfg(feature = "alloc")] 17 | return crate::query::details::local_point_projection_on_support_map( 18 | self, 19 | &mut VoronoiSimplex::new(), 20 | point, 21 | solid, 22 | ); 23 | } 24 | 25 | #[inline] 26 | fn project_local_point_and_get_feature( 27 | &self, 28 | point: &Point, 29 | ) -> (PointProjection, FeatureId) { 30 | (self.project_local_point(point, false), FeatureId::Unknown) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/query/point/point_voxels.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real}; 2 | use crate::query::{PointProjection, PointQuery}; 3 | use crate::shape::{Cuboid, FeatureId, VoxelType, Voxels}; 4 | 5 | impl PointQuery for Voxels { 6 | #[inline] 7 | fn project_local_point(&self, pt: &Point, solid: bool) -> PointProjection { 8 | // TODO: optimize this very naive implementation. 9 | let base_cuboid = Cuboid::new(self.voxel_size() / 2.0); 10 | let mut smallest_dist = Real::MAX; 11 | let mut result = PointProjection::new(false, *pt); 12 | 13 | for vox in self.voxels() { 14 | if vox.state.voxel_type() != VoxelType::Empty { 15 | let mut candidate = 16 | base_cuboid.project_local_point(&(pt - vox.center.coords), solid); 17 | candidate.point += vox.center.coords; 18 | 19 | let candidate_dist = (candidate.point - pt).norm(); 20 | if candidate_dist < smallest_dist { 21 | result = candidate; 22 | smallest_dist = candidate_dist; 23 | } 24 | } 25 | } 26 | 27 | result 28 | } 29 | 30 | #[inline] 31 | fn project_local_point_and_get_feature( 32 | &self, 33 | pt: &Point, 34 | ) -> (PointProjection, FeatureId) { 35 | // TODO: get the actual feature. 36 | (self.project_local_point(pt, false), FeatureId::Unknown) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/query/ray/mod.rs: -------------------------------------------------------------------------------- 1 | //! Ray-casting related definitions and implementations. 2 | 3 | #[doc(inline)] 4 | pub use self::ray::{Ray, RayCast, RayIntersection}; 5 | pub use self::ray_ball::ray_toi_with_ball; 6 | #[cfg(feature = "alloc")] 7 | pub use self::ray_composite_shape::{ 8 | RayCompositeShapeToiAndNormalBestFirstVisitor, RayCompositeShapeToiBestFirstVisitor, 9 | }; 10 | pub use self::ray_halfspace::{line_toi_with_halfspace, ray_toi_with_halfspace}; 11 | pub use self::ray_support_map::local_ray_intersection_with_support_map_with_params; 12 | #[cfg(feature = "dim3")] 13 | pub use self::ray_triangle::local_ray_intersection_with_triangle; 14 | #[cfg(all(feature = "dim3", feature = "alloc"))] 15 | pub use self::ray_trimesh::RayCullingMode; 16 | pub use self::simd_ray::SimdRay; 17 | 18 | #[doc(hidden)] 19 | pub mod ray; 20 | mod ray_aabb; 21 | mod ray_ball; 22 | mod ray_bounding_sphere; 23 | #[cfg(feature = "alloc")] 24 | mod ray_composite_shape; 25 | mod ray_cuboid; 26 | mod ray_halfspace; 27 | #[cfg(feature = "alloc")] 28 | mod ray_heightfield; 29 | mod ray_round_shape; 30 | mod ray_support_map; 31 | mod ray_triangle; 32 | #[cfg(feature = "alloc")] 33 | mod ray_trimesh; 34 | #[cfg(feature = "alloc")] 35 | mod ray_voxels; 36 | mod simd_ray; 37 | -------------------------------------------------------------------------------- /src/query/ray/ray_bounding_sphere.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::BoundingSphere; 2 | use crate::math::Real; 3 | use crate::query::{Ray, RayCast, RayIntersection}; 4 | use crate::shape::Ball; 5 | 6 | impl RayCast for BoundingSphere { 7 | #[inline] 8 | fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option { 9 | let centered_ray = ray.translate_by(-self.center().coords); 10 | Ball::new(self.radius()).cast_local_ray(¢ered_ray, max_time_of_impact, solid) 11 | } 12 | 13 | #[inline] 14 | fn cast_local_ray_and_get_normal( 15 | &self, 16 | ray: &Ray, 17 | max_time_of_impact: Real, 18 | solid: bool, 19 | ) -> Option { 20 | let centered_ray = ray.translate_by(-self.center().coords); 21 | Ball::new(self.radius()).cast_local_ray_and_get_normal( 22 | ¢ered_ray, 23 | max_time_of_impact, 24 | solid, 25 | ) 26 | } 27 | 28 | #[inline] 29 | fn intersects_local_ray(&self, ray: &Ray, max_time_of_impact: Real) -> bool { 30 | let centered_ray = ray.translate_by(-self.center().coords); 31 | Ball::new(self.radius()).intersects_local_ray(¢ered_ray, max_time_of_impact) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/query/ray/ray_cuboid.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Point, Real}; 3 | use crate::query::{Ray, RayCast, RayIntersection}; 4 | use crate::shape::Cuboid; 5 | 6 | impl RayCast for Cuboid { 7 | #[inline] 8 | fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option { 9 | let dl = Point::from(-self.half_extents); 10 | let ur = Point::from(self.half_extents); 11 | Aabb::new(dl, ur).cast_local_ray(ray, max_time_of_impact, solid) 12 | } 13 | 14 | #[inline] 15 | fn cast_local_ray_and_get_normal( 16 | &self, 17 | ray: &Ray, 18 | max_time_of_impact: Real, 19 | solid: bool, 20 | ) -> Option { 21 | let dl = Point::from(-self.half_extents); 22 | let ur = Point::from(self.half_extents); 23 | Aabb::new(dl, ur).cast_local_ray_and_get_normal(ray, max_time_of_impact, solid) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/query/ray/ray_round_shape.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::query::gjk::VoronoiSimplex; 3 | use crate::query::{Ray, RayCast, RayIntersection}; 4 | use crate::shape::{RoundShape, SupportMap}; 5 | 6 | impl RayCast for RoundShape { 7 | fn cast_local_ray_and_get_normal( 8 | &self, 9 | ray: &Ray, 10 | max_time_of_impact: Real, 11 | solid: bool, 12 | ) -> Option { 13 | crate::query::details::local_ray_intersection_with_support_map_with_params( 14 | self, 15 | &mut VoronoiSimplex::new(), 16 | ray, 17 | max_time_of_impact, 18 | solid, 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/query/ray/simd_ray.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, SimdReal, Vector}; 2 | use crate::query::Ray; 3 | use simba::simd::SimdValue; 4 | 5 | /// A structure representing 4 rays in an SIMD SoA fashion. 6 | #[derive(Debug, Copy, Clone)] 7 | pub struct SimdRay { 8 | /// The origin of the rays represented as a single SIMD point. 9 | pub origin: Point, 10 | /// The direction of the rays represented as a single SIMD vector. 11 | pub dir: Vector, 12 | } 13 | 14 | impl SimdRay { 15 | /// Creates a new SIMD ray with all its lanes filled with the same ray. 16 | pub fn splat(ray: Ray) -> Self { 17 | Self { 18 | origin: Point::splat(ray.origin), 19 | dir: Vector::splat(ray.dir), 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/query/sat/mod.rs: -------------------------------------------------------------------------------- 1 | //! Application of the Separating-Axis-Theorem (SAT). 2 | 3 | pub use self::sat_cuboid_cuboid::*; 4 | pub use self::sat_cuboid_point::*; 5 | pub use self::sat_cuboid_segment::*; 6 | pub use self::sat_cuboid_support_map::*; 7 | pub use self::sat_cuboid_triangle::*; 8 | pub use self::sat_support_map_support_map::*; 9 | #[cfg(feature = "dim3")] 10 | pub use self::sat_triangle_segment::*; 11 | // pub use self::sat_polygon_polygon::*; 12 | 13 | mod sat_cuboid_cuboid; 14 | mod sat_cuboid_point; 15 | mod sat_cuboid_segment; 16 | mod sat_cuboid_support_map; 17 | mod sat_cuboid_triangle; 18 | mod sat_support_map_support_map; 19 | #[cfg(feature = "dim3")] 20 | mod sat_triangle_segment; 21 | // mod sat_polygon_polygon; 22 | -------------------------------------------------------------------------------- /src/query/sat/sat_cuboid_point.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Point, Real, Vector}; 2 | use crate::shape::{Cuboid, SupportMap}; 3 | 4 | use na::Unit; 5 | 6 | // NOTE: this only works with cuboid on the rhs because it has its symmetry origin at zero 7 | // (therefore we can check only one normal direction). 8 | /// Computes the separation between a point and a cuboid, along the given direction `normal1`. 9 | pub fn point_cuboid_find_local_separating_normal_oneway( 10 | point1: Point, 11 | normal1: Option>>, 12 | shape2: &Cuboid, 13 | pos12: &Isometry, 14 | ) -> (Real, Vector) { 15 | let mut best_separation = -Real::MAX; 16 | let mut best_dir = Vector::zeros(); 17 | 18 | if let Some(normal1) = normal1 { 19 | let axis1 = if (pos12.translation.vector - point1.coords).dot(&normal1) >= 0.0 { 20 | normal1 21 | } else { 22 | -normal1 23 | }; 24 | 25 | let pt2 = shape2.support_point_toward(pos12, &-axis1); 26 | let separation = (pt2 - point1).dot(&axis1); 27 | 28 | if separation > best_separation { 29 | best_separation = separation; 30 | best_dir = *axis1; 31 | } 32 | } 33 | 34 | (best_separation, best_dir) 35 | } 36 | -------------------------------------------------------------------------------- /src/query/sat/sat_cuboid_segment.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real, Vector}; 2 | use crate::query::sat; 3 | use crate::shape::{Cuboid, Segment}; 4 | 5 | /// Finds the best separating edge between a cuboid and a segment. 6 | /// 7 | /// All combinations of edges from the cuboid and the segment are taken into 8 | /// account. 9 | #[cfg(feature = "dim3")] 10 | pub fn cuboid_segment_find_local_separating_edge_twoway( 11 | cube1: &Cuboid, 12 | segment2: &Segment, 13 | pos12: &Isometry, 14 | ) -> (Real, Vector) { 15 | let x2 = pos12 * (segment2.b - segment2.a); 16 | 17 | let axes = [ 18 | // Vector::{x, y ,z}().cross(y2) 19 | Vector::new(0.0, -x2.z, x2.y), 20 | Vector::new(x2.z, 0.0, -x2.x), 21 | Vector::new(-x2.y, x2.x, 0.0), 22 | ]; 23 | 24 | sat::cuboid_support_map_find_local_separating_edge_twoway(cube1, segment2, &axes, pos12) 25 | } 26 | 27 | /// Finds the best separating normal between a cuboid and a segment. 28 | /// 29 | /// Only the normals of `segment1` are tested. 30 | #[cfg(feature = "dim2")] 31 | pub fn segment_cuboid_find_local_separating_normal_oneway( 32 | segment1: &Segment, 33 | shape2: &Cuboid, 34 | pos12: &Isometry, 35 | ) -> (Real, Vector) { 36 | sat::point_cuboid_find_local_separating_normal_oneway( 37 | segment1.a, 38 | segment1.normal(), 39 | shape2, 40 | pos12, 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /src/query/sat/sat_support_map_support_map.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Real, Vector}; 2 | use crate::shape::SupportMap; 3 | use na::Unit; 4 | 5 | /// Computes the separation along the given direction, 6 | /// between two convex shapes implementing the `SupportMap` trait. 7 | #[allow(dead_code)] 8 | pub fn support_map_support_map_compute_separation( 9 | sm1: &impl SupportMap, 10 | sm2: &impl SupportMap, 11 | pos12: &Isometry, 12 | dir1: &Unit>, 13 | ) -> Real { 14 | let p1 = sm1.local_support_point_toward(dir1); 15 | let p2 = sm2.support_point_toward(pos12, &-*dir1); 16 | (p2 - p1).dot(dir1) 17 | } 18 | -------------------------------------------------------------------------------- /src/query/shape_cast/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementation details of the `cast_shapes` function. 2 | 3 | pub use self::shape_cast::{cast_shapes, ShapeCastHit, ShapeCastOptions, ShapeCastStatus}; 4 | pub use self::shape_cast_ball_ball::cast_shapes_ball_ball; 5 | pub use self::shape_cast_halfspace_support_map::{ 6 | cast_shapes_halfspace_support_map, cast_shapes_support_map_halfspace, 7 | }; 8 | #[cfg(feature = "alloc")] 9 | pub use self::{ 10 | shape_cast_composite_shape_shape::{ 11 | cast_shapes_composite_shape_shape, cast_shapes_shape_composite_shape, 12 | TOICompositeShapeShapeBestFirstVisitor, 13 | }, 14 | shape_cast_heightfield_shape::{cast_shapes_heightfield_shape, cast_shapes_shape_heightfield}, 15 | shape_cast_support_map_support_map::cast_shapes_support_map_support_map, 16 | shape_cast_voxels_shape::{cast_shapes_shape_voxels, cast_shapes_voxels_shape}, 17 | }; 18 | 19 | mod shape_cast; 20 | mod shape_cast_ball_ball; 21 | #[cfg(feature = "alloc")] 22 | mod shape_cast_composite_shape_shape; 23 | mod shape_cast_halfspace_support_map; 24 | #[cfg(feature = "alloc")] 25 | mod shape_cast_heightfield_shape; 26 | #[cfg(feature = "alloc")] 27 | mod shape_cast_support_map_support_map; 28 | #[cfg(feature = "alloc")] 29 | mod shape_cast_voxels_shape; 30 | -------------------------------------------------------------------------------- /src/query/split/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::split::{IntersectResult, SplitResult}; 2 | 3 | mod split; 4 | mod split_aabb; 5 | mod split_segment; 6 | 7 | #[cfg(all(feature = "dim3", feature = "spade"))] 8 | mod split_trimesh; 9 | -------------------------------------------------------------------------------- /src/query/split/split.rs: -------------------------------------------------------------------------------- 1 | /// The result of a plane-splitting operation. 2 | pub enum SplitResult { 3 | /// The split operation yield two results: one lying on the negative half-space of the plane 4 | /// and the second lying on the positive half-space of the plane. 5 | Pair(T, T), 6 | /// The shape being split is fully contained in the negative half-space of the plane. 7 | Negative, 8 | /// The shape being split is fully contained in the positive half-space of the plane. 9 | Positive, 10 | } 11 | 12 | /// The result of a plane-intersection operation. 13 | pub enum IntersectResult { 14 | /// The intersect operation yielded a result, lying in the plane 15 | Intersect(T), 16 | /// The shape being intersected is fully contained in the negative half-space of the plane. 17 | Negative, 18 | /// The shape being intersected is fully contained in the positive half-space of the plane. 19 | Positive, 20 | } 21 | -------------------------------------------------------------------------------- /src/query/split/split_aabb.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::Real; 3 | use crate::query::SplitResult; 4 | 5 | impl Aabb { 6 | /// Splits this `Aabb` along the given canonical axis. 7 | /// 8 | /// This will split the `Aabb` by a plane with a normal with it’s `axis`-th component set to 1. 9 | /// The splitting plane is shifted wrt. the origin by the `bias` (i.e. it passes through the point 10 | /// equal to `normal * bias`). 11 | /// 12 | /// # Result 13 | /// Returns the result of the split. The first `Aabb` returned is the piece lying on the negative 14 | /// half-space delimited by the splitting plane. The second `Aabb` returned is the piece lying on the 15 | /// positive half-space delimited by the splitting plane. 16 | pub fn canonical_split(&self, axis: usize, bias: Real, epsilon: Real) -> SplitResult { 17 | if self.mins[axis] >= bias - epsilon { 18 | SplitResult::Positive 19 | } else if self.maxs[axis] <= bias + epsilon { 20 | SplitResult::Negative 21 | } else { 22 | let mut left = *self; 23 | let mut right = *self; 24 | left.maxs[axis] = bias; 25 | right.mins[axis] = bias; 26 | SplitResult::Pair(left, right) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/query/visitors/bounding_volume_intersections_visitor.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::{Aabb, SimdAabb}; 2 | use crate::math::SIMD_WIDTH; 3 | use crate::partitioning::{SimdVisitStatus, SimdVisitor}; 4 | use core::marker::PhantomData; 5 | use simba::simd::SimdBool as _; 6 | 7 | /// Spatial partitioning data structure visitor collecting interferences with a given bounding volume. 8 | pub struct BoundingVolumeIntersectionsVisitor { 9 | bv: SimdAabb, 10 | callback: F, 11 | _phantom: PhantomData, 12 | } 13 | 14 | impl BoundingVolumeIntersectionsVisitor 15 | where 16 | F: FnMut(&T) -> bool, 17 | { 18 | /// Creates a new `BoundingVolumeIntersectionsVisitor`. 19 | #[inline] 20 | pub fn new(bv: &Aabb, callback: F) -> BoundingVolumeIntersectionsVisitor { 21 | BoundingVolumeIntersectionsVisitor { 22 | bv: SimdAabb::splat(*bv), 23 | callback, 24 | _phantom: PhantomData, 25 | } 26 | } 27 | } 28 | 29 | impl SimdVisitor for BoundingVolumeIntersectionsVisitor 30 | where 31 | F: FnMut(&T) -> bool, 32 | { 33 | #[inline] 34 | fn visit(&mut self, bv: &SimdAabb, b: Option<[Option<&T>; SIMD_WIDTH]>) -> SimdVisitStatus { 35 | let mask = bv.intersects(&self.bv); 36 | 37 | if let Some(data) = b { 38 | let bitmask = mask.bitmask(); 39 | 40 | for (ii, data) in data.iter().enumerate() { 41 | if (bitmask & (1 << ii)) != 0 { 42 | let Some(data) = data else { continue }; 43 | if !(self.callback)(data) { 44 | return SimdVisitStatus::ExitEarly; 45 | } 46 | } 47 | } 48 | } 49 | 50 | SimdVisitStatus::MaybeContinue(mask) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/query/visitors/mod.rs: -------------------------------------------------------------------------------- 1 | //! Visitors for performing geometric queries exploiting spatial partitioning data structures. 2 | 3 | pub use self::aabb_sets_interferences_collector::AabbSetsInterferencesCollector; 4 | pub use self::bounding_volume_intersections_simultaneous_visitor::BoundingVolumeIntersectionsSimultaneousVisitor; 5 | pub use self::bounding_volume_intersections_visitor::BoundingVolumeIntersectionsVisitor; 6 | pub use self::composite_closest_point_visitor::CompositeClosestPointVisitor; 7 | pub use self::composite_point_containment_test::CompositePointContainmentTest; 8 | pub use self::point_intersections_visitor::PointIntersectionsVisitor; 9 | pub use self::ray_intersections_visitor::RayIntersectionsVisitor; 10 | 11 | mod aabb_sets_interferences_collector; 12 | mod bounding_volume_intersections_simultaneous_visitor; 13 | mod bounding_volume_intersections_visitor; 14 | mod composite_closest_point_visitor; 15 | mod composite_point_containment_test; 16 | mod point_intersections_visitor; 17 | mod ray_intersections_visitor; 18 | -------------------------------------------------------------------------------- /src/shape/half_space.rs: -------------------------------------------------------------------------------- 1 | //! Support mapping based HalfSpace shape. 2 | use crate::math::{Real, Vector}; 3 | use na::Unit; 4 | 5 | #[cfg(feature = "rkyv")] 6 | use rkyv::{bytecheck, CheckBytes}; 7 | 8 | /// A half-space delimited by an infinite plane. 9 | #[derive(PartialEq, Debug, Clone, Copy)] 10 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 11 | #[cfg_attr( 12 | feature = "rkyv", 13 | derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes), 14 | archive(as = "Self") 15 | )] 16 | #[repr(C)] 17 | pub struct HalfSpace { 18 | /// The halfspace planar boundary's outward normal. 19 | pub normal: Unit>, 20 | } 21 | 22 | impl HalfSpace { 23 | /// Builds a new halfspace from its center and its normal. 24 | #[inline] 25 | pub fn new(normal: Unit>) -> HalfSpace { 26 | HalfSpace { normal } 27 | } 28 | 29 | /// Computes a scaled version of this half-space. 30 | /// 31 | /// Returns `None` if `self.normal` scaled by `scale` is zero (the scaled half-space 32 | /// degenerates to a single point). 33 | pub fn scaled(self, scale: &Vector) -> Option { 34 | Unit::try_new(self.normal.component_mul(scale), 0.0).map(|normal| Self { normal }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/shape/round_shape.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real, Vector}; 2 | use crate::shape::SupportMap; 3 | use na::Unit; 4 | 5 | #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] 6 | #[cfg_attr( 7 | feature = "rkyv", 8 | derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize), 9 | archive(check_bytes) 10 | )] 11 | #[derive(Copy, Clone, Debug)] 12 | #[repr(C)] 13 | /// A shape with rounded borders. 14 | pub struct RoundShape { 15 | /// The shape being rounded. 16 | pub inner_shape: S, 17 | /// The radius of the rounded border. 18 | pub border_radius: Real, 19 | } 20 | 21 | impl SupportMap for RoundShape { 22 | fn local_support_point(&self, dir: &Vector) -> Point { 23 | self.local_support_point_toward(&Unit::new_normalize(*dir)) 24 | } 25 | 26 | fn local_support_point_toward(&self, dir: &Unit>) -> Point { 27 | self.inner_shape.local_support_point_toward(dir) + **dir * self.border_radius 28 | } 29 | } 30 | 31 | /// A shape reference with rounded borders. 32 | pub(crate) struct RoundShapeRef<'a, S: ?Sized> { 33 | /// The shape being rounded. 34 | pub inner_shape: &'a S, 35 | /// The radius of the rounded border. 36 | pub border_radius: Real, 37 | } 38 | 39 | impl SupportMap for RoundShapeRef<'_, S> { 40 | fn local_support_point(&self, dir: &Vector) -> Point { 41 | self.local_support_point_toward(&Unit::new_normalize(*dir)) 42 | } 43 | 44 | fn local_support_point_toward(&self, dir: &Unit>) -> Point { 45 | self.inner_shape.local_support_point_toward(dir) + **dir * self.border_radius 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/shape/support_map.rs: -------------------------------------------------------------------------------- 1 | //! Traits for support mapping based shapes. 2 | 3 | use crate::math::{Isometry, Point, Real, Vector}; 4 | use na::Unit; 5 | 6 | /// Traits of convex shapes representable by a support mapping function. 7 | /// 8 | /// # Parameters: 9 | /// * V - type of the support mapping direction argument and of the returned point. 10 | pub trait SupportMap { 11 | // Evaluates the support function of this shape. 12 | // 13 | // A support function is a function associating a vector to the shape point which maximizes 14 | // their dot product. 15 | fn local_support_point(&self, dir: &Vector) -> Point; 16 | 17 | /// Same as `self.local_support_point` except that `dir` is normalized. 18 | fn local_support_point_toward(&self, dir: &Unit>) -> Point { 19 | self.local_support_point(dir.as_ref()) 20 | } 21 | 22 | // Evaluates the support function of this shape transformed by `transform`. 23 | // 24 | // A support function is a function associating a vector to the shape point which maximizes 25 | // their dot product. 26 | fn support_point(&self, transform: &Isometry, dir: &Vector) -> Point { 27 | let local_dir = transform.inverse_transform_vector(dir); 28 | transform * self.local_support_point(&local_dir) 29 | } 30 | 31 | /// Same as `self.support_point` except that `dir` is normalized. 32 | fn support_point_toward( 33 | &self, 34 | transform: &Isometry, 35 | dir: &Unit>, 36 | ) -> Point { 37 | let local_dir = Unit::new_unchecked(transform.inverse_transform_vector(dir)); 38 | transform * self.local_support_point_toward(&local_dir) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/transformation/convex_hull3/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(thiserror::Error, Debug, PartialEq)] 2 | /// Errors generated by the convex-hull calculation. 3 | pub enum ConvexHullError { 4 | /// Reached an impossible configuration in the convex-hull calculation, 5 | /// likely because of a bug. 6 | #[error("Internal error: {0}")] 7 | InternalError(&'static str), 8 | /// The convex hull calculation was unable to find a support point. 9 | /// This generally happens if the input point set contains invalid points (with NaN coordinates) 10 | /// or if they are almost coplanar. 11 | #[error("Input points are either invalid (NaN) or are almost coplanar.")] 12 | MissingSupportPoint, 13 | /// The convex-hull calculation failed because less than 3 points were provided. 14 | #[error("Less than 3 points were given to the convex-hull algorithm.")] 15 | IncompleteInput, 16 | /// Reached a piece of code we shouldn’t (internal error). 17 | #[error("Internal error: unreachable code path")] 18 | Unreachable, 19 | } 20 | -------------------------------------------------------------------------------- /src/transformation/convex_hull3/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::error::ConvexHullError; 2 | use self::initial_mesh::{try_get_initial_mesh, InitialMesh}; 3 | use self::triangle_facet::TriangleFacet; 4 | use self::validation::check_facet_links; 5 | pub use convex_hull::{convex_hull, try_convex_hull}; 6 | #[cfg(feature = "std")] 7 | pub use validation::check_convex_hull; 8 | 9 | mod convex_hull; 10 | mod error; 11 | mod initial_mesh; 12 | mod triangle_facet; 13 | mod validation; 14 | -------------------------------------------------------------------------------- /src/transformation/mesh_intersection/mesh_intersection_error.rs: -------------------------------------------------------------------------------- 1 | use crate::shape::TriMeshBuilderError; 2 | 3 | #[cfg(doc)] 4 | use crate::shape::{TriMesh, TriMeshFlags}; 5 | 6 | /// Error indicating that a query is not supported between certain shapes 7 | #[derive(thiserror::Error, Debug, Copy, Clone, Eq, PartialEq)] 8 | pub enum MeshIntersectionError { 9 | /// At least one of the meshes is missing its topology information. Ensure that the [`TriMeshFlags::ORIENTED`] flag is enabled on both meshes. 10 | #[error("at least one of the meshes is missing its topology information. Ensure that the `TriMeshFlags::ORIENTED` flag is enabled on both meshes.")] 11 | MissingTopology, 12 | /// At least one of the meshes is missing its pseudo-normals. Ensure that the [`TriMeshFlags::ORIENTED`] flag is enabled on both meshes. 13 | #[error("at least one of the meshes is missing its pseudo-normals. Ensure that the `TriMeshFlags::ORIENTED` flag is enabled on both meshes.")] 14 | MissingPseudoNormals, 15 | /// Internal failure while intersecting two triangles 16 | #[error("internal failure while intersecting two triangles")] 17 | TriTriError, 18 | /// Internal failure while merging faces resulting from intersections 19 | #[error("internal failure while merging faces resulting from intersections")] 20 | DuplicateVertices, 21 | /// Internal failure while triangulating an intersection face 22 | #[error("internal failure while triangulating an intersection face")] 23 | TriangulationError, 24 | /// See [`TriMeshBuilderError`] 25 | #[error("TriMeshBuilderError: {0}")] 26 | TriMeshBuilderError(TriMeshBuilderError), 27 | } 28 | 29 | impl From for MeshIntersectionError { 30 | fn from(value: TriMeshBuilderError) -> Self { 31 | MeshIntersectionError::TriMeshBuilderError(value) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/transformation/mesh_intersection/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::mesh_intersection::{ 2 | intersect_meshes, intersect_meshes_with_tolerances, MeshIntersectionTolerances, 3 | }; 4 | pub use self::mesh_intersection_error::MeshIntersectionError; 5 | use triangle_triangle_intersection::*; 6 | 7 | use crate::math::Real; 8 | 9 | mod mesh_intersection; 10 | mod mesh_intersection_error; 11 | mod triangle_triangle_intersection; 12 | 13 | const EPS: Real = 1.0e-6; 14 | -------------------------------------------------------------------------------- /src/transformation/mod.rs: -------------------------------------------------------------------------------- 1 | //! Transformation, simplification and decomposition of meshes. 2 | 3 | #[cfg(feature = "dim3")] 4 | pub(crate) use self::convex_hull2::convex_hull2_idx; 5 | #[cfg(feature = "dim2")] 6 | pub use self::convex_hull2::{convex_hull2 as convex_hull, convex_hull2_idx as convex_hull_idx}; 7 | #[cfg(all(feature = "dim3", feature = "std"))] 8 | pub use self::convex_hull3::check_convex_hull; 9 | #[cfg(feature = "dim3")] 10 | pub use self::convex_hull3::{convex_hull, try_convex_hull, ConvexHullError}; 11 | #[cfg(all(feature = "dim3", feature = "spade"))] 12 | pub use self::mesh_intersection::{ 13 | intersect_meshes, intersect_meshes_with_tolerances, MeshIntersectionError, 14 | MeshIntersectionTolerances, 15 | }; 16 | pub use self::polygon_intersection::{ 17 | convex_polygons_intersection, convex_polygons_intersection_points, 18 | convex_polygons_intersection_points_with_tolerances, 19 | convex_polygons_intersection_with_tolerances, polygons_intersection, 20 | polygons_intersection_points, 21 | }; 22 | 23 | mod convex_hull2; 24 | #[cfg(feature = "dim3")] 25 | mod convex_hull3; 26 | pub(crate) mod convex_hull_utils; 27 | 28 | mod polygon_intersection; 29 | /// Approximate convex decomposition using the VHACD algorithm. 30 | pub mod vhacd; 31 | /// Voxelization of a 2D polyline or 3D triangle mesh. 32 | pub mod voxelization; 33 | 34 | #[cfg(feature = "dim2")] 35 | pub(crate) mod ear_clipping; 36 | #[cfg(feature = "dim2")] 37 | pub(crate) mod hertel_mehlhorn; 38 | #[cfg(feature = "dim2")] 39 | pub use hertel_mehlhorn::{hertel_mehlhorn, hertel_mehlhorn_idx}; 40 | #[cfg(all(feature = "dim3", feature = "spade"))] 41 | mod mesh_intersection; 42 | #[cfg(feature = "dim3")] 43 | mod to_outline; 44 | #[cfg(feature = "dim2")] 45 | mod to_polyline; 46 | mod to_trimesh; 47 | pub mod utils; 48 | 49 | #[cfg(feature = "wavefront")] 50 | mod wavefront; 51 | -------------------------------------------------------------------------------- /src/transformation/to_outline/capsule_to_outline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::{Capsule, Cylinder}; 3 | use crate::transformation::utils; 4 | use alloc::vec::Vec; 5 | use na::{self, Point3}; 6 | 7 | impl Capsule { 8 | /// Outlines this capsule’s shape using polylines. 9 | pub fn to_outline(&self, nsubdiv: u32) -> (Vec>, Vec<[u32; 2]>) { 10 | let (vtx, idx) = canonical_capsule_outline(self.radius, self.half_height(), nsubdiv); 11 | (utils::transformed(vtx, self.canonical_transform()), idx) 12 | } 13 | } 14 | 15 | /// Generates a capsule. 16 | pub(crate) fn canonical_capsule_outline( 17 | caps_radius: Real, 18 | cylinder_half_height: Real, 19 | nsubdiv: u32, 20 | ) -> (Vec>, Vec<[u32; 2]>) { 21 | let (mut vtx, mut idx) = Cylinder::new(cylinder_half_height, caps_radius).to_outline(nsubdiv); 22 | let shift = vtx.len() as u32; 23 | 24 | // Generate the hemispheres. 25 | super::ball_to_outline::push_unit_hemisphere_outline(nsubdiv / 2, &mut vtx, &mut idx); 26 | super::ball_to_outline::push_unit_hemisphere_outline(nsubdiv / 2, &mut vtx, &mut idx); 27 | 28 | let ncap_pts = (nsubdiv / 2 + 1) * 2; 29 | vtx[shift as usize..(shift + ncap_pts) as usize] 30 | .iter_mut() 31 | .for_each(|pt| { 32 | *pt *= caps_radius * 2.0; 33 | pt.y += cylinder_half_height 34 | }); 35 | 36 | vtx[(shift + ncap_pts) as usize..] 37 | .iter_mut() 38 | .for_each(|pt| { 39 | *pt *= caps_radius * 2.0; 40 | pt.y = -pt.y - cylinder_half_height 41 | }); 42 | 43 | (vtx, idx) 44 | } 45 | -------------------------------------------------------------------------------- /src/transformation/to_outline/cone_to_outline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::Cone; 3 | use crate::transformation::utils; 4 | use alloc::{vec, vec::Vec}; 5 | use na::{self, Point3, Vector3}; 6 | 7 | impl Cone { 8 | /// Outlines this cone’s shape using polylines. 9 | pub fn to_outline(&self, nsubdiv: u32) -> (Vec>, Vec<[u32; 2]>) { 10 | let diameter = self.radius * 2.0; 11 | let height = self.half_height * 2.0; 12 | let scale = Vector3::new(diameter, height, diameter); 13 | let (vtx, idx) = unit_cone_outline(nsubdiv); 14 | (utils::scaled(vtx, scale), idx) 15 | } 16 | } 17 | 18 | /// Generates a cone with unit height and diameter. 19 | fn unit_cone_outline(nsubdiv: u32) -> (Vec>, Vec<[u32; 2]>) { 20 | let mut out_vtx = vec![Point3::new(-0.5, -0.5, 0.0), Point3::new(0.0, 0.5, 0.0)]; 21 | let mut out_ptx = vec![]; 22 | #[allow(clippy::single_range_in_vec_init)] // The single range is on purpose. 23 | utils::apply_revolution(false, true, &[0..1], nsubdiv, &mut out_vtx, &mut out_ptx); 24 | (out_vtx, out_ptx) 25 | } 26 | -------------------------------------------------------------------------------- /src/transformation/to_outline/convex_polyhedron_to_outline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::ConvexPolyhedron; 3 | use na::Point3; 4 | 5 | impl ConvexPolyhedron { 6 | /// Outlines this convex polyhedron’s shape using polylines. 7 | pub fn to_outline(&self) -> (Vec>, Vec<[u32; 3]>) { 8 | let mut indices = Vec::new(); 9 | 10 | for face in self.faces() { 11 | let i1 = face.first_vertex_or_edge; 12 | let i2 = i1 + face.num_vertices_or_edges; 13 | let first_id = self.vertices_adj_to_face()[i1 as usize] as u32; 14 | 15 | for idx in self.vertices_adj_to_face()[i1 as usize + 1..i2 as usize].windows(2) { 16 | indices.push([first_id, idx[0] as u32, idx[1] as u32]); 17 | } 18 | } 19 | 20 | (self.points().to_vec(), indices) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/transformation/to_outline/cuboid_to_outline.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Point, Real, Vector}; 3 | use crate::shape::Cuboid; 4 | use crate::transformation::utils; 5 | use alloc::{vec, vec::Vec}; 6 | 7 | impl Aabb { 8 | /// Outlines this Aabb’s shape using polylines. 9 | pub fn to_outline(&self) -> (Vec>, Vec<[u32; 2]>) { 10 | let center = self.center(); 11 | let half_extents = self.half_extents(); 12 | let mut cube_mesh = Cuboid::new(half_extents).to_outline(); 13 | cube_mesh.0.iter_mut().for_each(|p| *p += center.coords); 14 | cube_mesh 15 | } 16 | } 17 | 18 | impl Cuboid { 19 | /// Outlines this cuboid’s shape using polylines. 20 | pub fn to_outline(&self) -> (Vec>, Vec<[u32; 2]>) { 21 | let (vtx, idx) = unit_cuboid_outline(); 22 | (utils::scaled(vtx, self.half_extents * 2.0), idx) 23 | } 24 | } 25 | 26 | /** 27 | * Generates a cuboid shape with a split index buffer. 28 | * 29 | * The cuboid is centered at the origin, and has its half extents set to 0.5. 30 | */ 31 | fn unit_cuboid_outline() -> (Vec>, Vec<[u32; 2]>) { 32 | let aabb = Aabb::from_half_extents(Point::origin(), Vector::repeat(0.5)); 33 | ( 34 | aabb.vertices().to_vec(), 35 | vec![ 36 | [0, 1], 37 | [1, 2], 38 | [2, 3], 39 | [3, 0], 40 | [4, 5], 41 | [5, 6], 42 | [6, 7], 43 | [7, 4], 44 | [0, 4], 45 | [1, 5], 46 | [2, 6], 47 | [3, 7], 48 | ], 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /src/transformation/to_outline/cylinder_to_outline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::Cylinder; 3 | use crate::transformation::utils; 4 | use alloc::{vec, vec::Vec}; 5 | use na::{self, Point3, Vector3}; 6 | 7 | impl Cylinder { 8 | /// Outlines this cylinder’s shape using polylines. 9 | pub fn to_outline(&self, nsubdiv: u32) -> (Vec>, Vec<[u32; 2]>) { 10 | let diameter = self.radius * 2.0; 11 | let height = self.half_height * 2.0; 12 | let scale = Vector3::new(diameter, height, diameter); 13 | let (vtx, idx) = unit_cylinder_outline(nsubdiv); 14 | (utils::scaled(vtx, scale), idx) 15 | } 16 | } 17 | 18 | /// Generates a cylinder with unit height and diameter. 19 | fn unit_cylinder_outline(nsubdiv: u32) -> (Vec>, Vec<[u32; 2]>) { 20 | let mut out_vtx = vec![Point3::new(-0.5, -0.5, 0.0), Point3::new(-0.5, 0.5, 0.0)]; 21 | let mut out_idx = vec![]; 22 | #[allow(clippy::single_range_in_vec_init)] // The single range is on purpose. 23 | utils::apply_revolution(false, false, &[0..2], nsubdiv, &mut out_vtx, &mut out_idx); 24 | (out_vtx, out_idx) 25 | } 26 | -------------------------------------------------------------------------------- /src/transformation/to_outline/heightfield_to_outline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::{HeightField, HeightFieldCellStatus}; 3 | use na::Point3; 4 | 5 | impl HeightField { 6 | /// Outlines this heightfield’s shape using polylines. 7 | pub fn to_outline(&self) -> (Vec>, Vec<[u32; 2]>) { 8 | todo!() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/transformation/to_outline/mod.rs: -------------------------------------------------------------------------------- 1 | mod ball_to_outline; 2 | mod capsule_to_outline; 3 | mod cone_to_outline; 4 | // mod convex_polyhedron_to_outline; 5 | mod cuboid_to_outline; 6 | mod cylinder_to_outline; 7 | mod round_cone_to_outline; 8 | mod round_convex_polyhedron_to_outline; 9 | mod round_cuboid_to_outline; 10 | mod round_cylinder_to_outline; 11 | mod voxels_to_outline; 12 | // mod round_triangle_to_outline; 13 | // mod heightfield_to_outline; 14 | -------------------------------------------------------------------------------- /src/transformation/to_outline/round_cone_to_outline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::RoundCone; 3 | use crate::transformation::utils; 4 | use alloc::{vec, vec::Vec}; 5 | use na::{self, Point3, Vector3}; 6 | 7 | impl RoundCone { 8 | /// Outlines this round cone’s shape using polylines. 9 | pub fn to_outline( 10 | &self, 11 | nsubdiv: u32, 12 | border_nsubdiv: u32, 13 | ) -> (Vec>, Vec<[u32; 2]>) { 14 | let r = self.inner_shape.radius; 15 | let br = self.border_radius; 16 | let he = self.inner_shape.half_height; 17 | 18 | let mut out_vtx = vec![]; 19 | let mut out_idx = vec![]; 20 | 21 | // Compute the profile. 22 | let center_ab = Point3::new(-r, -he, 0.0); 23 | let center_cd = Point3::new(0.0, he, 0.0); 24 | let side_dir = Vector3::new(-2.0 * he, r, 0.0).normalize(); 25 | 26 | let a = Point3::new(-r, -he - br, 0.0); 27 | let b = Point3::new(-r, -he, 0.0) + side_dir * br; 28 | let c = Point3::new(0.0, he, 0.0) + side_dir * br; 29 | let d = Point3::new(0.0, he + br, 0.0); 30 | 31 | out_vtx.push(a); 32 | utils::push_arc(center_ab, a, b, border_nsubdiv, &mut out_vtx); 33 | out_vtx.push(b); 34 | out_vtx.push(c); 35 | utils::push_arc(center_cd, c, d, border_nsubdiv, &mut out_vtx); 36 | out_vtx.push(d); 37 | 38 | let circles = [ 39 | 0..1, 40 | border_nsubdiv..border_nsubdiv + 2, 41 | border_nsubdiv * 2 + 1..border_nsubdiv * 2 + 2, 42 | ]; 43 | utils::apply_revolution(false, true, &circles, nsubdiv, &mut out_vtx, &mut out_idx); 44 | (out_vtx, out_idx) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/transformation/to_outline/round_cylinder_to_outline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::RoundCylinder; 3 | use crate::transformation::utils; 4 | use alloc::{vec, vec::Vec}; 5 | use na::{self, Point3}; 6 | 7 | impl RoundCylinder { 8 | /// Outlines this round cylinder’s shape using polylines. 9 | pub fn to_outline( 10 | &self, 11 | nsubdiv: u32, 12 | border_nsubdiv: u32, 13 | ) -> (Vec>, Vec<[u32; 2]>) { 14 | let r = self.inner_shape.radius; 15 | let br = self.border_radius; 16 | let he = self.inner_shape.half_height; 17 | 18 | let mut out_vtx = vec![]; 19 | let mut out_idx = vec![]; 20 | 21 | // Compute the profile. 22 | let center_ab = Point3::new(-r, -he, 0.0); 23 | let center_cd = Point3::new(-r, he, 0.0); 24 | let a = Point3::new(-r, -he - br, 0.0); 25 | let b = Point3::new(-r - br, -he, 0.0); 26 | let c = Point3::new(-r - br, he, 0.0); 27 | let d = Point3::new(-r, he + br, 0.0); 28 | 29 | out_vtx.push(a); 30 | utils::push_arc(center_ab, a, b, border_nsubdiv, &mut out_vtx); 31 | out_vtx.push(b); 32 | out_vtx.push(c); 33 | utils::push_arc(center_cd, c, d, border_nsubdiv, &mut out_vtx); 34 | out_vtx.push(d); 35 | 36 | let circles = [ 37 | 0..1, 38 | border_nsubdiv..border_nsubdiv + 2, 39 | border_nsubdiv * 2 + 1..border_nsubdiv * 2 + 2, 40 | ]; 41 | utils::apply_revolution(false, false, &circles, nsubdiv, &mut out_vtx, &mut out_idx); 42 | (out_vtx, out_idx) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/transformation/to_outline/round_triangle_to_outline.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Point, Real, Vector}; 3 | use crate::shape::RoundTriangle; 4 | use crate::transformation::utils; 5 | 6 | impl RoundTriangle { 7 | /// Outlines this round triangle’s surface with polylines. 8 | pub fn to_outline(&self, nsubdivs: u32) -> (Vec>, Vec<[u32; 2]>) { 9 | let tri = &self.inner_shape; 10 | let n = tri 11 | .normal() 12 | .map(|n| n.into_inner()) 13 | .unwrap_or_else(|| Vector::zeros()); 14 | let mut out_vtx = vec![ 15 | tri.a + n * self.border_radius, 16 | tri.b + n * self.border_radius, 17 | tri.c + n * self.border_radius, 18 | tri.a - n * self.border_radius, 19 | tri.b - n * self.border_radius, 20 | tri.c - n * self.border_radius, 21 | ]; 22 | let mut out_idx = vec![[0, 1], [1, 2], [2, 0], [3, 4], [4, 5], [5, 3]]; 23 | let ab = tri.b - tri.a; 24 | let ac = tri.c - tri.a; 25 | let bc = tri.b - tri.c; 26 | 27 | utils::push_arc_and_idx(tri.a, 0, 3, nsubdivs, &mut out_vtx, &mut out_idx); 28 | utils::push_arc_and_idx(tri.b, 1, 4, nsubdivs, &mut out_vtx, &mut out_idx); 29 | utils::push_arc_and_idx(tri.c, 2, 5, nsubdivs, &mut out_vtx, &mut out_idx); 30 | 31 | (out_vtx, out_idx) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/transformation/to_polyline/ball_to_polyline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::Ball; 3 | use crate::transformation::utils; 4 | use alloc::vec::Vec; 5 | use na::{self, Point2, RealField}; 6 | 7 | impl Ball { 8 | /// Discretize the boundary of this ball as a polygonal line. 9 | pub fn to_polyline(&self, nsubdivs: u32) -> Vec> { 10 | let diameter = self.radius * 2.0; 11 | let two_pi = Real::two_pi(); 12 | let dtheta = two_pi / (nsubdivs as Real); 13 | 14 | let mut pts = Vec::with_capacity(nsubdivs as usize); 15 | utils::push_xy_arc(diameter / 2.0, nsubdivs, dtheta, &mut pts); 16 | 17 | pts 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/transformation/to_polyline/capsule_to_polyline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::Capsule; 3 | use crate::transformation::utils; 4 | use alloc::vec::Vec; 5 | use na::{self, Point2, RealField, Vector2}; 6 | 7 | impl Capsule { 8 | /// Discretize the boundary of this capsule as a polygonal line. 9 | pub fn to_polyline(&self, nsubdiv: u32) -> Vec> { 10 | let pi = Real::pi(); 11 | let dtheta = pi / (nsubdiv as Real); 12 | 13 | let mut points: Vec> = Vec::with_capacity(nsubdiv as usize); 14 | 15 | utils::push_xy_arc(self.radius, nsubdiv, dtheta, &mut points); 16 | 17 | let npoints = points.len(); 18 | 19 | for i in 0..npoints { 20 | let new_point = points[i] + Vector2::new(na::zero(), self.half_height()); 21 | 22 | points.push(-new_point); 23 | points[i] = new_point; 24 | } 25 | 26 | utils::transformed(points, self.canonical_transform()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/transformation/to_polyline/cuboid_to_polyline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::Cuboid; 3 | use crate::transformation::utils; 4 | use alloc::{vec, vec::Vec}; 5 | use na::{self, Point2}; 6 | 7 | impl Cuboid { 8 | /// Discretize the boundary of this cuboid as a polygonal line. 9 | pub fn to_polyline(&self) -> Vec> { 10 | utils::scaled(unit_rectangle(), self.half_extents * 2.0) 11 | } 12 | } 13 | 14 | /// The contour of a unit cuboid lying on the x-y plane. 15 | fn unit_rectangle() -> Vec> { 16 | vec![ 17 | Point2::new(-0.5, -0.5), 18 | Point2::new(0.5, -0.5), 19 | Point2::new(0.5, 0.5), 20 | Point2::new(-0.5, 0.5), 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/transformation/to_polyline/heightfield_to_polyline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::HeightField; 3 | use alloc::{vec, vec::Vec}; 4 | use na::Point2; 5 | 6 | impl HeightField { 7 | /// Rasterize this heightfield as a (potentially discontinuous) polyline. 8 | pub fn to_polyline(&self) -> (Vec>, Vec<[u32; 2]>) { 9 | let mut vertices = vec![]; 10 | let mut indices = vec![]; 11 | 12 | for seg in self.segments() { 13 | let base_id = vertices.len() as u32; 14 | if vertices.last().map(|pt| seg.a != *pt).unwrap_or(true) { 15 | indices.push([base_id, base_id + 1]); 16 | vertices.push(seg.a); 17 | } else { 18 | indices.push([base_id - 1, base_id]); 19 | } 20 | 21 | vertices.push(seg.b); 22 | } 23 | 24 | (vertices, indices) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/transformation/to_polyline/mod.rs: -------------------------------------------------------------------------------- 1 | mod ball_to_polyline; 2 | mod capsule_to_polyline; 3 | mod cuboid_to_polyline; 4 | mod heightfield_to_polyline; 5 | mod round_convex_polygon_to_polyline; 6 | mod round_cuboid_to_polyline; 7 | mod voxels_to_polyline; 8 | -------------------------------------------------------------------------------- /src/transformation/to_polyline/round_convex_polygon_to_polyline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::RoundConvexPolygon; 3 | use crate::transformation::utils; 4 | use alloc::{vec, vec::Vec}; 5 | use na::{self, Point2}; 6 | 7 | impl RoundConvexPolygon { 8 | /// Discretize the boundary of this round convex polygon as a polygonal line. 9 | pub fn to_polyline(&self, border_subdivs: u32) -> Vec> { 10 | let mut out_vtx = vec![]; 11 | let pts = self.inner_shape.points(); 12 | let ns = self.inner_shape.normals(); 13 | let br = self.border_radius; 14 | 15 | out_vtx.push(pts[0] + **ns.last().unwrap() * br); 16 | 17 | for ia in 0..pts.len() - 1 { 18 | let ib = ia + 1; 19 | 20 | let arc_start = *out_vtx.last().unwrap(); 21 | let arc_end = pts[ia] + *ns[ia] * br; 22 | utils::push_arc(pts[ia], arc_start, arc_end, border_subdivs, &mut out_vtx); 23 | out_vtx.push(arc_end); 24 | out_vtx.push(pts[ib] + *ns[ia] * br); 25 | } 26 | 27 | let arc_center = *pts.last().unwrap(); 28 | let arc_start = *out_vtx.last().unwrap(); 29 | let arc_end = arc_center + **ns.last().unwrap() * br; 30 | utils::push_arc(arc_center, arc_start, arc_end, border_subdivs, &mut out_vtx); 31 | out_vtx.push(arc_end); 32 | 33 | out_vtx 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/transformation/to_polyline/round_cuboid_to_polyline.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::RoundCuboid; 3 | use crate::transformation::utils; 4 | use alloc::{vec, vec::Vec}; 5 | use na::{self, Point2}; 6 | 7 | impl RoundCuboid { 8 | /// Discretize the boundary of this round cuboid as a polygonal line. 9 | pub fn to_polyline(&self, border_subdivs: u32) -> Vec> { 10 | let mut out_vtx = vec![]; 11 | let he = self.inner_shape.half_extents; 12 | let br = self.border_radius; 13 | 14 | let arc_centers = [ 15 | Point2::new(-he.x, -he.y), 16 | Point2::new(he.x, -he.y), 17 | Point2::new(he.x, he.y), 18 | Point2::new(-he.x, he.y), 19 | ]; 20 | let arc_vertices = [ 21 | ( 22 | Point2::new(-he.x - br, -he.y), 23 | Point2::new(-he.x, -he.y - br), 24 | ), 25 | (Point2::new(he.x, -he.y - br), Point2::new(he.x + br, -he.y)), 26 | (Point2::new(he.x + br, he.y), Point2::new(he.x, he.y + br)), 27 | (Point2::new(-he.x, he.y + br), Point2::new(-he.x - br, he.y)), 28 | ]; 29 | 30 | for i in 0..4 { 31 | out_vtx.push(arc_vertices[i].0); 32 | utils::push_arc( 33 | arc_centers[i], 34 | arc_vertices[i].0, 35 | arc_vertices[i].1, 36 | border_subdivs, 37 | &mut out_vtx, 38 | ); 39 | out_vtx.push(arc_vertices[i].1); 40 | } 41 | 42 | out_vtx 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/transformation/to_trimesh/cone_to_trimesh.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::Cone; 3 | use crate::transformation::utils; 4 | use alloc::vec::Vec; 5 | use na::{self, Point3, RealField, Vector3}; 6 | 7 | impl Cone { 8 | /// Discretize the boundary of this cone as a triangle-mesh. 9 | pub fn to_trimesh(&self, nsubdiv: u32) -> (Vec>, Vec<[u32; 3]>) { 10 | let diameter = self.radius * 2.0; 11 | let height = self.half_height * 2.0; 12 | let scale = Vector3::new(diameter, height, diameter); 13 | let (vtx, idx) = unit_cone(nsubdiv); 14 | (utils::scaled(vtx, scale), idx) 15 | } 16 | } 17 | 18 | /// Generates a cone with unit height and diameter. 19 | fn unit_cone(nsubdiv: u32) -> (Vec>, Vec<[u32; 3]>) { 20 | let two_pi = Real::two_pi(); 21 | let dtheta = two_pi / (nsubdiv as Real); 22 | let mut coords = Vec::new(); 23 | let mut indices = Vec::new(); 24 | 25 | utils::push_circle( 26 | na::convert(0.5), 27 | nsubdiv, 28 | dtheta, 29 | na::convert(-0.5), 30 | &mut coords, 31 | ); 32 | 33 | coords.push(Point3::new(0.0, 0.5, 0.0)); 34 | 35 | utils::push_degenerate_top_ring_indices(0, coords.len() as u32 - 1, nsubdiv, &mut indices); 36 | utils::push_filled_circle_indices(0, nsubdiv, &mut indices); 37 | 38 | (coords, indices) 39 | } 40 | -------------------------------------------------------------------------------- /src/transformation/to_trimesh/convex_polyhedron_to_trimesh.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::ConvexPolyhedron; 3 | use alloc::vec::Vec; 4 | use na::Point3; 5 | 6 | impl ConvexPolyhedron { 7 | /// Discretize the boundary of this convex polyhedron as a triangle-mesh. 8 | pub fn to_trimesh(&self) -> (Vec>, Vec<[u32; 3]>) { 9 | let mut indices = Vec::new(); 10 | 11 | for face in self.faces() { 12 | let i1 = face.first_vertex_or_edge; 13 | let i2 = i1 + face.num_vertices_or_edges; 14 | let first_id = self.vertices_adj_to_face()[i1 as usize]; 15 | 16 | for idx in self.vertices_adj_to_face()[i1 as usize + 1..i2 as usize].windows(2) { 17 | indices.push([first_id, idx[0], idx[1]]); 18 | } 19 | } 20 | 21 | (self.points().to_vec(), indices) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/transformation/to_trimesh/cylinder_to_trimesh.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::Cylinder; 3 | use crate::transformation::utils; 4 | use alloc::vec::Vec; 5 | use na::{self, Point3, RealField, Vector3}; 6 | 7 | impl Cylinder { 8 | /// Discretize the boundary of this cylinder as a triangle-mesh. 9 | pub fn to_trimesh(&self, nsubdiv: u32) -> (Vec>, Vec<[u32; 3]>) { 10 | let diameter = self.radius * 2.0; 11 | let height = self.half_height * 2.0; 12 | let scale = Vector3::new(diameter, height, diameter); 13 | let (vtx, idx) = unit_cylinder(nsubdiv); 14 | (utils::scaled(vtx, scale), idx) 15 | } 16 | } 17 | 18 | /// Generates a cylinder with unit height and diameter. 19 | fn unit_cylinder(nsubdiv: u32) -> (Vec>, Vec<[u32; 3]>) { 20 | let two_pi = Real::two_pi(); 21 | let invsubdiv = 1.0 / (nsubdiv as Real); 22 | let dtheta = two_pi * invsubdiv; 23 | let mut coords = Vec::new(); 24 | let mut indices = Vec::new(); 25 | 26 | utils::push_circle( 27 | na::convert(0.5), 28 | nsubdiv, 29 | dtheta, 30 | na::convert(-0.5), 31 | &mut coords, 32 | ); 33 | 34 | utils::push_circle( 35 | na::convert(0.5), 36 | nsubdiv, 37 | dtheta, 38 | na::convert(0.5), 39 | &mut coords, 40 | ); 41 | 42 | utils::push_ring_indices(0, nsubdiv, nsubdiv, &mut indices); 43 | utils::push_filled_circle_indices(0, nsubdiv, &mut indices); 44 | utils::push_filled_circle_indices(nsubdiv, nsubdiv, &mut indices); 45 | 46 | let len = indices.len(); 47 | let bottom_start_id = len - (nsubdiv as usize - 2); 48 | utils::reverse_clockwising(&mut indices[bottom_start_id..]); 49 | 50 | (coords, indices) 51 | } 52 | -------------------------------------------------------------------------------- /src/transformation/to_trimesh/heightfield_to_trimesh.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use crate::shape::HeightField; 3 | use alloc::vec::Vec; 4 | use na::Point3; 5 | 6 | impl HeightField { 7 | /// Discretize the boundary of this heightfield as a triangle-mesh. 8 | pub fn to_trimesh(&self) -> (Vec>, Vec<[u32; 3]>) { 9 | let mut vertices = Vec::new(); 10 | let mut indices = Vec::new(); 11 | 12 | for (i, tri) in self.triangles().enumerate() { 13 | vertices.push(tri.a); 14 | vertices.push(tri.b); 15 | vertices.push(tri.c); 16 | 17 | let i = i as u32; 18 | indices.push([i * 3, i * 3 + 1, i * 3 + 2]) 19 | } 20 | 21 | (vertices, indices) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/transformation/to_trimesh/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "dim3")] 2 | mod ball_to_trimesh; 3 | #[cfg(feature = "dim3")] 4 | mod capsule_to_trimesh; 5 | #[cfg(feature = "dim3")] 6 | mod cone_to_trimesh; 7 | #[cfg(feature = "dim3")] 8 | mod convex_polyhedron_to_trimesh; 9 | mod cuboid_to_trimesh; 10 | #[cfg(feature = "dim3")] 11 | mod cylinder_to_trimesh; 12 | #[cfg(feature = "dim3")] 13 | mod heightfield_to_trimesh; 14 | #[cfg(feature = "dim3")] 15 | mod voxels_to_trimesh; 16 | -------------------------------------------------------------------------------- /src/transformation/to_trimesh/voxels_to_trimesh.rs: -------------------------------------------------------------------------------- 1 | use crate::bounding_volume::Aabb; 2 | use crate::math::{Point, Real}; 3 | use crate::shape::Voxels; 4 | use alloc::{vec, vec::Vec}; 5 | 6 | impl Voxels { 7 | /// Computes an unoptimized mesh representation of this shape. 8 | /// 9 | /// Each free face of each voxel will result in two triangles. No effort is made to merge 10 | /// adjacent triangles on large flat areas. 11 | pub fn to_trimesh(&self) -> (Vec>, Vec<[u32; 3]>) { 12 | let aabb = Aabb::from_half_extents(Point::origin(), self.voxel_size() / 2.0); 13 | let aabb_vtx = aabb.vertices(); 14 | 15 | let mut vtx = vec![]; 16 | let mut idx = vec![]; 17 | for vox in self.voxels() { 18 | let mask = vox.state.free_faces(); 19 | for i in 0..6 { 20 | if mask.bits() & (1 << i) != 0 { 21 | let fvid = Aabb::FACES_VERTEX_IDS[i]; 22 | let base_id = vtx.len() as u32; 23 | vtx.push(vox.center + aabb_vtx[fvid.0].coords); 24 | vtx.push(vox.center + aabb_vtx[fvid.1].coords); 25 | vtx.push(vox.center + aabb_vtx[fvid.2].coords); 26 | vtx.push(vox.center + aabb_vtx[fvid.3].coords); 27 | 28 | if i % 2 == 0 { 29 | idx.push([base_id, base_id + 1, base_id + 2]); 30 | idx.push([base_id, base_id + 2, base_id + 3]); 31 | } else { 32 | idx.push([base_id, base_id + 2, base_id + 1]); 33 | idx.push([base_id, base_id + 3, base_id + 2]); 34 | } 35 | } 36 | } 37 | } 38 | 39 | (vtx, idx) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/transformation/vhacd/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::parameters::VHACDParameters; 2 | pub use self::vhacd::VHACD; 3 | 4 | pub(crate) use self::vhacd::CutPlane; 5 | 6 | mod parameters; 7 | mod vhacd; 8 | -------------------------------------------------------------------------------- /src/transformation/voxelization/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::voxel_set::{Voxel, VoxelSet}; 2 | pub use self::voxelized_volume::{FillMode, VoxelValue, VoxelizedVolume}; 3 | 4 | mod voxel_set; 5 | mod voxelized_volume; 6 | -------------------------------------------------------------------------------- /src/transformation/wavefront.rs: -------------------------------------------------------------------------------- 1 | use crate::shape::TriMesh; 2 | use alloc::{string::ToString, vec}; 3 | use obj::{Group, IndexTuple, ObjData, ObjError, Object, SimplePolygon}; 4 | use std::path::PathBuf; 5 | 6 | impl TriMesh { 7 | /// Outputs a Wavefront (`.obj`) file at the given path. 8 | /// 9 | /// This function is enabled by the `wavefront` feature flag. 10 | pub fn to_obj_file(&self, path: &PathBuf) -> Result<(), ObjError> { 11 | let mut file = std::fs::File::create(path).unwrap(); 12 | 13 | ObjData { 14 | #[expect(clippy::unnecessary_cast)] 15 | position: self 16 | .vertices() 17 | .iter() 18 | .map(|v| [v.x as f32, v.y as f32, v.z as f32]) 19 | .collect(), 20 | objects: vec![Object { 21 | groups: vec![Group { 22 | polys: self 23 | .indices() 24 | .iter() 25 | .map(|tri| { 26 | SimplePolygon(vec![ 27 | IndexTuple(tri[0] as usize, None, None), 28 | IndexTuple(tri[1] as usize, None, None), 29 | IndexTuple(tri[2] as usize, None, None), 30 | ]) 31 | }) 32 | .collect(), 33 | name: "".to_string(), 34 | index: 0, 35 | material: None, 36 | }], 37 | name: "".to_string(), 38 | }], 39 | ..Default::default() 40 | } 41 | .write_to_buf(&mut file) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/utils/as_bytes.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::slice; 3 | 4 | use na::{Point2, Point3, Vector2, Vector3}; 5 | use simba::scalar::RealField; 6 | 7 | /// Trait that transforms thing to a slice of u8. 8 | pub trait AsBytes { 9 | /// Converts `self` to a slice of bytes. 10 | fn as_bytes(&self) -> &[u8]; 11 | } 12 | 13 | macro_rules! generic_as_bytes_impl( 14 | ($T: ident, $dimension: expr) => ( 15 | impl AsBytes for $T { 16 | #[inline(always)] 17 | fn as_bytes(&self) -> &[u8] { 18 | unsafe { 19 | slice::from_raw_parts(self as *const $T as *const u8, mem::size_of::() * $dimension) 20 | } 21 | } 22 | } 23 | ) 24 | ); 25 | 26 | generic_as_bytes_impl!(Vector2, 2); 27 | generic_as_bytes_impl!(Point2, 2); 28 | generic_as_bytes_impl!(Vector3, 3); 29 | generic_as_bytes_impl!(Point3, 3); 30 | 31 | // TODO: implement for all `T: Copy` instead? 32 | -------------------------------------------------------------------------------- /src/utils/ccw_face_normal.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real, Vector}; 2 | use na::Unit; 3 | 4 | /// Computes the direction pointing toward the right-hand-side of an oriented segment. 5 | /// 6 | /// Returns `None` if the segment is degenerate. 7 | #[inline] 8 | #[cfg(feature = "dim2")] 9 | pub fn ccw_face_normal(pts: [&Point; 2]) -> Option>> { 10 | let ab = pts[1] - pts[0]; 11 | let res = Vector::new(ab[1], -ab[0]); 12 | 13 | Unit::try_new(res, crate::math::DEFAULT_EPSILON) 14 | } 15 | 16 | /// Computes the normal of a counter-clock-wise triangle. 17 | /// 18 | /// Returns `None` if the triangle is degenerate. 19 | #[inline] 20 | #[cfg(feature = "dim3")] 21 | pub fn ccw_face_normal(pts: [&Point; 3]) -> Option>> { 22 | let ab = pts[1] - pts[0]; 23 | let ac = pts[2] - pts[0]; 24 | let res = ab.cross(&ac); 25 | 26 | Unit::try_new(res, crate::math::DEFAULT_EPSILON) 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/center.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real}; 2 | use na; 3 | 4 | /// Computes the center of a set of point. 5 | #[inline] 6 | pub fn center(pts: &[Point]) -> Point { 7 | assert!( 8 | !pts.is_empty(), 9 | "Cannot compute the center of less than 1 point." 10 | ); 11 | 12 | let denom: Real = na::convert::(1.0 / (pts.len() as f64)); 13 | 14 | let mut piter = pts.iter(); 15 | let mut res = *piter.next().unwrap() * denom; 16 | 17 | for pt in piter { 18 | res += pt.coords * denom; 19 | } 20 | 21 | res 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/cleanup.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real}; 2 | use alloc::vec::Vec; 3 | use core::iter; 4 | 5 | /// Given an index buffer, remove from `points` every point that is not indexed. 6 | pub fn remove_unused_points(points: &mut Vec>, idx: &mut [[u32; 3]]) { 7 | let mut used: Vec = iter::repeat_n(false, points.len()).collect(); 8 | let mut remap: Vec = (0..points.len()).collect(); 9 | let used = &mut used[..]; 10 | let remap = &mut remap[..]; 11 | 12 | for i in idx.iter() { 13 | used[i[0] as usize] = true; 14 | used[i[1] as usize] = true; 15 | used[i[2] as usize] = true; 16 | } 17 | 18 | let mut i = 0; 19 | while i != points.len() { 20 | if !used[i] { 21 | let _ = points.swap_remove(i); 22 | remap[points.len()] = i; 23 | used[i] = used[points.len()]; 24 | } else { 25 | i += 1; 26 | } 27 | } 28 | 29 | for id in idx.iter_mut() { 30 | id[0] = remap[id[0] as usize] as u32; 31 | id[1] = remap[id[1] as usize] as u32; 32 | id[2] = remap[id[2] as usize] as u32; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/consts.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::excessive_precision)] // Clippy will complain in f32 mode, but we also need these consts for f64. 2 | 3 | use crate::math::Real; 4 | 5 | // pub(crate) const SIN_10_DEGREES: Real = 0.17364817766; 6 | // pub(crate) const COS_10_DEGREES: Real = 0.98480775301; 7 | // pub(crate) const COS_45_DEGREES: Real = 0.70710678118; 8 | // pub(crate) const SIN_45_DEGREES: Real = COS_45_DEGREES; 9 | pub(crate) const COS_1_DEGREES: Real = 0.99984769515; 10 | // pub(crate) const COS_5_DEGREES: Real = 0.99619469809; 11 | pub(crate) const COS_FRAC_PI_8: Real = 0.92387953251; 12 | #[cfg(feature = "dim2")] 13 | pub(crate) const SIN_FRAC_PI_8: Real = 0.38268343236; 14 | -------------------------------------------------------------------------------- /src/utils/cov.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Matrix, Point, Real}; 2 | 3 | /// Computes the covariance matrix of a set of points. 4 | pub fn cov(pts: &[Point]) -> Matrix { 5 | center_cov(pts).1 6 | } 7 | 8 | /// Computes the center and the covariance matrix of a set of points. 9 | pub fn center_cov(pts: &[Point]) -> (Point, Matrix) { 10 | let center = crate::utils::center(pts); 11 | let mut cov: Matrix = na::zero(); 12 | let normalizer: Real = 1.0 / (pts.len() as Real); 13 | 14 | for p in pts.iter() { 15 | let cp = *p - center; 16 | // NOTE: this is more numerically stable than using cov.syger. 17 | cov += cp * (cp * normalizer).transpose(); 18 | } 19 | 20 | (center, cov) 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/deterministic_state.rs: -------------------------------------------------------------------------------- 1 | use core::hash::BuildHasher; 2 | use std::collections::hash_map::DefaultHasher; 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() -> DeterministicState { 11 | DeterministicState 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/hashable_partial_eq.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::AsBytes; 2 | use core::hash::{Hash, Hasher}; 3 | 4 | /// A structure that implements `Eq` and is hashable even if the wrapped data implements only 5 | /// `PartialEq`. 6 | #[derive(PartialEq, Clone, Debug)] 7 | pub struct HashablePartialEq { 8 | value: T, 9 | } 10 | 11 | impl HashablePartialEq { 12 | /// Creates a new `HashablePartialEq`. Please make sure that you really 13 | /// want to transform the wrapped object's partial equality to an equivalence relation. 14 | pub fn new(value: T) -> HashablePartialEq { 15 | HashablePartialEq { value } 16 | } 17 | 18 | /// Gets the wrapped value. 19 | pub fn unwrap(self) -> T { 20 | self.value 21 | } 22 | } 23 | 24 | impl Eq for HashablePartialEq {} 25 | 26 | impl Hash for HashablePartialEq { 27 | #[inline] 28 | fn hash(&self, state: &mut H) { 29 | state.write(self.value.as_bytes()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/inv.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | 3 | pub fn inv(val: Real) -> Real { 4 | if val == 0.0 { 5 | 0.0 6 | } else { 7 | 1.0 / val 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/median.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use na; 3 | 4 | /// Computes the median of a set of values. 5 | #[inline] 6 | pub fn median(vals: &mut [Real]) -> Real { 7 | assert!( 8 | !vals.is_empty(), 9 | "Cannot compute the median of zero values." 10 | ); 11 | 12 | vals.sort_by(|a, b| a.partial_cmp(b).unwrap()); 13 | 14 | let n = vals.len(); 15 | 16 | if n % 2 == 0 { 17 | (vals[n / 2 - 1] + vals[n / 2]) / na::convert::(2.0) 18 | } else { 19 | vals[n / 2] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/obb.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Isometry, Point, Real, Rotation, Translation, Vector, DIM}; 2 | use crate::shape::Cuboid; 3 | 4 | /// Computes an oriented bounding box for the given set of points. 5 | /// 6 | /// The returned OBB is not guaranteed to be the smallest enclosing OBB. 7 | /// Though it should be a pretty good on for most purposes. 8 | pub fn obb(pts: &[Point]) -> (Isometry, Cuboid) { 9 | let cov = crate::utils::cov(pts); 10 | let mut eigv = cov.symmetric_eigen().eigenvectors; 11 | 12 | if eigv.determinant() < 0.0 { 13 | eigv = -eigv; 14 | } 15 | 16 | let mut mins = Vector::repeat(Real::MAX); 17 | let mut maxs = Vector::repeat(-Real::MAX); 18 | 19 | for pt in pts { 20 | for i in 0..DIM { 21 | let dot = eigv.column(i).dot(&pt.coords); 22 | mins[i] = mins[i].min(dot); 23 | maxs[i] = maxs[i].max(dot); 24 | } 25 | } 26 | 27 | #[cfg(feature = "dim2")] 28 | let rot = Rotation::from_rotation_matrix(&na::Rotation2::from_matrix_unchecked(eigv)); 29 | #[cfg(feature = "dim3")] 30 | let rot = Rotation::from_rotation_matrix(&na::Rotation3::from_matrix_unchecked(eigv)); 31 | 32 | ( 33 | rot * Translation::from((maxs + mins) / 2.0), 34 | Cuboid::new((maxs - mins) / 2.0), 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /src/utils/point_cloud_support_point.rs: -------------------------------------------------------------------------------- 1 | use crate::math::{Point, Real, Vector}; 2 | 3 | /// Computes the index of the support point of a cloud of points. 4 | #[inline] 5 | pub fn point_cloud_support_point_id(dir: &Vector, points: &[Point]) -> usize { 6 | let mut best_pt = 0; 7 | let mut best_dot = points[0].coords.dot(dir); 8 | 9 | for (i, p) in points.iter().enumerate().skip(1) { 10 | let dot = p.coords.dot(dir); 11 | 12 | if dot > best_dot { 13 | best_dot = dot; 14 | best_pt = i; 15 | } 16 | } 17 | 18 | best_pt 19 | } 20 | 21 | /// Computes the support point of a cloud of points. 22 | #[inline] 23 | pub fn point_cloud_support_point(dir: &Vector, points: &[Point]) -> Point { 24 | points[point_cloud_support_point_id(dir, points)] 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/sort.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "dim3")] 2 | #[inline] 3 | /// Sorts a set of three values in increasing order. 4 | pub fn sort2(a: T, b: T) -> (T, T) { 5 | if a > b { 6 | (b, a) 7 | } else { 8 | (a, b) 9 | } 10 | } 11 | 12 | /// Sorts a set of three values in increasing order. 13 | #[inline] 14 | pub fn sort3<'a, T: PartialOrd + Copy>(a: &'a T, b: &'a T, c: &'a T) -> (&'a T, &'a T, &'a T) { 15 | let a_b = *a > *b; 16 | let a_c = *a > *c; 17 | let b_c = *b > *c; 18 | 19 | let sa; 20 | let sb; 21 | let sc; 22 | 23 | // Sort the three values. 24 | if a_b { 25 | // a > b 26 | if a_c { 27 | // a > c 28 | sc = a; 29 | 30 | if b_c { 31 | // b > c 32 | sa = c; 33 | sb = b; 34 | } else { 35 | // b <= c 36 | sa = b; 37 | sb = c; 38 | } 39 | } else { 40 | // a <= c 41 | sa = b; 42 | sb = a; 43 | sc = c; 44 | } 45 | } else { 46 | // a < b 47 | if !a_c { 48 | // a <= c 49 | sa = a; 50 | 51 | if b_c { 52 | // b > c 53 | sb = c; 54 | sc = b; 55 | } else { 56 | sb = b; 57 | sc = c; 58 | } 59 | } else { 60 | // a > c 61 | sa = c; 62 | sb = a; 63 | sc = b; 64 | } 65 | } 66 | 67 | (sa, sb, sc) 68 | } 69 | -------------------------------------------------------------------------------- /src/utils/sorted_pair.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::PartialOrd; 2 | use core::mem; 3 | use core::ops::Deref; 4 | 5 | /// A pair of elements sorted in increasing order. 6 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 7 | #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] 8 | #[cfg_attr( 9 | feature = "rkyv", 10 | derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize), 11 | archive(check_bytes) 12 | )] 13 | pub struct SortedPair([T; 2]); 14 | 15 | impl SortedPair { 16 | /// Sorts two elements in increasing order into a new pair. 17 | pub fn new(element1: T, element2: T) -> Self { 18 | if element1 > element2 { 19 | SortedPair([element2, element1]) 20 | } else { 21 | SortedPair([element1, element2]) 22 | } 23 | } 24 | } 25 | 26 | impl Deref for SortedPair { 27 | type Target = (T, T); 28 | 29 | fn deref(&self) -> &(T, T) { 30 | unsafe { mem::transmute(self) } 31 | } 32 | } 33 | 34 | // TODO: can we avoid these manual impls of Hash/PartialEq/Eq for the archived types? 35 | #[cfg(feature = "rkyv")] 36 | impl core::hash::Hash for ArchivedSortedPair 37 | where 38 | [::Archived; 2]: core::hash::Hash, 39 | { 40 | fn hash(&self, state: &mut H) { 41 | self.0.hash(state) 42 | } 43 | } 44 | 45 | #[cfg(feature = "rkyv")] 46 | impl PartialEq for ArchivedSortedPair 47 | where 48 | [::Archived; 2]: PartialEq, 49 | { 50 | fn eq(&self, other: &Self) -> bool { 51 | self.0 == other.0 52 | } 53 | } 54 | 55 | #[cfg(feature = "rkyv")] 56 | impl Eq for ArchivedSortedPair where 57 | [::Archived; 2]: Eq 58 | { 59 | } 60 | -------------------------------------------------------------------------------- /src/utils/spade.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | 3 | /// Ensures the given coordinate doesn’t go out of the bounds of spade’s acceptable values. 4 | /// 5 | /// Returns 0.0 if the coordinate is smaller than `spade::MIN_ALLOWED_VALUE`. 6 | /// Returns `spade::MAX_ALLOWED_VALUE` the coordinate is larger than `spade::MAX_ALLOWED_VALUE`. 7 | pub fn sanitize_spade_coord(coord: Real) -> Real { 8 | let abs = coord.abs(); 9 | 10 | #[allow(clippy::unnecessary_cast)] 11 | if abs as f64 <= spade::MIN_ALLOWED_VALUE { 12 | return 0.0; 13 | } 14 | 15 | #[cfg(feature = "f64")] 16 | if abs > spade::MAX_ALLOWED_VALUE { 17 | // This cannot happen in f32 since the max is 3.40282347E+38. 18 | return spade::MAX_ALLOWED_VALUE * coord.signum(); 19 | } 20 | 21 | coord 22 | } 23 | 24 | /// Ensures the coordinates of the given point don’t go out of the bounds of spade’s acceptable values. 25 | pub fn sanitize_spade_point(point: spade::Point2) -> spade::Point2 { 26 | spade::Point2::new(sanitize_spade_coord(point.x), sanitize_spade_coord(point.y)) 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/weighted_value.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use core::cmp::Ordering; 3 | 4 | #[derive(Copy, Clone)] 5 | pub struct WeightedValue { 6 | pub value: T, 7 | pub cost: Real, 8 | } 9 | 10 | impl WeightedValue { 11 | /// Creates a new reference packed with a cost value. 12 | #[inline] 13 | pub fn new(value: T, cost: Real) -> WeightedValue { 14 | WeightedValue { value, cost } 15 | } 16 | } 17 | 18 | impl PartialEq for WeightedValue { 19 | #[inline] 20 | fn eq(&self, other: &WeightedValue) -> bool { 21 | self.cost.eq(&other.cost) 22 | } 23 | } 24 | 25 | impl Eq for WeightedValue {} 26 | 27 | impl PartialOrd for WeightedValue { 28 | #[inline] 29 | fn partial_cmp(&self, other: &WeightedValue) -> Option { 30 | Some(self.cmp(other)) 31 | } 32 | } 33 | 34 | impl Ord for WeightedValue { 35 | #[inline] 36 | fn cmp(&self, other: &WeightedValue) -> Ordering { 37 | if self.cost < other.cost { 38 | Ordering::Less 39 | } else if self.cost > other.cost { 40 | Ordering::Greater 41 | } else { 42 | Ordering::Equal 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /write_examples.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | write_examples() { 4 | examples_path=$1 5 | output_path=$2 6 | 7 | echo >> $output_path 8 | 9 | find $examples_path -type f -iname '*.rs' -print0 | 10 | while IFS= read -r -d '' line; do 11 | example=$(basename ${line} .rs) 12 | echo "[[example]]" >> $output_path 13 | echo "name = \"$example\"" >> $output_path 14 | echo "path = \"examples/$example.rs\"" >> $output_path 15 | echo "doc-scrape-examples = true" >> $output_path 16 | echo >> $output_path 17 | done 18 | } 19 | 20 | write_examples ./crates/parry2d/examples ./crates/parry2d/Cargo.toml 21 | write_examples ./crates/parry3d/examples ./crates/parry3d/Cargo.toml 22 | --------------------------------------------------------------------------------