Repository navigation
Conversation
- Added InertialTerm class for computing energy, gradient, and Hessian of rigid bodies. - Implemented mass properties computation for 2D and 3D meshes, including point clouds. - Created pose representation with rotation vector and matrix conversions. - Developed RigidBodies class to manage multiple rigid bodies from mesh data. - Implemented ImplicitEuler time integrator for updating positions and velocities. - Added unit tests for mass properties and pose transformations using Catch2 framework.
- Updated finite-diff package from version 1.0.1 to 1.0.3 in CMake configuration. - Remove template from Pose. - Modified RigidBody and related classes to use the updated Pose structure. - Added unit tests for RigidBody construction and transformations.
- Update CPM to 0.42.0 - Add TinyGLTF dependency - Rename save_obj to write_candidates_obj - Add write_gltf
…_throw_error for consistency
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #184 +/- ##
==========================================
- Coverage 96.97% 96.24% -0.73%
==========================================
Files 221 264 +43
Lines 22426 25787 +3361
Branches 1540 1965 +425
==========================================
+ Hits 21747 24818 +3071
- Misses 679 969 +290
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:
|
…in normal collisions test (#185)
- Introduce new overloads for build methods across various classes, allowing for the use of precomputed AABBs. - Update related documentation and ensure consistent handling of vertex boxes and edges. - Improve voxel size suggestion logic based on bounding box dimensions.
… function signatures
…ment collision detection methods
# Conflicts: # python/example.ipynb
Finish the Rigid IPC / ABD port on the `dynamics` branch with the remaining algorithmic features, modernize the incremental potential, and extract the kinematic driver into its own class. Objective units: - Keep the objective uniformly in kg·m²: inertia is unscaled and every other term (body forces, orthogonality, barrier, friction, augmented Lagrangian) is multiplied by the integrator's (βΔt)² acceleration scaling at summation. The library energy terms are now pure, unscaled potentials; the demo Simulator applies the scaling. Barrier stiffness κ lives only in the BarrierPotential. Friction (velocity-based, lagged) [Ferguson et al. 2021]: - Wire TangentialCollisions + FrictionPotential into the demo Simulator. Friction is evaluated on velocities recovered from the BDF history (correct at order ≥ 2), lagged per solve, with a single outer loop and momentum-balance convergence. New settings: friction_coefficient, static_friction_speed_bound, friction_iterations. Static / kinematic bodies (augmented Lagrangian + DOF masking): - RigidBody gains Type (STATIC/KINEMATIC/DYNAMIC) and per-DOF is_dof_fixed. - New rigid/affine AugmentedLagrangian energy terms (manual derivatives) drive kinematic bodies to per-step targets; DOF masking pins static/fixed/AL- satisfied DOFs, composed with the joint reduction. - Kinematic driving is encapsulated in a demo KinematicDriver owned by the Simulator (set_kinematic_driver); RigidBody keeps only its classification. 2D simulation (rigid + affine): - Dimension-generic mass properties (2×2 second moment; edge/point-cloud lumped masses), explicit time-integrator layout, dim-aware kinematics and simulator, 2D affine terms and joints (add_hinge throws in 2D). Codimensional contact: - Rigid body-pair broad phase emits vertex-vertex (and 3D codim edge-vertex) candidates via a new two-tree LBVH query; point clouds and segment nets get valid lumped mass and BVHs. Tests: friction (FD + stick/slip + deceleration), static/kinematic (both modes), 2D, codim, and augmented-Lagrangian finite-difference checks. Full suite green (358 cases) + hidden bunny (115 assertions). Python bindings and demo/README.md updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prep for merging the RBD/ABD inertial and body-force terms behind a single
change of variables.
Stage 0: extract rotation_to_matrix_jacobian/_hessian into pose.{hpp,cpp},
dispatching 2D (so(2) exp map, 4x1) vs 3D (Rodrigues, 9x3 / 9x9), and refactor
Pose::transform_vertices_jacobian/_hessian to use them (behavior-preserving).
Stage 1: move rigid::AffinePose into a new ipc::affine::Pose (affine/pose.{hpp,cpp})
as the shared affine-coordinate representation. Update all references across the
integrator, affine_dof, body_forces, inertial_term, demo kinematics/simulator,
Python bindings (Python-facing name kept as "AffinePose"), and tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stage 2 of unifying the RBD/ABD body terms behind one to-affine map. Nothing is wired into the simulator yet; the existing terms remain in use. - ipc::dynamics::ToAffine (RigidToAffine / AffineToAffine): maps the optimization DOFs to affine pose coordinates y=[p; vec(A)] and pulls affine-space gradients/Hessians back to the DOFs (block-diagonal per body; rotation block PSD-projected once). RigidToAffine uses A=Q(θ); AffineToAffine is the identity. - ipc::dynamics::BodyPotentials: owns the affine-coordinate terms (inertia, body forces, SO(dim) orthogonality penalty for ABD, augmented Lagrangian), evaluates each at y=to_affine(x), sums with the incremental-potential scaling, and applies the to-affine chain rule once. AL is separable via include_al for barrier- stiffness seeding. - Fix a latent 2D bug in affine::BodyForces::update (gravity().head(dim)). - Tests: test_to_affine (FD chain rule vs a synthetic affine quadratic, 2D+3D, rigid+affine) and test_body_potentials (aggregator FD gradient/Hessian). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…old terms
Stage 3 (final) of the merge. The demo simulator now drives the body (non-
contact) physics through a single dynamics::BodyPotentials behind the
parameterization's dynamics::ToAffine map, replacing the parallel rigid/affine
term set.
- Integrator state is affine-shaped [p; vec(Q)] per body in every dimension
(rot_ndof = dim*dim); the 2D rigid scalar-angle special cases are removed
(integrator decode, state init, ω extraction). The rigid optimization DOF
stays reduced [p; θ]; only the state/affine coords/AL use the matrix form.
- Kinematics delegates poses()/to_integrator_state()/from_integrator_state() to
a held ToAffine (single source of the θ→A map); candidates/CCD and the
vertex-space maps stay in Kinematics.
- Simulator: the five term optionals + two AL optionals collapse to one
BodyPotentials. energy/gradient/full_hessian drop their is_affine branches
(inertia + body forces + orthogonality + AL, chain rule applied once); barrier
and friction keep the vertex-space chain rule. AL is separable via include_al
for barrier-stiffness seeding; DOF pinning reads the merged AL's satisfied
flags. (βΔt)² scaling is unchanged. BodyPotentials gains AL params + satisfied
accessors.
- Delete rigid::{InertialTerm, BodyForces, AugmentedLagrangian} and
RigidPotential; their behavior is now the affine term + ToAffine pullback. The
affine terms are the merged implementations and are retained.
- Tests: drop the rigid-term unit tests (covered by [to_affine]/[body_potentials]);
retarget the AL test to the affine AL; drop the tautological rigid-vs-affine
inertial cross-check. Re-baseline the 2D ballistic-rotation angle tolerance:
2D rotation now integrates in matrix space (as in 3D), differing from the
linear-in-angle value by O((Δtω)³).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document the merged formulation (body terms in affine coordinates aggregated by dynamics::BodyPotentials behind the dynamics::ToAffine map) and refresh the architecture, augmented-Lagrangian, and tests sections. Add a "Rigid and affine dynamics" section spelling out the main difference from Rigid IPC: rigid bodies optimize [p; θ] but integrate the rotation in matrix space (as in ABD), versus Rigid IPC's rotation-vector dynamics throughout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/Wall enables off-by-default warnings (C4711/C4710/C4514/C4820/C5045) that flooded CI with ~80k messages. /W4 is the MSVC equivalent of -Wall -Wextra. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
M_PI is not defined by <cmath> on MSVC unless _USE_MATH_DEFINES is set before the include, which broke the CI build. Switch to igl::PI (the convention already used throughout the test suite) and include <igl/PI.h>. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pose::rotation_vector() asserted A.isUnitary(1e-9), a tolerance carried over from the rigid case where A = exp(θ) is orthogonal to machine precision. For affine bodies A is only softly held in SO(dim) by the orthogonality potential and drifts ~1e-5 from unitary, so the assert fired in debug builds (release compiles it out), aborting 8 simulator tests. Relax to isUnitary(1e-3): comfortably above the observed ~2.8e-5 worst-case drift while still catching genuine non-rotations. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The hook overrode the mirror's default entry (clang-format -i) with a bare `clang-format` and no -i flag, so clang-format printed to stdout, left files unchanged, and always exited 0 -- the hook silently passed on everything and never reformatted or blocked a commit. Drop the redundant name/description/entry overrides and add -i to args so the hook reformats staged files in place.
Add MSVC /wd flags to cmake/ipc_toolkit/ipc_toolkit_warnings.cmake to silence warnings that don't have GCC/Clang equivalents (e.g. /wd4244, /wd4267, /wd4250, /wd4201, /wd4068, /wd4100), keeping MSVC output consistent with other compilers. In src/ipc/math/math.cpp switch Eigen::JacobiSVD to take compute flags as a template parameter and add a clarifying comment (ComputeFullUV). This matches Eigen's preferred compile-time option and avoids runtime-deprecation/behavior issues.
Remove experimental /experimental:external and /external:W1 compiler flags for MSVC. These flags were used to suppress warnings from external files but are no longer needed.
- rigid.py: apply the original Rigid IPC 04-bolt.json fixture settings (dt=0.01, dhat=1e-4 < thread clearance of 1.69e-4, static nut, solver iteration budget, continue on non-convergence) - rigid.py: disable contact area weighting (Rigid IPC parity). The barrier stiffness initialization's minimum-stiffness fallback (used whenever a step starts with no active contacts) does not account for the Voronoi area weights (~1e-6 m^2 on the fine thread mesh), leaving the barrier orders of magnitude too soft: the screw wedged nanometers into the threads and Newton ground to the iteration limit - demo: use the IPC collision set type when area weighting is off (the improved max approximator assumes area weights) - demo: log the actual (non-squared) minimum distance at stiffness init - python: RigidBodies.__getitem__ returns a live reference; add vertex_to_body Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eCCD Replace the direct Tight-Inclusion calls in the piecewise-linear driver with calls through a stored NarrowPhaseCCD (set_narrow_phase_ccd; default TightInclusionCCD). A new NarrowPhaseCCD::component_mode flag, set by NonlinearCCD, tells the narrow phase it is a component of the piecewise driver, which supplies its own conservative margins: - TightInclusionCCD bypasses its standalone ccd_strategy (no minimum- separation inflation, small-TOI rerun, or rescaling) and forwards the driver's per-piece cm_no_zero_toi hint (true on the first piece), a new trailing parameter on the NarrowPhaseCCD interface - AdditiveCCD uses COMPONENT_CONSERVATIVE_RESCALING = 0.99 (a raw TOI never terminates since its advancement gap is (1-cr) x initial gap), honors cm_no_zero_toi by halving the minimum separation until the initial distance clears it, and logs expected within-separation returns at trace level - NonlinearCCD's tolerance/max_iterations move to the TightInclusionCCD component; conservative_rescaling stays (used by the driver and the plane-vertex CCD) - demo: rigid mode uses AdditiveCCD per piece (bolt scene: 0.50 s vs 0.65 s with Tight Inclusion vs 8.45 s original Rigid IPC, 20 steps; identical trajectories, intersection-free) - python: NarrowPhaseCCD hierarchy switches to shared_ptr holders; NonlinearCCD.narrow_phase_ccd is a property whose setter enforces component_mode Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add IPC_TOOLKIT_BUILD_DEMO using main's unforced option pattern - Keep the dynamics/demo Python bindings alongside the deprecated GCP-name __getattr__ - Keep AdditiveCCD::handle_initial_proximity with the templated additive_ccd - Take main's MSVC warning variable and newer tests-data commit - Drop unordered_map_and_set.cpp (header-only on main) and save_obj (replaced by write_candidates_obj on dynamics) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Project each contact's rigid-DOF Hessian block to PSD, as in Rigid IPC - Add fixed-size single-vertex Pose Jacobian and Hessian - Restore position-rotation coupling in RigidToAffine::apply_hessian - Add Simulator minimum_separation_distance; default use_area_weighting to false - Skip candidates between bodies that are both non-dynamic - Add unique rigid body names with name lookup - Expose num_contacts, last_momentum_balance, and a writable external_force in Python Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Trim read_gltf/write_gltf to geometry and node names - Take per-body DOF masks in read_gltf, keyed by unique body name - Add RigidBody::set_density and RigidBodies::make_unique_names - Load a settings .py with configure(scene) in rigid.py - Enable glTF and CUDA in the python preset and find its module Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Use a double pi in Pose::clamp_rotation_to_pi (-Werror=float-conversion) - Cast glTF buffer values to float explicitly - Keep polysolve's unused CuSolverDN.cu and bare /utf-8 away from nvcc - Raise the CTest per-test timeout for the slow ESP test on Windows Debug Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Set the second moment of bodies with two rotation DOFs fixed - Apply external torques as the world torque at any orientation - Pass the CCD component mode per call instead of mutating the shared narrow phase - Keep the old NonlinearCCD constructor and save_obj as deprecated overloads, and old Python CCD overrides working - Read glTF node hierarchies, matrix transforms, and multiple or unindexed primitives; write faceless bodies as lines or points - Make the joint reduction sparse and reject invalid or late joints - Fix the BDF::dvdx history index and the FILIB-off sinc build - Add a shared ipc::PI and rename getters to lower_case - Fix the CPM OPTIONS syntax in the polysolve recipe Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zfergus
marked this pull request as ready for review
October 25, 2026 16:18
This branch has not been deployed
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 rigid and affine body dynamics as reusable library components (
src/ipc/dynamics/), glTF scene I/O (src/ipc/io/), and a demo simulator (demo/) that assembles them, touching CCD, the LBVH broad phase, and the Python bindings along the way.Mechanism
ipc::rigid—Pose,RigidBody, andRigidBodies(aCollisionMeshof all bodies, built from per-body meshes). Each body is re-centered on its center of mass and expressed in its principal frame. This follows Rigid IPC (Ferguson et al. 2021).ipc::rigid::RigidCandidates— finds body pairs with a bounding-sphere broad phase, then queries each pair with two-tree LBVH queries in one body's rest frame. Narrow-phase CCD follows the curved rigid trajectories throughNonlinearCCD, with a conservative bound on how far each piece strays from its chord.ipc::affine— the body terms are written once in affine coordinates y = [p; vec(A)]:InertialTerm,BodyForces,OrthogonalityPotential, anAugmentedLagrangianthat drives kinematic bodies, and linearJointConstraints. This follows ABD (Lan et al. 2022), with joints as in Chen et al. 2022, §4.1.ipc::dynamics— aToAffinechange of variables lets the same terms serve both models.RigidToAffinemaps [p; θ] to A = R(θ);AffineToAffineis the identity.BodyPotentialssums the terms and applies the chain rule once, andBDFprovides orders 1–6.demo/(ipc_toolkit_demo, not part of the library) — a polysolve-basedSimulatorwith adaptive barrier stiffness, lagged friction, and static/kinematic bodies. It is exposed in Python asipctk.demo.Impact
NonlinearCCDcan now use anyNarrowPhaseCCDfor its per-piece linear CCD; the demo usesAdditiveCCD.IPC_TOOLKIT_WITH_GLTF=ON.API changes
IPC_TOOLKIT_BUILD_DEMO(ON at top level, OFF as a subproject) andIPC_TOOLKIT_WITH_GLTF(OFF).NarrowPhaseCCD: every query takes a trailingNarrowPhaseCCDMode mode = STANDALONE.COMPONENTandCOMPONENT_NO_ZERO_TOItell a method it is the per-piece CCD inside a piecewise driver, so it returns the raw time of impact. The mode is passed per call, so a shared narrow-phase object is never modified.NonlinearCCD: the constructor is nowNonlinearCCD(narrow_phase_ccd = nullptr, conservative_rescaling); null selects aTightInclusionCCD. Also new:set_narrow_phase_ccd()andnarrow_phase_ccd().LBVH: new two-tree queriesdetect_*_candidates(const LBVH& other, can_collide, candidates)between two separately built trees.CollisionMesh: the destructor andvertices(full_positions)are now virtual, soRigidBodiescan map poses to vertices.nearest_rotation,sinc(scalar and interval),ipc::PI(<ipc/math/constants.hpp>), and a 2Dcross_product_matrix(double).ipctklogger is no longer registered in spdlog's global registry. Previously, two Python modules that each embed the toolkit failed on the duplicate name.RigidBodies,RigidBody,Pose/Poses,AffinePose,JointConstraints,read_gltf/write_gltf, andNarrowPhaseCCDMode.std::shared_ptr.NarrowPhaseCCDsubclasses written without amodeparameter keep working.💥 Breaking changes
NarrowPhaseCCDmust add themodeparameter to their four overrides.NonlinearCCD::toleranceandNonlinearCCD::max_iterationsare removed. Configure theTightInclusionCCDyou pass in instead.NonlinearCCD(tolerance, max_iterations, conservative_rescaling)in C++ and Python. It builds the equivalentTightInclusionCCD.Candidates::save_obj, renamed towrite_obj, in C++ and Python.<ipc/utils/save_obj.hpp>, which forwards to<ipc/io/write_candidates_obj.hpp>.New dependencies
ipc_toolkit_demo, never byipc_toolkit.PRIVATE.IPC_TOOLKIT_WITH_GLTF=ON.Type of change
How Has This Been Tested?
ipc_toolkit_tests "~[!benchmark]": all 518 test cases pass. This includes 86 new cases intests/src/tests/{dynamics,demo,io}:ToAffinechain rule, and rigid pose derivatives.python -m nose2inpython/tests: all 106 tests pass. These include the newNarrowPhaseCCDModeand backward-compatibility tests.src/ipc/math/sinc.cppwithIPC_TOOLKIT_WITH_FILIBoff.Test Configuration:
Checklist