Giter Club home page Giter Club logo

tight-inclusion's Introduction

Tight-Inclusion Continuous Collision Detection

Build

A conservative continuous collision detection (CCD) method with support for minimum separation.

To know more about this work, please read our ACM Transactions on Graphics paper:
"A Large Scale Benchmark and an Inclusion-Based Algorithm for Continuous Collision Detection" and watch our SIGGRAPH 2022 presentation.

Build

To compile the code, first, make sure CMake is installed.

To build the library on Linux or macOS:

mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j4

Then you can run a CCD example:

./app/Tight_Inclusion_bin

Optional

We also provide an example that tests sample queries using our CCD method. This requires installing gmp on your system before compiling the code. Set the CMake option TIGHT_INCLUSION_WITH_SAMPLE_QUERIES to ON when compiling:

cmake .. -DCMAKE_BUILD_TYPE=Release -DTIGHT_INCLUSION_WITH_SAMPLE_QUERIES=ON
make -j4

Then you can run ./app/Tight_Inclusion_bin to test the handcrafted and simulation queries in the Sample Queries.

Usage

Overview

  • Include: #include <tight_inclusion/ccd.hpp>
  • Check vertex-face CCD: bool ticcd::vertexFaceCCD(...)
  • Check edge-edge CCD: bool ticcd::edgeEdgeCCD(...)

Details

๐Ÿ’ก Each CCD function returns a boolean result corresponding to if a collision is detected. Because our method is conservative, we guarantee a result of false implies no collision occurs. If the result is true, there may not be a collision but we falsely report a collision. However, we can guarantee that this happens only if the minimal distance between the two primitives in this time step is no larger than tolerance + ms + err (see below for a description of these parameters).

Parameters

For both vertex-face and edge-edge CCD, the input query is given by eight vertices which are in the format of Eigen::Vector3d. Please read our code in tight_inclusion/ccd.hpp for the correct input order of the vertices.

Besides the input vertices, there are some input and output parameters for users to tune the performance or to get more information from the CCD.

Here is a list of the explanations of the parameters:

Input
  • err: The numerical filters of the $x$, $y$ and $z$ coordinates. It measures the errors introduced by floating-point calculation when solving inclusion functions.
  • ms: A minimum separation distance (no less than 0). We guarantee a collision will be reported if the distance between the two primitives is less than ms.
  • tolerance: User-specific solving precision. It is the target maximal $x$, $y$, and $z$ length of the inclusion function. We suggest the to use 1e-6.
  • t_max: The time range $[0, t_{\max}]$ where we detect collisions. Since the input query implies the motion is in time interval $[0, 1]$, t_max should not be larger than 1.
  • max_itr: The maximum number of iterations our inclusion-based root-finding algorithm can take. This enables early termination of the algorithm. If you set max_itr < 0, early termination will be disabled, but this may cause longer running times. We suggest setting max_itr = 1e6.
  • no_zero_toi: For simulators which use non-zero minimum separation distance (ms > 0) to make sure intersection-free for each time-step, we have the option no_zero_toi to avoid returning a collision time toi of 0. The code will continue the refinement in higher precision if the output toi is 0 under the given tolerance, so the eventual toi will not be 0.
  • CCD_TYPE: Enumeration of possible CCD schemes. The default and recommended type is BREADTH_FIRST_SEARCH. If set DEPTH_FIRST_SEARCH, the code will switch to a naive conservative CCD algorithm but lacks our advanced features.
Output
  • toi: The time of impact. If multiple collisions happen in this time step, it will return the earliest collision time. If there is no collision, the returned toi value will be std::numeric_limits<double>::infinity().
  • output_tolerance: The resulting solve's precision. If early termination is enabled, the solving precision may not reach the target precision. This parameter will return the resulting solving precision when the code is terminated.

Tips

