Source code for diffsim.collision

"""Approximate collision penalties for the explicit solver.

This inexpensive randomized vertex-pair model is useful for previews only. It has
no continuous collision detection or non-penetration guarantee. Frictionless IPC
lives in :mod:`diffsim.ipc` because it must be coupled to an implicit nonlinear solve.
"""

import torch


[docs] class SimplifiedCollisionHandler: """ Fast simplified collision detection for real-time simulation Uses distance-based repulsion without full CCD """
[docs] def __init__( self, collision_distance=0.02, repulsion_stiffness=1e4, max_checks_per_frame=1000, ): """ Args: collision_distance: minimum distance threshold repulsion_stiffness: strength of repulsion forces max_checks_per_frame: maximum collision checks per frame (for performance) """ self.d_min = collision_distance self.k_repulsion = repulsion_stiffness self.max_checks = max_checks_per_frame
[docs] def compute_self_collision_forces(self, mesh, positions): """ Fast self-collision detection using simple distance checks Args: mesh: TetrahedralMesh positions: :math:`(N, 3)` current positions Returns: forces: :math:`(N, 3)` repulsion forces """ forces = torch.zeros_like(positions) # Vectorized collision detection for speed N = positions.shape[0] # Limit checks for performance - sample vertex pairs num_samples = min(self.max_checks, N * 5) if num_samples == 0: return forces # Generate random pairs in batch indices_i = torch.randint(0, N, (num_samples,), device=positions.device) indices_j = torch.randint(0, N, (num_samples,), device=positions.device) # Filter out self-pairs valid = indices_i != indices_j indices_i = indices_i[valid] indices_j = indices_j[valid] if len(indices_i) == 0: return forces # Compute distances in batch pos_i = positions[indices_i] pos_j = positions[indices_j] diff = pos_i - pos_j dist = torch.norm(diff, dim=1, keepdim=True) + 1e-12 # Find colliding pairs colliding = dist.squeeze(-1) < self.d_min if not colliding.any(): return forces # Compute repulsion forces for colliding pairs dist_colliding = dist[colliding] diff_colliding = diff[colliding] penetration = self.d_min - dist_colliding force_mag = self.k_repulsion * penetration / dist_colliding force = force_mag * diff_colliding # Accumulate forces (using scatter_add for efficiency) indices_i_colliding = indices_i[colliding] indices_j_colliding = indices_j[colliding] forces.index_add_(0, indices_i_colliding, force) forces.index_add_(0, indices_j_colliding, -force) return forces