Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/source/tutorials/nonlinear_ccd.rst
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,13 @@ The following code snippet shows an example of how to use interval arithmetic to

.. code-block:: cpp

#include <ipc/utils/interval.hpp>
#include <ipc/math/interval.hpp>

using namespace ipc;

Vector2I position(
const VectorMax3d& center,
const VectorMax3d& point,
Eigen::ConstRef<VectorMax3d> center,
Eigen::ConstRef<VectorMax3d> point,
const double omega,
const Interval& t)
{
Expand Down
325 changes: 151 additions & 174 deletions python/src/ccd/nonlinear_ccd.cpp

Large diffs are not rendered by default.

78 changes: 55 additions & 23 deletions python/tests/test_ccd.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import numpy as np

from find_ipctk import ipctk
from ipctk.filib import Interval

from utils import load_mesh


Expand All @@ -28,16 +26,44 @@ class DumbNarrowPhaseCCD(ipctk.NarrowPhaseCCD):
def __init__(self):
ipctk.NarrowPhaseCCD.__init__(self)

def point_point_ccd(self, p0_t0, p1_t0, p0_t1, p1_t1, min_distance=0.0, tmax=1.0):
def point_point_ccd(
self, p0_t0, p1_t0, p0_t1, p1_t1, min_distance=0.0, tmax=1.0
):
return True, 0.0

def point_edge_ccd(self, p_t0, e0_t0, e1_t0, p_t1, e0_t1, e1_t1, min_distance=0.0, tmax=1.0):
def point_edge_ccd(
self, p_t0, e0_t0, e1_t0, p_t1, e0_t1, e1_t1, min_distance=0.0, tmax=1.0
):
return True, 0.0

def point_triangle_ccd(self, p_t0, t0_t0, t1_t0, t2_t0, p_t1, t0_t1, t1_t1, t2_t1, min_distance=0.0, tmax=1.0):
def point_triangle_ccd(
self,
p_t0,
t0_t0,
t1_t0,
t2_t0,
p_t1,
t0_t1,
t1_t1,
t2_t1,
min_distance=0.0,
tmax=1.0,
):
return True, 0.0

def edge_edge_ccd(self, ea0_t0, ea1_t0, eb0_t0, eb1_t0, ea0_t1, ea1_t1, eb0_t1, eb1_t1, min_distance=0.0, tmax=1.0):
def edge_edge_ccd(
self,
ea0_t0,
ea1_t0,
eb0_t0,
eb1_t0,
ea0_t1,
ea1_t1,
eb0_t1,
eb1_t1,
min_distance=0.0,
tmax=1.0,
):
return True, 0.0

V0, E, F = load_mesh("two-cubes-close.ply")
Expand All @@ -48,10 +74,12 @@ def edge_edge_ccd(self, ea0_t0, ea1_t0, eb0_t0, eb1_t0, ea0_t1, ea1_t1, eb0_t1,
ipctk.set_num_threads(1)

assert not ipctk.is_step_collision_free(
mesh, V0, V1, narrow_phase_ccd=DumbNarrowPhaseCCD())
mesh, V0, V1, narrow_phase_ccd=DumbNarrowPhaseCCD()
)

toi = ipctk.compute_collision_free_stepsize(
mesh, V0, V1, narrow_phase_ccd=DumbNarrowPhaseCCD())
mesh, V0, V1, narrow_phase_ccd=DumbNarrowPhaseCCD()
)
assert 0 <= toi <= 1


Expand Down Expand Up @@ -90,8 +118,7 @@ def detect_face_face_candidates(self):

assert ipctk.is_step_collision_free(mesh, V0, V1, broad_phase=broad_phase)

toi = ipctk.compute_collision_free_stepsize(
mesh, V0, V1, broad_phase=broad_phase)
toi = ipctk.compute_collision_free_stepsize(mesh, V0, V1, broad_phase=broad_phase)
assert toi == 1

assert not ipctk.has_intersections(mesh, V0, broad_phase=broad_phase)
Expand Down Expand Up @@ -124,7 +151,9 @@ def max_distance_from_linear(self, t0: float, t1: float):
assert p0.max_distance_from_linear(0, 1) == 0
assert p1.max_distance_from_linear(0, 1) == 0

is_colliding, toi = ipctk.point_point_nonlinear_ccd(p0, p1)
ccd = ipctk.NonlinearCCD()

is_colliding, toi = ccd.point_point_ccd(p0, p1)

assert is_colliding
assert 0 < toi < 1
Expand All @@ -145,15 +174,17 @@ def max_distance_from_linear(self, t0: float, t1: float):
# p0 = IntervalLinearTrajectory(np.array([-1, 0, 0]), np.array([1, 0, 0]))
# p1 = IntervalLinearTrajectory(np.array([0, -1, 0]), np.array([0, 1, 0]))