๐Ÿ’ก The input parameter err is crucial to guarantee our algorithm is a conservative method not affected by floating-point rounding errors. To run a single query, you can set err = Eigen::Array3d(-1, -1, -1) to enable a sub-function to calculate the real numerical filters when solving CCD. If you are integrating our CCD in simulators, you need to:

  • Include the headler: #include <tight_inclusion/interval_root_finder.hpp>.
  • Call
    std::array<double, 3> err_vf = ticcd::get_numerical_error()
    
    and
    std::array<double, 3> err_ee = ticcd::get_numerical_error()
    
  • Use the parameter err_ee each time you call bool ticcd::edgeEdgeCCD() and err_vf when you call bool ticcd::vertexFaceCCD().

The parameters for function ticcd::get_numerical_error() are:

  • vertices: Vertices of the axis-aligned bounding box of the simulation scene. Before you run the simulation, you need to conservatively estimate the axis-aligned bounding box in which the meshes will be located during the whole simulation process, and the vertices should be the corners of the AABB.
  • is_vertex_face: A boolean flag corresponding to if you are checking vertex-face or edge-edge CCD.
  • using_minimum_separation: A boolean flag corresponding to if you are using minimum-separation CCD (the input parameter ms > 0).

To better understand or to get more details of our Tight-Inclusion CCD algorithm, please refer to our paper.

Citation

If you use this work in your project, please consider citing the original paper:

@article{Wang:2021:Benchmark,
    title        = {A Large Scale Benchmark and an Inclusion-Based Algorithm for Continuous Collision Detection},
    author       = {Bolun Wang and Zachary Ferguson and Teseo Schneider and Xin Jiang and Marco Attene and Daniele Panozzo},
    year         = 2021,
    month        = oct,
    journal      = {ACM Transactions on Graphics},
    volume       = 40,
    number       = 5,
    articleno    = 188,
    numpages     = 16
}

tight-inclusion's People

Contributors

teseoch avatar wangbolun300 avatar zfergus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tight-inclusion's Issues

a case returned 0 TOI

For the PT CCD case below, TICCD returned 0 TOI.

3.5000000000e-01 0.0000000000e+00 3.5000000000e-01
3.4604643638e-01 2.7847887896e-03 3.4658121160e-01
3.5000819263e-01 -1.5763377304e-06 3.5001214242e-01
3.4903882032e-01 -1.5866575093e-03 3.4560164356e-01
0.0000000000e+00 0.0000000000e+00 0.0000000000e+00
1.0120300789e-07 -1.4009994248e-04 -8.5653091896e-05
-9.2976239634e-07 1.4361165221e-06 5.5168830145e-07
1.1649271683e-04 -3.6172565398e-05 -4.7128237917e-05

The format is

node_x node_y node_z
traingle_node0_x traingle_node0_y traingle_node0_z
traingle_node1_x traingle_node1_y traingle_node1_z
traingle_node2_x traingle_node2_y traingle_node2_z
node_displacement_x node_displacement_y node_displacement_z
traingle_node0_displacement_x traingle_node0_displacement_y traingle_node0_displacement_z
traingle_node1_displacement_x traingle_node1_displacement_y traingle_node1_displacement_z
traingle_node2_displacement_x traingle_node2_displacement_y traingle_node2_displacement_z

I used the TICCD from the CCDWrapper repo (latest commit), and I called it like

