Repository navigation
Add per-primitive thickness to collision meshes - #287
Merged
Merged
Conversation
Each vertex, edge, and face of a CollisionMesh can carry a thickness, and a pair of primitives rests at the sum of theirs. - `CollisionMesh::set_thickness(vertex, edge, face)` sets it directly; `set_vertex_thickness` (edges and faces take their thickest vertex) and `set_face_thickness` (edges and vertices take their thickest face) derive it. `pair_thickness(candidate)` sums a pair's two sides. - Each candidate type has an inline `thickness(vertex, edge, face)` on the three thickness vectors, so the candidates do not depend on the mesh. - `NormalCollisions::build` sets each collision's `dmin` to its pair's thickness and activates it within `dhat` of that, including the sub-stencil a distance type reduces a face-vertex or edge-edge pair to. - `Candidates::build` inflates each vertex box by the largest thickness of the vertex and the edges and faces it bounds, so every edge and face box covers its primitive. This works with every broad phase unchanged. - CCD (`is_step_collision_free`, `compute_collision_free_stepsize`, `compute_cfl_stepsize`, `compute_per_vertex_safe_distances`) stops each pair at its own thickness. A thick mesh takes the CPU narrow phase rather than the GPU Sweep and Tiniest Queue pipeline, which has no thickness. - `TangentialCollision` keeps its normal collision's `dmin`, which the friction forces use for the normal force magnitude. The uniform `min_distance` and `dmin` parameters of these functions are now `std::optional<double>` and deprecated. A given value overrides the mesh's thickness and logs a warning once; otherwise each pair uses its thickness (zero without one). The per-pair narrow-phase functions keep a plain `double`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- semi_implicit_stiffness over a mesh takes an optional, deprecated dmin like the rest of the API and otherwise uses each stencil's own: a collision's dmin, or a candidate's thickness. It used one uniform dmin, so thick contacts got the stiffness of a pair at the surface. - The Python thickness(vertex, edge, face) checks its indices and raises IndexError instead of reading past an empty or short array. - Bind TangentialCollision.dmin in Python. - The collision builder sums a sub-stencil's thickness through CollisionMesh::pair_thickness on the reduced candidate instead of repeating each pair rule. - Document that CCD needs each vertex and edge no thicker than the thickest face or edge containing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #287 +/- ##
==========================================
+ Coverage 96.76% 96.78% +0.01%
==========================================
Files 223 224 +1
Lines 22538 22772 +234
Branches 1550 1577 +27
==========================================
+ Hits 21809 22039 +230
- Misses 729 733 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds per-primitive thickness to
CollisionMesh. Contacts (collisions/normal), the broad phase (candidates), CCD (ipc.hpp,Candidates), and friction (TangentialPotential) then keep each pair of primitives the sum of their thicknesses apart.dmin.NormalCollisions::buildsets each collision'sdminto its pair's thickness and activates it withindhatof that. When a distance type reduces a face-vertex pair to an edge-vertex or vertex-vertex collision (or an edge-edge pair to one of those), the thickness is that of the primitives the collision keeps. The barrier, adhesion, and friction potentials already readcollision.dmin.is_step_collision_free,compute_collision_free_stepsize,compute_cfl_stepsize, andcompute_per_vertex_safe_distancesstop each pair at its own thickness.Impacts:
dminfor the whole mesh. A panel seam is as thick as its thicker side.min_distanceanddminparameters are deprecated in favor of the mesh's thickness.API changes
CollisionMeshsetters:set_thickness(vertex, edge, face)sets all three.set_vertex_thickness(vertex): edges and faces take their thickest vertex.set_face_thickness(face, vertex = {}): edges and vertices take their thickest face. An edge on no face takes its thicker vertex.clear_thickness()removes it.CollisionMeshaccessors:has_thickness(),vertex_thickness(vi),edge_thickness(ei),face_thickness(fi), the*_thicknesses()vectors,max_thickness(), andpair_thickness(candidate). All return 0 (or empty) without a thickness.thickness(vertex_thickness, edge_thickness, face_thickness)that sums its two sides, so the candidates do not depend onCollisionMesh.TangentialCollision::dmin: thedminof the normal collision it was built from.build_vertex_boxes: new overloads take a per-vertex inflation on top ofinflation_radius.min_distanceanddmindefault toNone.💥 Breaking changes
min_distanceis nowstd::optional<double> = std::nulloptonis_step_collision_free,compute_collision_free_stepsize,Candidates::{is_step_collision_free, compute_collision_free_stepsize, compute_cfl_stepsize, compute_per_vertex_safe_distances}, andogc::TrustRegion::{warm_start_time_step, update, update_if_needed}.dminis nowstd::optional<double> = std::nulloptonNormalCollisions::build(both overloads) andTangentialPotential::{force, force_jacobian, gcp_force, gcp_force_jacobian}.doublecompile unchanged. Code that takes these functions' addresses with the old signatures (e.g.,py::overload_cast<..., const double, ...>) needsstd::optional<double>.TangentialPotential::forceandforce_jacobiannow default to each collision's owndmininstead of 0. Before, building collisions withdmin = dand callingforcewithout adminused 0 for the normal force magnitude. It now usesd, consistent with the barrier.*Candidate::ccd,NarrowPhaseCCD, the barrier and stiffness functions) keep a plaindouble. They receive the resolved value.min_distancetakes the CPU narrow phase incompute_collision_free_stepsize, even with theSweepAndTiniestQueuebroad phase. The GPU pipeline has no per-primitive thickness.Type of change
How Has This Been Tested?
tests/src/tests/collisions/test_thickness.cpp. Unless noted, each case runs on every broad phase.dminof 2τ ontwo-cubes-close.ply: the same collision counts, barrier energy, and gradient, for the IPC, improved max approximation, and OGC collision sets. The improved max approximation is compared at 1e-2, since two builds of the same set already differ by ~1e-3 in gradient.dhatof the sum. Every collision'sdminequals its pair's thickness, including distinct vertex, edge, and face values. An explicitdminoverrides the thickness.min_distanceoverrides it. The same holds foris_step_collision_free.dmin. The default friction force equals passing the pairs' thickness explicitly and differs from passingdmin = 0.test_collision_mesh.cpp: the derivation rules (seams, edges on no face), the candidates' sums, and rejection of wrong sizes, negative values, and NaN.python/tests/test_collision_mesh.py::test_thickness: the bindings, contact, CCD, and override, end to end.ipc_toolkit_tests "~[!benchmark]"(467 test cases) andpython -m nose2.Test Configuration:
IPC_TOOLKIT_WITH_CUDA=ON, Release, RTX 5080)Checklist