# is_colliding, toi = ipctk.point_point_nonlinear_ccd(p0, p1)
# is_colliding, toi = ccd.point_point_ccd(p0, p1)

# assert is_colliding
# assert 0 < toi < 1
# assert np.abs(toi - 0.5) < 1e-2

# BEGIN_RIGID_2D_TRAJECTORY
class Rigid2DTrajectory(ipctk.NonlinearTrajectory):
def __init__(self, position, translation, delta_translation, rotation, delta_rotation):
def __init__(
self, position, translation, delta_translation, rotation, delta_rotation
):
ipctk.NonlinearTrajectory.__init__(self)
self.position = position
self.translation = translation
Expand All @@ -164,9 +195,11 @@ def __init__(self, position, translation, delta_translation, rotation, delta_rot
# BEGIN_RIGID_2D_CALL
def __call__(self, t):
theta = self.rotation + t * self.delta_rotation
R = np.array([[np.cos(theta), -np.sin(theta)],
[np.sin(theta), np.cos(theta)]])
R = np.array(
[[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]
)
return R @ self.position + self.translation + t * self.delta_translation

# END_RIGID_2D_CALL

# BEGIN_RIGID_2D_MAX_DISTANCE_FROM_LINEAR
Expand All @@ -177,20 +210,19 @@ def max_distance_from_linear(self, t0, t1):
p_t0 = self(t0)
p_t1 = self(t1)
return np.linalg.norm(self((t0 + t1) / 2) - ((p_t1 - p_t0) * 0.5 + p_t0))

# END_RIGID_2D_MAX_DISTANCE_FROM_LINEAR

# END_RIGID_2D_TRAJECTORY

# BEGIN_TEST_RIGID_2D_TRAJECTORY
p = Rigid2DTrajectory(
np.array([0, 0.5]), np.zeros(2), np.zeros(2), 0, 0)
e0 = Rigid2DTrajectory(
np.array([-1, 0]), np.zeros(2), np.zeros(2), 0, np.pi)
e1 = Rigid2DTrajectory(
np.array([1, 0]), np.zeros(2), np.zeros(2), 0, np.pi)
p = Rigid2DTrajectory(np.array([0, 0.5]), np.zeros(2), np.zeros(2), 0, 0)
e0 = Rigid2DTrajectory(np.array([-1, 0]), np.zeros(2), np.zeros(2), 0, np.pi)
e1 = Rigid2DTrajectory(np.array([1, 0]), np.zeros(2), np.zeros(2), 0, np.pi)

# increase the conservative_rescaling from 0.8 to 0.9 to get a more accurate estimate
collision, toi = ipctk.point_edge_nonlinear_ccd(
p, e0, e1, conservative_rescaling=0.9)
ccd.conservative_rescaling = 0.9
collision, toi = ccd.point_edge_ccd(p, e0, e1)

assert collision
assert 0.49 <= toi <= 0.5 # conservative estimate
Expand Down
70 changes: 34 additions & 36 deletions src/ipc/ccd/nonlinear_ccd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,17 @@ double IntervalNonlinearTrajectory::max_distance_from_linear(

// ============================================================================

bool conservative_piecewise_linear_ccd(
NonlinearCCD::NonlinearCCD(
const double _tolerance,
const long _max_iterations,
const double _conservative_rescaling)
: tolerance(_tolerance)
, max_iterations(_max_iterations)
, conservative_rescaling(_conservative_rescaling)
{
}

bool NonlinearCCD::conservative_piecewise_linear_ccd(
const std::function<double(const double)>& distance,
const std::function<double(const double, const double)>&
max_distance_from_linear,
Expand All @@ -62,15 +72,15 @@ bool conservative_piecewise_linear_ccd(
const bool /*no_zero_toi*/,
double& /*toi*/)>& linear_ccd,
double& toi,
const double min_distance,
const double tmax,
const double min_sep_distance,
const double conservative_rescaling)
{
const double distance_t0 = distance(0);
if (check_initial_distance(distance_t0, min_sep_distance, toi)) {
if (check_initial_distance(distance_t0, min_distance, toi)) {
return true;
}
assert(distance_t0 > min_sep_distance);
assert(distance_t0 > min_distance);

double ti0 = 0;
std::stack<double> ts;
Expand Down Expand Up @@ -102,38 +112,38 @@ bool conservative_piecewise_linear_ccd(
return true;
}

double min_distance = max_distance_from_linear(ti0, ti1);
double min_distance_linear = max_distance_from_linear(ti0, ti1);

#ifndef USE_FIXED_PIECES
// Check if the minimum distance is too large and we need to subdivide
// (Large distances cause the slow CCD)
if ((min_distance
if ((min_distance_linear
>= std::min((1 - conservative_rescaling) * distance_ti0, 0.01))
&& (num_subdivisions < MAX_NUM_SUBDIVISIONS || ti0 == 0)) {
logger().trace(
"Subdividing at ti=[{:g}, {:g}] min_distance={:g} distance_ti0={:g}",
ti0, ti1, min_distance, distance_ti0);
ti0, ti1, min_distance_linear, distance_ti0);
ts.push((ti1 + ti0) / 2);
num_subdivisions++;
continue;
}
#endif

min_distance += min_sep_distance;
min_distance_linear += min_distance;

const bool is_impacting =
linear_ccd(ti0, ti1, min_distance, /*no_zero_toi=*/ti0 == 0, toi);
const bool is_impacting = linear_ccd(
ti0, ti1, min_distance_linear, /*no_zero_toi=*/ti0 == 0, toi);

logger().trace(
"Evaluated at ti=[{:g}, {:g}] min_distance={:g} distance_ti0={:g}; result={}{}",
ti0, ti1, min_distance, distance_ti0, is_impacting,
ti0, ti1, min_distance_linear, distance_ti0, is_impacting,
is_impacting ? fmt::format(" toi={:g}", (ti1 - ti0) * toi + ti0)
: "");

if (is_impacting) {
toi = (ti1 - ti0) * toi + ti0;
if (toi == 0) {
// This is impossible because distance_t0 > min_sep_distance
// This is impossible because distance_t0 > min_distance
ts.push((ti1 + ti0) / 2);
num_subdivisions++;
continue;
Expand All @@ -150,15 +160,12 @@ bool conservative_piecewise_linear_ccd(

// ============================================================================

bool point_point_nonlinear_ccd(
bool NonlinearCCD::point_point_ccd(
const NonlinearTrajectory& p0,
const NonlinearTrajectory& p1,
double& toi,
const double tmax,
const double min_distance,
const double tolerance,
const long max_iterations,
const double conservative_rescaling)
const double tmax) const
{
return conservative_piecewise_linear_ccd(
[&](const double t) {
Expand All @@ -184,19 +191,16 @@ bool point_point_nonlinear_ccd(
output_tolerance, // delta_actual
no_zero_toi); // no zero toi
},
toi, tmax, min_distance, conservative_rescaling);
toi, min_distance, tmax, conservative_rescaling);
}

bool point_edge_nonlinear_ccd(
bool NonlinearCCD::point_edge_ccd(
const NonlinearTrajectory& p,
const NonlinearTrajectory& e0,
const NonlinearTrajectory& e1,
double& toi,
const double tmax,
const double min_distance,
const double tolerance,
const long max_iterations,
const double conservative_rescaling)
const double tmax) const
{
return conservative_piecewise_linear_ccd(
[&](const double t) {
Expand All @@ -223,20 +227,17 @@ bool point_edge_nonlinear_ccd(
output_tolerance, // delta_actual
no_zero_toi); // no zero toi
},
toi, tmax, min_distance, conservative_rescaling);
toi, min_distance, tmax, conservative_rescaling);
}

bool edge_edge_nonlinear_ccd(
bool NonlinearCCD::edge_edge_ccd(
const NonlinearTrajectory& ea0,
const NonlinearTrajectory& ea1,
const NonlinearTrajectory& eb0,
const NonlinearTrajectory& eb1,
double& toi,
const double tmax,
const double min_distance,
const double tolerance,
const long max_iterations,
const double conservative_rescaling)
const double tmax) const
{
return conservative_piecewise_linear_ccd(
[&](const double t) {
Expand Down Expand Up @@ -265,20 +266,17 @@ bool edge_edge_nonlinear_ccd(
output_tolerance, // delta_actual
no_zero_toi); // no zero toi
},
toi, tmax, min_distance, conservative_rescaling);
toi, min_distance, tmax, conservative_rescaling);
}

bool point_triangle_nonlinear_ccd(
bool NonlinearCCD::point_triangle_ccd(
const NonlinearTrajectory& p,
const NonlinearTrajectory& t0,
const NonlinearTrajectory& t1,
const NonlinearTrajectory& t2,
double& toi,
const double tmax,
const double min_distance,
const double tolerance,
const long max_iterations,
const double conservative_rescaling)
const double tmax) const
{
return conservative_piecewise_linear_ccd(
[&](const double t) {
Expand Down Expand Up @@ -306,7 +304,7 @@ bool point_triangle_nonlinear_ccd(
output_tolerance, // delta_actual
no_zero_toi); // no zero toi
},
toi, tmax, min_distance, conservative_rescaling);
toi, min_distance, tmax, conservative_rescaling);
}

} // namespace ipc
Loading