template <class T>
bool Point_Triangle_CCD_TI(
    const Eigen::Matrix<T, 3, 1>& p, 
    const Eigen::Matrix<T, 3, 1>& t0, 
    const Eigen::Matrix<T, 3, 1>& t1,
    const Eigen::Matrix<T, 3, 1>& t2,
    const Eigen::Matrix<T, 3, 1>& dp, 
    const Eigen::Matrix<T, 3, 1>& dt0, 
    const Eigen::Matrix<T, 3, 1>& dt1,
    const Eigen::Matrix<T, 3, 1>& dt2,
    T eta, T thickness, T& toc)
{
    T toc_prev = toc;
    T output_tolerance;

    T dist2_cur;
    Point_Triangle_Distance_Unclassified(p, t0, t1, t2, dist2_cur);
    T dist_cur = std::sqrt(dist2_cur);
    if (inclusion_ccd::vertexFaceCCD_double(p, t0, t1, t2,
        p + dp, t0 + dt0, t1 + dt1, t2 + dt2, { { -1, 0, 0 } },
        eta * (dist2_cur - thickness * thickness) / (dist_cur + thickness) + thickness,
        toc, 1e-6, toc_prev, 1e6, output_tolerance, 1))
    {
        if (toc < 1.0e-6) {
            std::cout << "PT CCD tiny!" << std::endl;
            if (inclusion_ccd::vertexFaceCCD_double(p, t0, t1, t2,
                p + dp, t0 + dt0, t1 + dt1, t2 + dt2, { { -1, 0, 0 } },
                thickness, toc, 1e-6, toc_prev, 1e6, output_tolerance, 1))
            {
                toc *= (1.0 - eta);
                return true;
            }
            else {
                return false;
            }
        }
        return true;
    }
    return false;
}

printf("TICCD:\n");
largestAlpha = 1;
tstart = clock();
if (Point_Triangle_CCD_TI(x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], 0.1, 0.0, largestAlpha)) {
    printf("%le\n", largestAlpha);
}
else {
    printf("no collision\n");
}
printf("%.1lems\n", double(clock() - tstart) / CLOCKS_PER_SEC * 1000);

The output is

TICCD:
PT CCD tiny!
0.000000e+00
1.9e+00ms

However I implemented a simpler version of TICCD according to the paper as

template <class T>
void Point_Triangle_Distance_Vector_Unclassified(
    const Eigen::Matrix<T, 3, 1>& p, 
    const Eigen::Matrix<T, 3, 1>& t0, 
    const Eigen::Matrix<T, 3, 1>& t1,
    const Eigen::Matrix<T, 3, 1>& t2,
    const Eigen::Matrix<T, 3, 1>& dp, 
    const Eigen::Matrix<T, 3, 1>& dt0, 
    const Eigen::Matrix<T, 3, 1>& dt1,
    const Eigen::Matrix<T, 3, 1>& dt2,
    T t, T lambda, T beta,
    Eigen::Matrix<T, 3, 1>& distVec)
{
    const Eigen::Matrix<T, 3, 1> tp = (1 - lambda - beta) * t0 + lambda * t1 + beta * t2;
    const Eigen::Matrix<T, 3, 1> dtp = (1 - lambda - beta) * dt0 + lambda * dt1 + beta * dt2;
    distVec = p + t * dp - (tp + t * dtp);
}

