summaryrefslogtreecommitdiff
path: root/libs/ode-0.16.1/libccd/src/ccd.c
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-10-01 20:59:36 -0500
committersanine <sanine.not@pm.me>2022-10-01 20:59:36 -0500
commitc5fc66ee58f2c60f2d226868bb1cf5b91badaf53 (patch)
tree277dd280daf10bf77013236b8edfa5f88708c7e0 /libs/ode-0.16.1/libccd/src/ccd.c
parent1cf9cc3408af7008451f9133fb95af66a9697d15 (diff)
add ode
Diffstat (limited to 'libs/ode-0.16.1/libccd/src/ccd.c')
-rw-r--r--libs/ode-0.16.1/libccd/src/ccd.c955
1 files changed, 955 insertions, 0 deletions
diff --git a/libs/ode-0.16.1/libccd/src/ccd.c b/libs/ode-0.16.1/libccd/src/ccd.c
new file mode 100644
index 0000000..5221b8f
--- /dev/null
+++ b/libs/ode-0.16.1/libccd/src/ccd.c
@@ -0,0 +1,955 @@
+/***
+ * libccd
+ * ---------------------------------
+ * Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
+ *
+ *
+ * This file is part of libccd.
+ *
+ * Distributed under the OSI-approved BSD License (the "License");
+ * see accompanying file BDS-LICENSE for details or see
+ * <http://www.opensource.org/licenses/bsd-license.php>.
+ *
+ * This software is distributed WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the License for more information.
+ */
+
+#include <stdio.h>
+#include <float.h>
+#include <ccd/ccd.h>
+#include <ccdcustom/vec3.h>
+#include <ccd/simplex.h>
+#include <ccd/polytope.h>
+#include <ccd/alloc.h>
+#include <ccd/dbg.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+/** Performs GJK algorithm. Returns 0 if intersection was found and simplex
+ * is filled with resulting polytope. */
+static int __ccdGJK(const void *obj1, const void *obj2,
+ const ccd_t *ccd, ccd_simplex_t *simplex);
+
+/** Performs GJK+EPA algorithm. Returns 0 if intersection was found and
+ * pt is filled with resulting polytope and nearest with pointer to
+ * nearest element (vertex, edge, face) of polytope to origin. */
+static int __ccdGJKEPA(const void *obj1, const void *obj2,
+ const ccd_t *ccd,
+ ccd_pt_t *pt, ccd_pt_el_t **nearest);
+
+
+/** Returns true if simplex contains origin.
+ * This function also alteres simplex and dir according to further
+ * processing of GJK algorithm. */
+static int doSimplex(ccd_simplex_t *simplex, ccd_vec3_t *dir);
+static int doSimplex2(ccd_simplex_t *simplex, ccd_vec3_t *dir);
+static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir);
+static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir);
+
+/** d = a x b x c */
+_ccd_inline void tripleCross(const ccd_vec3_t *a, const ccd_vec3_t *b,
+ const ccd_vec3_t *c, ccd_vec3_t *d);
+
+
+/** Transforms simplex to polytope. It is assumed that simplex has 4
+ * vertices. */
+static int simplexToPolytope4(const void *obj1, const void *obj2,
+ const ccd_t *ccd,
+ ccd_simplex_t *simplex,
+ ccd_pt_t *pt, ccd_pt_el_t **nearest);
+
+/** Transforms simplex to polytope, three vertices required */
+static int simplexToPolytope3(const void *obj1, const void *obj2,
+ const ccd_t *ccd,
+ const ccd_simplex_t *simplex,
+ ccd_pt_t *pt, ccd_pt_el_t **nearest);
+
+/** Transforms simplex to polytope, two vertices required */
+static int simplexToPolytope2(const void *obj1, const void *obj2,
+ const ccd_t *ccd,
+ const ccd_simplex_t *simplex,
+ ccd_pt_t *pt, ccd_pt_el_t **nearest);
+
+/** Expands polytope using new vertex v. */
+static void expandPolytope(ccd_pt_t *pt, ccd_pt_el_t *el,
+ const ccd_support_t *newv);
+
+/** Finds next support point (at stores it in out argument).
+ * Returns 0 on success, -1 otherwise */
+static int nextSupport(const void *obj1, const void *obj2, const ccd_t *ccd,
+ const ccd_pt_el_t *el,
+ ccd_support_t *out);
+
+
+
+void ccdFirstDirDefault(const void *o1, const void *o2, ccd_vec3_t *dir)
+{
+ ccdVec3Set(dir, CCD_ONE, CCD_ZERO, CCD_ZERO);
+}
+
+int ccdGJKIntersect(const void *obj1, const void *obj2, const ccd_t *ccd)
+{
+ ccd_simplex_t simplex;
+ return __ccdGJK(obj1, obj2, ccd, &simplex) == 0;
+}
+
+int ccdGJKSeparate(const void *obj1, const void *obj2, const ccd_t *ccd,
+ ccd_vec3_t *sep)
+{
+ ccd_pt_t polytope;
+ ccd_pt_el_t *nearest;
+ int ret;
+
+ ccdPtInit(&polytope);
+
+ ret = __ccdGJKEPA(obj1, obj2, ccd, &polytope, &nearest);
+
+ // set separation vector
+ if (nearest)
+ ccdVec3Copy(sep, &nearest->witness);
+
+ ccdPtDestroy(&polytope);
+
+ return ret;
+}
+
+
+static int penEPAPosCmp(const void *a, const void *b)
+{
+ ccd_pt_vertex_t *v1, *v2;
+ v1 = *(ccd_pt_vertex_t **)a;
+ v2 = *(ccd_pt_vertex_t **)b;
+
+ if (ccdEq(v1->dist, v2->dist)){
+ return 0;
+ }else if (v1->dist < v2->dist){
+ return -1;
+ }else{
+ return 1;
+ }
+}
+
+static void penEPAPos(const ccd_pt_t *pt, const ccd_pt_el_t *nearest,
+ ccd_vec3_t *pos)
+{
+ ccd_pt_vertex_t *v;
+ ccd_pt_vertex_t **vs;
+ size_t i, len;
+ ccd_real_t scale;
+
+ // compute median
+ len = 0;
+ ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){
+ len++;
+ }
+
+ vs = CCD_ALLOC_ARR(ccd_pt_vertex_t *, len);
+ i = 0;
+ ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){
+ vs[i++] = v;
+ }
+
+ qsort(vs, len, sizeof(ccd_pt_vertex_t *), penEPAPosCmp);
+
+ ccdVec3Set(pos, CCD_ZERO, CCD_ZERO, CCD_ZERO);
+ scale = CCD_ZERO;
+ if (len % 2 == 1)
+ len++;
+
+ for (i = 0; i < len / 2; i++){
+ ccdVec3Add(pos, &vs[i]->v.v1);
+ ccdVec3Add(pos, &vs[i]->v.v2);
+ scale += CCD_REAL(2.);
+ }
+ ccdVec3Scale(pos, CCD_ONE / scale);
+
+ free(vs);
+}
+
+int ccdGJKPenetration(const void *obj1, const void *obj2, const ccd_t *ccd,
+ ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos)
+{
+ ccd_pt_t polytope;
+ ccd_pt_el_t *nearest;
+ int ret;
+
+ ccdPtInit(&polytope);
+
+ ret = __ccdGJKEPA(obj1, obj2, ccd, &polytope, &nearest);
+
+ // set separation vector
+ if (ret == 0 && nearest){
+ // store normalized direction vector
+ ccdVec3Copy(dir, &nearest->witness);
+ ret = ccdVec3SafeNormalize(dir);
+
+ if (ret == 0) {
+ // compute depth of penetration
+ *depth = CCD_SQRT(nearest->dist);
+ // compute position
+ penEPAPos(&polytope, nearest, pos);
+ }
+ }
+
+ ccdPtDestroy(&polytope);
+
+ return ret;
+}
+
+
+
+
+static int __ccdGJK(const void *obj1, const void *obj2,
+ const ccd_t *ccd, ccd_simplex_t *simplex)
+{
+ unsigned long iterations;
+ ccd_vec3_t dir; // direction vector
+ ccd_support_t last; // last support point
+ int do_simplex_res;
+
+ // initialize simplex struct
+ ccdSimplexInit(simplex);
+
+ // get first direction
+ ccd->first_dir(obj1, obj2, &dir);
+ // get first support point
+ __ccdSupport(obj1, obj2, &dir, ccd, &last);
+ // and add this point to simplex as last one
+ ccdSimplexAdd(simplex, &last);
+
+ // set up direction vector to as (O - last) which is exactly -last
+ ccdVec3Copy(&dir, &last.v);
+ ccdVec3Scale(&dir, -CCD_ONE);
+
+ // start iterations
+ for (iterations = 0UL; iterations < ccd->max_iterations; ++iterations) {
+ // obtain support point
+ __ccdSupport(obj1, obj2, &dir, ccd, &last);
+
+ // check if farthest point in Minkowski difference in direction dir
+ // isn't somewhere before origin (the test on negative dot product)
+ // - because if it is, objects are not intersecting at all.
+ if (ccdVec3Dot(&last.v, &dir) < CCD_ZERO){
+ return -1; // intersection not found
+ }
+
+ // add last support vector to simplex
+ ccdSimplexAdd(simplex, &last);
+
+ // if doSimplex returns 1 if objects intersect, -1 if objects don't
+ // intersect and 0 if algorithm should continue
+ do_simplex_res = doSimplex(simplex, &dir);
+ if (do_simplex_res == 1){
+ return 0; // intersection found
+ }else if (do_simplex_res == -1){
+ return -1; // intersection not found
+ }
+
+ if (ccdIsZero(ccdVec3Len2(&dir))){
+ return -1; // intersection not found
+ }
+ }
+
+ // intersection wasn't found
+ return -1;
+}
+
+static int __ccdGJKEPA(const void *obj1, const void *obj2,
+ const ccd_t *ccd,
+ ccd_pt_t *polytope, ccd_pt_el_t **nearest)
+{
+ ccd_simplex_t simplex;
+ ccd_support_t supp; // support point
+ int ret, size;
+
+ *nearest = NULL;
+
+ // run GJK and obtain terminal simplex
+ ret = __ccdGJK(obj1, obj2, ccd, &simplex);
+ if (ret != 0)
+ return -1;
+
+ // transform simplex to polytope - simplex won't be used anymore
+ size = ccdSimplexSize(&simplex);
+ if (size == 4){
+ if (simplexToPolytope4(obj1, obj2, ccd, &simplex, polytope, nearest) != 0){
+ return 0;// touch contact
+ }
+ }else if (size == 3){
+ if (simplexToPolytope3(obj1, obj2, ccd, &simplex, polytope, nearest) != 0){
+ return 0; // touch contact
+ }
+ }else{ // size == 2
+ if (simplexToPolytope2(obj1, obj2, ccd, &simplex, polytope, nearest) != 0){
+ return 0; // touch contact
+ }
+ }
+
+ while (1){
+ // get triangle nearest to origin
+ *nearest = ccdPtNearest(polytope);
+
+ // get next support point
+ if (nextSupport(obj1, obj2, ccd, *nearest, &supp) != 0)
+ break;
+
+ // expand nearest triangle using new point - supp
+ expandPolytope(polytope, *nearest, &supp);
+ }
+
+ return 0;
+}
+
+
+
+static int doSimplex2(ccd_simplex_t *simplex, ccd_vec3_t *dir)
+{
+ const ccd_support_t *A, *B;
+ ccd_vec3_t AB, AO, tmp;
+ ccd_real_t dot;
+
+ // get last added as A
+ A = ccdSimplexLast(simplex);
+ // get the other point
+ B = ccdSimplexPoint(simplex, 0);
+ // compute AB oriented segment
+ ccdVec3Sub2(&AB, &B->v, &A->v);
+ // compute AO vector
+ ccdVec3Copy(&AO, &A->v);
+ ccdVec3Scale(&AO, -CCD_ONE);
+
+ // dot product AB . AO
+ dot = ccdVec3Dot(&AB, &AO);
+
+ // check if origin doesn't lie on AB segment
+ ccdVec3Cross(&tmp, &AB, &AO);
+ if (ccdIsZero(ccdVec3Len2(&tmp)) && dot > CCD_ZERO){
+ return 1;
+ }
+
+ // check if origin is in area where AB segment is
+ if (ccdIsZero(dot) || dot < CCD_ZERO){
+ // origin is in outside are of A
+ ccdSimplexSet(simplex, 0, A);
+ ccdSimplexSetSize(simplex, 1);
+ ccdVec3Copy(dir, &AO);
+ }else{
+ // origin is in area where AB segment is
+
+ // keep simplex untouched and set direction to
+ // AB x AO x AB
+ tripleCross(&AB, &AO, &AB, dir);
+ }
+
+ return 0;
+}
+
+static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir)
+{
+ const ccd_support_t *A, *B, *C;
+ ccd_vec3_t AO, AB, AC, ABC, tmp;
+ ccd_real_t dot, dist;
+
+ // get last added as A
+ A = ccdSimplexLast(simplex);
+ // get the other points
+ B = ccdSimplexPoint(simplex, 1);
+ C = ccdSimplexPoint(simplex, 0);
+
+ // check touching contact
+ dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL);
+ if (ccdIsZero(dist)){
+ return 1;
+ }
+
+ // check if triangle is really triangle (has area > 0)
+ // if not simplex can't be expanded and thus no itersection is found
+ if (ccdVec3Eq(&A->v, &B->v) || ccdVec3Eq(&A->v, &C->v)){
+ return -1;
+ }
+
+ // compute AO vector
+ ccdVec3Copy(&AO, &A->v);
+ ccdVec3Scale(&AO, -CCD_ONE);
+
+ // compute AB and AC segments and ABC vector (perpendircular to triangle)
+ ccdVec3Sub2(&AB, &B->v, &A->v);
+ ccdVec3Sub2(&AC, &C->v, &A->v);
+ ccdVec3Cross(&ABC, &AB, &AC);
+
+ ccdVec3Cross(&tmp, &ABC, &AC);
+ dot = ccdVec3Dot(&tmp, &AO);
+ if (ccdIsZero(dot) || dot > CCD_ZERO){
+ dot = ccdVec3Dot(&AC, &AO);
+ if (ccdIsZero(dot) || dot > CCD_ZERO){
+ // C is already in place
+ ccdSimplexSet(simplex, 1, A);
+ ccdSimplexSetSize(simplex, 2);
+ tripleCross(&AC, &AO, &AC, dir);
+ }else{
+ccd_do_simplex3_45:
+ dot = ccdVec3Dot(&AB, &AO);
+ if (ccdIsZero(dot) || dot > CCD_ZERO){
+ ccdSimplexSet(simplex, 0, B);
+ ccdSimplexSet(simplex, 1, A);
+ ccdSimplexSetSize(simplex, 2);
+ tripleCross(&AB, &AO, &AB, dir);
+ }else{
+ ccdSimplexSet(simplex, 0, A);
+ ccdSimplexSetSize(simplex, 1);
+ ccdVec3Copy(dir, &AO);
+ }
+ }
+ }else{
+ ccdVec3Cross(&tmp, &AB, &ABC);
+ dot = ccdVec3Dot(&tmp, &AO);
+ if (ccdIsZero(dot) || dot > CCD_ZERO){
+ goto ccd_do_simplex3_45;
+ }else{
+ dot = ccdVec3Dot(&ABC, &AO);
+ if (ccdIsZero(dot) || dot > CCD_ZERO){
+ ccdVec3Copy(dir, &ABC);
+ }else{
+ ccd_support_t Ctmp;
+ ccdSupportCopy(&Ctmp, C);
+ ccdSimplexSet(simplex, 0, B);
+ ccdSimplexSet(simplex, 1, &Ctmp);
+
+ ccdVec3Copy(dir, &ABC);
+ ccdVec3Scale(dir, -CCD_ONE);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir)
+{
+ const ccd_support_t *A, *B, *C, *D;
+ ccd_vec3_t AO, AB, AC, AD, ABC, ACD, ADB;
+ int B_on_ACD, C_on_ADB, D_on_ABC;
+ int AB_O, AC_O, AD_O;
+ ccd_real_t dist;
+
+ // get last added as A
+ A = ccdSimplexLast(simplex);
+ // get the other points
+ B = ccdSimplexPoint(simplex, 2);
+ C = ccdSimplexPoint(simplex, 1);
+ D = ccdSimplexPoint(simplex, 0);
+
+ // check if tetrahedron is really tetrahedron (has volume > 0)
+ // if it is not simplex can't be expanded and thus no intersection is
+ // found
+ dist = ccdVec3PointTriDist2(&A->v, &B->v, &C->v, &D->v, NULL);
+ if (ccdIsZero(dist)){
+ return -1;
+ }
+
+ // check if origin lies on some of tetrahedron's face - if so objects
+ // intersect
+ dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL);
+ if (ccdIsZero(dist))
+ return 1;
+ dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &C->v, &D->v, NULL);
+ if (ccdIsZero(dist))
+ return 1;
+ dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &D->v, NULL);
+ if (ccdIsZero(dist))
+ return 1;
+ dist = ccdVec3PointTriDist2(ccd_vec3_origin, &B->v, &C->v, &D->v, NULL);
+ if (ccdIsZero(dist))
+ return 1;
+
+ // compute AO, AB, AC, AD segments and ABC, ACD, ADB normal vectors
+ ccdVec3Copy(&AO, &A->v);
+ ccdVec3Scale(&AO, -CCD_ONE);
+ ccdVec3Sub2(&AB, &B->v, &A->v);
+ ccdVec3Sub2(&AC, &C->v, &A->v);
+ ccdVec3Sub2(&AD, &D->v, &A->v);
+ ccdVec3Cross(&ABC, &AB, &AC);
+ ccdVec3Cross(&ACD, &AC, &AD);
+ ccdVec3Cross(&ADB, &AD, &AB);
+
+ // side (positive or negative) of B, C, D relative to planes ACD, ADB
+ // and ABC respectively
+ B_on_ACD = ccdSign(ccdVec3Dot(&ACD, &AB));
+ C_on_ADB = ccdSign(ccdVec3Dot(&ADB, &AC));
+ D_on_ABC = ccdSign(ccdVec3Dot(&ABC, &AD));
+
+ // whether origin is on same side of ACD, ADB, ABC as B, C, D
+ // respectively
+ AB_O = ccdSign(ccdVec3Dot(&ACD, &AO)) == B_on_ACD;
+ AC_O = ccdSign(ccdVec3Dot(&ADB, &AO)) == C_on_ADB;
+ AD_O = ccdSign(ccdVec3Dot(&ABC, &AO)) == D_on_ABC;
+
+ if (AB_O && AC_O && AD_O){
+ // origin is in tetrahedron
+ return 1;
+
+ // rearrange simplex to triangle and call doSimplex3()
+ }else if (!AB_O){
+ // B is farthest from the origin among all of the tetrahedron's
+ // points, so remove it from the list and go on with the triangle
+ // case
+
+ // D and C are in place
+ ccdSimplexSet(simplex, 2, A);
+ ccdSimplexSetSize(simplex, 3);
+ }else if (!AC_O){
+ // C is farthest
+ ccdSimplexSet(simplex, 1, D);
+ ccdSimplexSet(simplex, 0, B);
+ ccdSimplexSet(simplex, 2, A);
+ ccdSimplexSetSize(simplex, 3);
+ }else{ // (!AD_O)
+ ccdSimplexSet(simplex, 0, C);
+ ccdSimplexSet(simplex, 1, B);
+ ccdSimplexSet(simplex, 2, A);
+ ccdSimplexSetSize(simplex, 3);
+ }
+
+ return doSimplex3(simplex, dir);
+}
+
+static int doSimplex(ccd_simplex_t *simplex, ccd_vec3_t *dir)
+{
+ if (ccdSimplexSize(simplex) == 2){
+ // simplex contains segment only one segment
+ return doSimplex2(simplex, dir);
+ }else if (ccdSimplexSize(simplex) == 3){
+ // simplex contains triangle
+ return doSimplex3(simplex, dir);
+ }else{ // ccdSimplexSize(simplex) == 4
+ // tetrahedron - this is the only shape which can encapsule origin
+ // so doSimplex4() also contains test on it
+ return doSimplex4(simplex, dir);
+ }
+}
+
+_ccd_inline void tripleCross(const ccd_vec3_t *a, const ccd_vec3_t *b,
+ const ccd_vec3_t *c, ccd_vec3_t *d)
+{
+ ccd_vec3_t e;
+ ccdVec3Cross(&e, a, b);
+ ccdVec3Cross(d, &e, c);
+}
+
+
+
+/** Transforms simplex to polytope. It is assumed that simplex has 4
+ * vertices! */
+static int simplexToPolytope4(const void *obj1, const void *obj2,
+ const ccd_t *ccd,
+ ccd_simplex_t *simplex,
+ ccd_pt_t *pt, ccd_pt_el_t **nearest)
+{
+ const ccd_support_t *a, *b, *c, *d;
+ int use_polytope3;
+ ccd_real_t dist;
+ ccd_pt_vertex_t *v[4];
+ ccd_pt_edge_t *e[6];
+ size_t i;
+
+ a = ccdSimplexPoint(simplex, 0);
+ b = ccdSimplexPoint(simplex, 1);
+ c = ccdSimplexPoint(simplex, 2);
+ d = ccdSimplexPoint(simplex, 3);
+
+ // check if origin lies on some of tetrahedron's face - if so use
+ // simplexToPolytope3()
+ use_polytope3 = 0;
+ dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &b->v, &c->v, NULL);
+ if (ccdIsZero(dist)){
+ use_polytope3 = 1;
+ }
+ dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &c->v, &d->v, NULL);
+ if (ccdIsZero(dist)){
+ use_polytope3 = 1;
+ ccdSimplexSet(simplex, 1, c);
+ ccdSimplexSet(simplex, 2, d);
+ }
+ dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &b->v, &d->v, NULL);
+ if (ccdIsZero(dist)){
+ use_polytope3 = 1;
+ ccdSimplexSet(simplex, 2, d);
+ }
+ dist = ccdVec3PointTriDist2(ccd_vec3_origin, &b->v, &c->v, &d->v, NULL);
+ if (ccdIsZero(dist)){
+ use_polytope3 = 1;
+ ccdSimplexSet(simplex, 0, b);
+ ccdSimplexSet(simplex, 1, c);
+ ccdSimplexSet(simplex, 2, d);
+ }
+
+ if (use_polytope3){
+ ccdSimplexSetSize(simplex, 3);
+ return simplexToPolytope3(obj1, obj2, ccd, simplex, pt, nearest);
+ }
+
+ // no touching contact - simply create tetrahedron
+ for (i = 0; i < 4; i++){
+ v[i] = ccdPtAddVertex(pt, ccdSimplexPoint(simplex, i));
+ }
+
+ e[0] = ccdPtAddEdge(pt, v[0], v[1]);
+ e[1] = ccdPtAddEdge(pt, v[1], v[2]);
+ e[2] = ccdPtAddEdge(pt, v[2], v[0]);
+ e[3] = ccdPtAddEdge(pt, v[3], v[0]);
+ e[4] = ccdPtAddEdge(pt, v[3], v[1]);
+ e[5] = ccdPtAddEdge(pt, v[3], v[2]);
+
+ ccdPtAddFace(pt, e[0], e[1], e[2]);
+ ccdPtAddFace(pt, e[3], e[4], e[0]);
+ ccdPtAddFace(pt, e[4], e[5], e[1]);
+ ccdPtAddFace(pt, e[5], e[3], e[2]);
+
+ return 0;
+}
+
+/** Transforms simplex to polytope, three vertices required */
+static int simplexToPolytope3(const void *obj1, const void *obj2,
+ const ccd_t *ccd,
+ const ccd_simplex_t *simplex,
+ ccd_pt_t *pt, ccd_pt_el_t **nearest)
+{
+ const ccd_support_t *a, *b, *c;
+ ccd_support_t d, d2;
+ ccd_vec3_t ab, ac, dir;
+ ccd_pt_vertex_t *v[5];
+ ccd_pt_edge_t *e[9];
+ ccd_real_t dist, dist2;
+
+ *nearest = NULL;
+
+ a = ccdSimplexPoint(simplex, 0);
+ b = ccdSimplexPoint(simplex, 1);
+ c = ccdSimplexPoint(simplex, 2);
+
+ // If only one triangle left from previous GJK run origin lies on this
+ // triangle. So it is necessary to expand triangle into two
+ // tetrahedrons connected with base (which is exactly abc triangle).
+
+ // get next support point in direction of normal of triangle
+ ccdVec3Sub2(&ab, &b->v, &a->v);
+ ccdVec3Sub2(&ac, &c->v, &a->v);
+ ccdVec3Cross(&dir, &ab, &ac);
+ __ccdSupport(obj1, obj2, &dir, ccd, &d);
+ dist = ccdVec3PointTriDist2(&d.v, &a->v, &b->v, &c->v, NULL);
+
+ // and second one take in opposite direction
+ ccdVec3Scale(&dir, -CCD_ONE);
+ __ccdSupport(obj1, obj2, &dir, ccd, &d2);
+ dist2 = ccdVec3PointTriDist2(&d2.v, &a->v, &b->v, &c->v, NULL);
+
+ // check if face isn't already on edge of minkowski sum and thus we
+ // have touching contact
+ if (ccdIsZero(dist) || ccdIsZero(dist2)){
+ v[0] = ccdPtAddVertex(pt, a);
+ v[1] = ccdPtAddVertex(pt, b);
+ v[2] = ccdPtAddVertex(pt, c);
+ e[0] = ccdPtAddEdge(pt, v[0], v[1]);
+ e[1] = ccdPtAddEdge(pt, v[1], v[2]);
+ e[2] = ccdPtAddEdge(pt, v[2], v[0]);
+ *nearest = (ccd_pt_el_t *)ccdPtAddFace(pt, e[0], e[1], e[2]);
+
+ return -1;
+ }
+
+ // form polyhedron
+ v[0] = ccdPtAddVertex(pt, a);
+ v[1] = ccdPtAddVertex(pt, b);
+ v[2] = ccdPtAddVertex(pt, c);
+ v[3] = ccdPtAddVertex(pt, &d);
+ v[4] = ccdPtAddVertex(pt, &d2);
+
+ e[0] = ccdPtAddEdge(pt, v[0], v[1]);
+ e[1] = ccdPtAddEdge(pt, v[1], v[2]);
+ e[2] = ccdPtAddEdge(pt, v[2], v[0]);
+
+ e[3] = ccdPtAddEdge(pt, v[3], v[0]);
+ e[4] = ccdPtAddEdge(pt, v[3], v[1]);
+ e[5] = ccdPtAddEdge(pt, v[3], v[2]);
+
+ e[6] = ccdPtAddEdge(pt, v[4], v[0]);
+ e[7] = ccdPtAddEdge(pt, v[4], v[1]);
+ e[8] = ccdPtAddEdge(pt, v[4], v[2]);
+
+ ccdPtAddFace(pt, e[3], e[4], e[0]);
+ ccdPtAddFace(pt, e[4], e[5], e[1]);
+ ccdPtAddFace(pt, e[5], e[3], e[2]);
+
+ ccdPtAddFace(pt, e[6], e[7], e[0]);
+ ccdPtAddFace(pt, e[7], e[8], e[1]);
+ ccdPtAddFace(pt, e[8], e[6], e[2]);
+
+ return 0;
+}
+
+/** Transforms simplex to polytope, two vertices required */
+static int simplexToPolytope2(const void *obj1, const void *obj2,
+ const ccd_t *ccd,
+ const ccd_simplex_t *simplex,
+ ccd_pt_t *pt, ccd_pt_el_t **nearest)
+{
+ const ccd_support_t *a, *b;
+ ccd_vec3_t ab, ac, dir;
+ ccd_support_t supp[4];
+ ccd_pt_vertex_t *v[6];
+ ccd_pt_edge_t *e[12];
+ size_t i;
+ int found;
+
+ a = ccdSimplexPoint(simplex, 0);
+ b = ccdSimplexPoint(simplex, 1);
+
+ // This situation is a bit tricky. If only one segment comes from
+ // previous run of GJK - it means that either this segment is on
+ // minkowski edge (and thus we have touch contact) or it it isn't and
+ // therefore segment is somewhere *inside* minkowski sum and it *must*
+ // be possible to fully enclose this segment with polyhedron formed by
+ // at least 8 triangle faces.
+
+ // get first support point (any)
+ found = 0;
+ for (i = 0; i < ccd_points_on_sphere_len; i++){
+ __ccdSupport(obj1, obj2, &ccd_points_on_sphere[i], ccd, &supp[0]);
+ if (!ccdVec3Eq(&a->v, &supp[0].v) && !ccdVec3Eq(&b->v, &supp[0].v)){
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ goto simplexToPolytope2_touching_contact;
+
+ // get second support point in opposite direction than supp[0]
+ ccdVec3Copy(&dir, &supp[0].v);
+ ccdVec3Scale(&dir, -CCD_ONE);
+ __ccdSupport(obj1, obj2, &dir, ccd, &supp[1]);
+ if (ccdVec3Eq(&a->v, &supp[1].v) || ccdVec3Eq(&b->v, &supp[1].v))
+ goto simplexToPolytope2_touching_contact;
+
+ // next will be in direction of normal of triangle a,supp[0],supp[1]
+ ccdVec3Sub2(&ab, &supp[0].v, &a->v);
+ ccdVec3Sub2(&ac, &supp[1].v, &a->v);
+ ccdVec3Cross(&dir, &ab, &ac);
+ __ccdSupport(obj1, obj2, &dir, ccd, &supp[2]);
+ if (ccdVec3Eq(&a->v, &supp[2].v) || ccdVec3Eq(&b->v, &supp[2].v))
+ goto simplexToPolytope2_touching_contact;
+
+ // and last one will be in opposite direction
+ ccdVec3Scale(&dir, -CCD_ONE);
+ __ccdSupport(obj1, obj2, &dir, ccd, &supp[3]);
+ if (ccdVec3Eq(&a->v, &supp[3].v) || ccdVec3Eq(&b->v, &supp[3].v))
+ goto simplexToPolytope2_touching_contact;
+
+ goto simplexToPolytope2_not_touching_contact;
+simplexToPolytope2_touching_contact:
+ v[0] = ccdPtAddVertex(pt, a);
+ v[1] = ccdPtAddVertex(pt, b);
+ *nearest = (ccd_pt_el_t *)ccdPtAddEdge(pt, v[0], v[1]);
+ return -1;
+
+simplexToPolytope2_not_touching_contact:
+ // form polyhedron
+ v[0] = ccdPtAddVertex(pt, a);
+ v[1] = ccdPtAddVertex(pt, &supp[0]);
+ v[2] = ccdPtAddVertex(pt, b);
+ v[3] = ccdPtAddVertex(pt, &supp[1]);
+ v[4] = ccdPtAddVertex(pt, &supp[2]);
+ v[5] = ccdPtAddVertex(pt, &supp[3]);
+
+ e[0] = ccdPtAddEdge(pt, v[0], v[1]);
+ e[1] = ccdPtAddEdge(pt, v[1], v[2]);
+ e[2] = ccdPtAddEdge(pt, v[2], v[3]);
+ e[3] = ccdPtAddEdge(pt, v[3], v[0]);
+
+ e[4] = ccdPtAddEdge(pt, v[4], v[0]);
+ e[5] = ccdPtAddEdge(pt, v[4], v[1]);
+ e[6] = ccdPtAddEdge(pt, v[4], v[2]);
+ e[7] = ccdPtAddEdge(pt, v[4], v[3]);
+
+ e[8] = ccdPtAddEdge(pt, v[5], v[0]);
+ e[9] = ccdPtAddEdge(pt, v[5], v[1]);
+ e[10] = ccdPtAddEdge(pt, v[5], v[2]);
+ e[11] = ccdPtAddEdge(pt, v[5], v[3]);
+
+ ccdPtAddFace(pt, e[4], e[5], e[0]);
+ ccdPtAddFace(pt, e[5], e[6], e[1]);
+ ccdPtAddFace(pt, e[6], e[7], e[2]);
+ ccdPtAddFace(pt, e[7], e[4], e[3]);
+
+ ccdPtAddFace(pt, e[8], e[9], e[0]);
+ ccdPtAddFace(pt, e[9], e[10], e[1]);
+ ccdPtAddFace(pt, e[10], e[11], e[2]);
+ ccdPtAddFace(pt, e[11], e[8], e[3]);
+
+ return 0;
+}
+
+/** Expands polytope's tri by new vertex v. Triangle tri is replaced by
+ * three triangles each with one vertex in v. */
+static void expandPolytope(ccd_pt_t *pt, ccd_pt_el_t *el,
+ const ccd_support_t *newv)
+{
+ ccd_pt_vertex_t *v[5];
+ ccd_pt_edge_t *e[8] = {0};
+ ccd_pt_face_t *f[2];
+
+
+ // element can be either segment or triangle
+ if (el->type == CCD_PT_EDGE){
+ // In this case, segment should be replaced by new point.
+ // Simpliest case is when segment stands alone and in this case
+ // this segment is replaced by two other segments both connected to
+ // newv.
+ // Segment can be also connected to max two faces and in that case
+ // each face must be replaced by two other faces. To do this
+ // correctly it is necessary to have correctly ordered edges and
+ // vertices which is exactly what is done in following code.
+ //
+
+ ccdPtEdgeVertices((const ccd_pt_edge_t *)el, &v[0], &v[2]);
+
+ ccdPtEdgeFaces((ccd_pt_edge_t *)el, &f[0], &f[1]);
+
+ if (f[0]){
+ ccdPtFaceEdges(f[0], &e[0], &e[1], &e[2]);
+ if (e[0] == (ccd_pt_edge_t *)el){
+ e[0] = e[2];
+ }else if (e[1] == (ccd_pt_edge_t *)el){
+ e[1] = e[2];
+ }
+ ccdPtEdgeVertices(e[0], &v[1], &v[3]);
+ if (v[1] != v[0] && v[3] != v[0]){
+ e[2] = e[0];
+ e[0] = e[1];
+ e[1] = e[2];
+ if (v[1] == v[2])
+ v[1] = v[3];
+ }else{
+ if (v[1] == v[0])
+ v[1] = v[3];
+ }
+
+ if (f[1]){
+ ccdPtFaceEdges(f[1], &e[2], &e[3], &e[4]);
+ if (e[2] == (ccd_pt_edge_t *)el){
+ e[2] = e[4];
+ }else if (e[3] == (ccd_pt_edge_t *)el){
+ e[3] = e[4];
+ }
+ ccdPtEdgeVertices(e[2], &v[3], &v[4]);
+ if (v[3] != v[2] && v[4] != v[2]){
+ e[4] = e[2];
+ e[2] = e[3];
+ e[3] = e[4];
+ if (v[3] == v[0])
+ v[3] = v[4];
+ }else{
+ if (v[3] == v[2])
+ v[3] = v[4];
+ }
+ }
+
+
+ v[4] = ccdPtAddVertex(pt, newv);
+
+ ccdPtDelFace(pt, f[0]);
+ if (f[1]){
+ ccdPtDelFace(pt, f[1]);
+ ccdPtDelEdge(pt, (ccd_pt_edge_t *)el);
+ }
+
+ e[4] = ccdPtAddEdge(pt, v[4], v[2]);
+ e[5] = ccdPtAddEdge(pt, v[4], v[0]);
+ e[6] = ccdPtAddEdge(pt, v[4], v[1]);
+ if (f[1])
+ e[7] = ccdPtAddEdge(pt, v[4], v[3]);
+
+ ccdPtAddFace(pt, e[1], e[4], e[6]);
+ ccdPtAddFace(pt, e[0], e[6], e[5]);
+ if (f[1]){
+ ccdPtAddFace(pt, e[3], e[5], e[7]);
+ ccdPtAddFace(pt, e[4], e[7], e[2]);
+ }else{
+ ccdPtAddFace(pt, e[4], e[5], (ccd_pt_edge_t *)el);
+ }
+ }
+ }else{ // el->type == CCD_PT_FACE
+ // replace triangle by tetrahedron without base (base would be the
+ // triangle that will be removed)
+
+ // get triplet of surrounding edges and vertices of triangle face
+ ccdPtFaceEdges((const ccd_pt_face_t *)el, &e[0], &e[1], &e[2]);
+ ccdPtEdgeVertices(e[0], &v[0], &v[1]);
+ ccdPtEdgeVertices(e[1], &v[2], &v[3]);
+
+ // following code sorts edges to have e[0] between vertices 0-1,
+ // e[1] between 1-2 and e[2] between 2-0
+ if (v[2] != v[1] && v[3] != v[1]){
+ // swap e[1] and e[2]
+ e[3] = e[1];
+ e[1] = e[2];
+ e[2] = e[3];
+ }
+ if (v[3] != v[0] && v[3] != v[1])
+ v[2] = v[3];
+
+ // remove triangle face
+ ccdPtDelFace(pt, (ccd_pt_face_t *)el);
+
+ // expand triangle to tetrahedron
+ v[3] = ccdPtAddVertex(pt, newv);
+ e[3] = ccdPtAddEdge(pt, v[3], v[0]);
+ e[4] = ccdPtAddEdge(pt, v[3], v[1]);
+ e[5] = ccdPtAddEdge(pt, v[3], v[2]);
+
+ ccdPtAddFace(pt, e[3], e[4], e[0]);
+ ccdPtAddFace(pt, e[4], e[5], e[1]);
+ ccdPtAddFace(pt, e[5], e[3], e[2]);
+ }
+}
+
+/** Finds next support point (at stores it in out argument).
+ * Returns 0 on success, -1 otherwise */
+static int nextSupport(const void *obj1, const void *obj2, const ccd_t *ccd,
+ const ccd_pt_el_t *el,
+ ccd_support_t *out)
+{
+ ccd_vec3_t *a, *b, *c;
+ ccd_real_t dist;
+
+ if (el->type == CCD_PT_VERTEX)
+ return -1;
+
+ // touch contact
+ if (ccdIsZero(el->dist))
+ return -1;
+
+ __ccdSupport(obj1, obj2, &el->witness, ccd, out);
+
+ if (el->type == CCD_PT_EDGE){
+ // fetch end points of edge
+ ccdPtEdgeVec3((ccd_pt_edge_t *)el, &a, &b);
+
+ // get distance from segment
+ dist = ccdVec3PointSegmentDist2(&out->v, a, b, NULL);
+ }else{ // el->type == CCD_PT_FACE
+ // fetch vertices of triangle face
+ ccdPtFaceVec3((ccd_pt_face_t *)el, &a, &b, &c);
+
+ // check if new point can significantly expand polytope
+ dist = ccdVec3PointTriDist2(&out->v, a, b, c, NULL);
+ }
+
+ if (dist < ccd->epa_tolerance)
+ return -1;
+
+ return 0;
+}