template <class T>
bool Point_Triangle_CheckInterval_Unclassified(
    const Eigen::Matrix<T, 3, 1>& p, 
    const Eigen::Matrix<T, 3, 1>& t0, 
    const Eigen::Matrix<T, 3, 1>& t1,
    const Eigen::Matrix<T, 3, 1>& t2,
    const Eigen::Matrix<T, 3, 1>& dp, 
    const Eigen::Matrix<T, 3, 1>& dt0, 
    const Eigen::Matrix<T, 3, 1>& dt1,
    const Eigen::Matrix<T, 3, 1>& dt2,
    const std::array<T, 6>& interval,
    T gap)
{
    Eigen::Matrix<T, 3, 1> distVecMax, distVecMin;
    distVecMax.setConstant(-2 * gap - 1);
    distVecMin.setConstant(2 * gap + 1);
    for (int t = 0; t < 2; ++t) {
        for (int lambda = 0; lambda < 2; ++lambda) {
            for (int beta = 0; beta < 2; ++beta) {
                if (lambda == 1 && beta == 1) {
                    continue;
                }
                Eigen::Matrix<T, 3, 1> distVec;
                Point_Triangle_Distance_Vector_Unclassified(p, t0, t1, t2, dp, dt0, dt1, dt2, 
                    interval[t], interval[2 + lambda], interval[4 + beta], distVec);
                distVecMax = distVecMax.array().max(distVec.array());
                distVecMin = distVecMin.array().min(distVec.array());
            }
        }
    }
    return (distVecMax.array() >= -gap).all() && (distVecMin.array() <= gap).all();
}
template <class T>
bool Point_Triangle_TICCD(
    const Eigen::Matrix<T, 3, 1>& p, 
    const Eigen::Matrix<T, 3, 1>& t0, 
    const Eigen::Matrix<T, 3, 1>& t1,
    const Eigen::Matrix<T, 3, 1>& t2,
    Eigen::Matrix<T, 3, 1> dp, 
    Eigen::Matrix<T, 3, 1> dt0, 
    Eigen::Matrix<T, 3, 1> dt1,
    Eigen::Matrix<T, 3, 1> dt2,
    T eta, T thickness, T& toc)
{
    T dist2_cur;
    Point_Triangle_Distance_Unclassified(p, t0, t1, t2, dist2_cur);
    T dist_cur = std::sqrt(dist2_cur);
    T gap = eta * (dist2_cur - thickness * thickness) / (dist_cur + thickness);

    T tTol = 1e-3;

    std::vector<std::array<T, 6>> roots;
    std::deque<std::array<T, 6>> intervals;
    intervals.push_back({0, toc, 0, 1, 0, 1});
    int iterAmt = 0;
    while (!intervals.empty()) {
        ++iterAmt;

        std::array<T, 6> curIV = intervals.front();
        intervals.pop_front();

        // check
        if (Point_Triangle_CheckInterval_Unclassified(p, t0, t1, t2, dp, dt0, dt1, dt2, curIV, gap)) {
            if (curIV[0] && curIV[1] - curIV[0] < tTol) {
                // root found within tTol
                roots.emplace_back(curIV);
            }
            else {
                // split interval and push back
                std::vector<T> intervalLen({curIV[1] - curIV[0], curIV[3] - curIV[2], curIV[5] - curIV[4]});
                switch (std::max_element(intervalLen.begin(), intervalLen.end()) - intervalLen.begin()) {
                case 0:
                    intervals.push_back({curIV[0], (curIV[1] + curIV[0]) / 2, curIV[2], curIV[3], curIV[4], curIV[5]});
                    intervals.push_back({(curIV[1] + curIV[0]) / 2, curIV[1], curIV[2], curIV[3], curIV[4], curIV[5]});
                    break;

                case 1:
                    intervals.push_back({curIV[0], curIV[1], curIV[2], (curIV[2] + curIV[3]) / 2, curIV[4], curIV[5]});
                    intervals.push_back({curIV[0], curIV[1], (curIV[2] + curIV[3]) / 2, curIV[3], curIV[4], curIV[5]});
                    break;

                case 2:
                    intervals.push_back({curIV[0], curIV[1], curIV[2], curIV[3], curIV[4], (curIV[4] + curIV[5]) / 2});
                    intervals.push_back({curIV[0], curIV[1], curIV[2], curIV[3], (curIV[4] + curIV[5]) / 2, curIV[5]});
                    break;
                }
            }
        }
    }
    
    if (roots.empty()) {
        printf("TICCD PT converged with %d iters\n", iterAmt);
        return false;
    }
    else {
        for (const auto& rI : roots) {
            if (toc > rI[0]) {
                toc = rI[0];
            }
        }
        printf("TICCD PT converged with %d iters\n", iterAmt);
        return true;
    }
}

and it kinds of worked on this case and output

TICCD PT converged with 6143 iters
4.882812e-04
5.3e-01ms

which with tighter convergence tolerance might be able to reach the ground truth solution -- no collision.

In my implementation, I didn't include the epsilons in formula (5) in the paper. Is it the main cause here?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.