# src/PointTriangle.h

#define vecdot(a,b) ((a).x*(b).x + (a).y*(b).y + (a).z*(b).z)
#define vecdotproduct(a,b) ((coord){(a).y*(b).z - (a).z*(b).y,	\
(a).z*(b).x - (a).x*(b).z,	\
(a).x*(b).y - (a).y*(b).x})
#define vecdiff(a,b)  ((coord){(a).x - (b).x, (a).y - (b).y, (a).z - (b).z})
#define vecdist2(a,b) (sq((a).x - (b).x) + sq((a).y - (b).y) + sq((a).z - (b).z))
• Returns the squared distance between point P and triangle P0, P1,
• P2. Squared distance is returned in d2. s and t returns the
• closest point in parametric form in terms of edges P0P1 and P0P2.
double PointTriangleDistance (const coord * P,
const coord * P0,
const coord * P1,
const coord * P2,
double * s, double * t)
{
// From http://www.geometrictools.com/Documentation/DistancePoint3Triangle3.pdf
double d2;
coord diff = vecdiff (*P0, *P);
coord edge0 = vecdiff(*P1, *P0);
coord edge1 = vecdiff(*P2, *P0);
double a00 = vecdot(edge0, edge0);
double a01 = vecdot(edge0, edge1);
double a11 = vecdot(edge1, edge1);
double b0 = vecdot(diff, edge0);
double b1 = vecdot(diff, edge1);
double c = vecdot(diff, diff);
double det = fabs(a00*a11 - a01*a01);
*s = a01*b1 - a11*b0;
*t = a01*b0 - a00*b1;

if (*s + *t <= det)
{
if (*s < 0.0)
{
if (*t < 0.0)  // region 4
{
if (b0 < 0.0)
{
*t = 0.0;
if (-b0 >= a00)
{
*s = 1.0;
d2 = a00 + 2.0*b0 + c;
}
else
{
*s = -b0/a00;
d2 = b0**s + c;
}
}
else
{
*s = 0.0;
if (b1 >= 0.0)
{
*t = 0.0;
d2 = c;
}
else if (-b1 >= a11)
{
*t = 1.0;
d2 = a11 + 2.0*b1 + c;
}
else
{
*t = -b1/a11;
d2 = b1**t + c;
}
}
}
else  // region 3
{
*s = 0.0;
if (b1 >= 0.0)
{
*t = 0.0;
d2 = c;
}
else if (-b1 >= a11)
{
*t = 1.0;
d2 = a11 + 2.0*b1 + c;
}
else
{
*t = -b1/a11;
d2 = b1**t + c;
}
}
}
else if (*t < 0.0)  // region 5
{
*t = 0.0;
if (b0 >= 0.0)
{
*s = 0.0;
d2 = c;
}
else if (-b0 >= a00)
{
*s = 1.0;
d2 = a00 + 2.0*b0 + c;
}
else
{
*s = -b0/a00;
d2 = b0**s + c;
}
}
else  // region 0
{
if (det == 0.) // degenerate triangle
d2 = HUGE;
else {
double invDet = 1.0/det;
*s *= invDet;
*t *= invDet;
d2 = *s*(a00**s + a01**t + 2.0*b0) + *t*(a01**s + a11**t + 2.0*b1) + c;
}
}
}
else
{
if (*s < 0.0)  // region 2
{
double tmp0 = a01 + b0;
double tmp1 = a11 + b1;
if (tmp1 > tmp0)
{
double numer = tmp1 - tmp0;
double denom = a00 - 2.0*a01 + a11;
if (numer >= denom)
{
*s = 1.0;
*t = 0.0;
d2 = a00 + 2.0*b0 + c;
}
else
{
*s = numer/denom;
*t = 1.0 - *s;
d2 = *s*(a00**s + a01**t + 2.0*b0) + *t*(a01**s + a11**t + 2.0*b1) + c;
}
}
else
{
*s = 0.0;
if (tmp1 <= 0.0)
{
*t = 1.0;
d2 = a11 + 2.0*b1 + c;
}
else if (b1 >= 0.0)
{
*t = 0.0;
d2 = c;
}
else
{
*t = -b1/a11;
d2 = b1**t + c;
}
}
}
else if (*t < 0.0)  // region 6
{
double tmp0 = a01 + b1;
double tmp1 = a00 + b0;
if (tmp1 > tmp0)
{
double numer = tmp1 - tmp0;
double denom = a00 - 2.0*a01 + a11;
if (numer >= denom)
{
*t = 1.0;
*s = 0.0;
d2 = a11 + 2.0*b1 + c;
}
else
{
*t = numer/denom;
*s = 1.0 - *t;
d2 = *s*(a00**s + a01**t + 2.0*b0) + *t*(a01**s + a11**t + 2.0*b1) + c;
}
}
else
{
*t = 0.0;
if (tmp1 <= 0.0)
{
*s = 1.0;
d2 = a00 + 2.0*b0 + c;
}
else if (b0 >= 0.0)
{
*s = 0.0;
d2 = c;
}
else
{
*s = -b0/a00;
d2 = b0**s + c;
}
}
}
else  // region 1
{
double numer = a11 + b1 - a01 - b0;
if (numer <= 0.0)
{
*s = 0.0;
*t = 1.0;
d2 = a11 + 2.0*b1 + c;
}
else
{
double denom = a00 - 2.0*a01 + a11;
if (numer >= denom)
{
*s = 1.0;
*t = 0.0;
d2 = a00 + 2.0*b0 + c;
}
else
{
*s = numer/denom;
*t = 1.0 - *s;
d2 = *s*(a00**s + a01**t + 2.0*b0) + *t*(a01**s + a11**t + 2.0*b1) + c;
}
}
}
}

// Account for numerical round-off error
if (d2 < 0.0)
{
d2 = 0.0;
}

return d2;
}

int PointTriangleOrientation (const coord * P,
const coord * P0,
const coord * P1,
const coord * P2)
{
coord diff = vecdiff (*P0, *P);
coord edge0 = vecdiff (*P1, *P0);
coord edge1 = vecdiff (*P2, *P0);
coord n = vecdotproduct (edge0, edge1);
return sign (vecdot (diff, n));
}

Returns the squared distance between p and [p0:p1].

double PointSegmentDistance (const coord * p, const coord * p0, const coord * p1,
coord * segmentClosest, double * segmentParameter)
{
// The direction vector is not unit length.  The normalization is deferred
// until it is needed.
coord direction = vecdiff (*p1, *p0);
coord diff = vecdiff (*p, *p1);
double t = vecdot(direction, diff);
if (t >= (double)0)
{
*segmentParameter = (double)1;
*segmentClosest = *p1;
}
else
{
diff = vecdiff (*p, *p0);
t = vecdot(direction, diff);
if (t <= (double)0)
{
*segmentParameter = (double)0;
*segmentClosest = *p0;
}
else
{
double sqrLength = vecdot(direction, direction);
if (sqrLength > (double)0)
{
t /= sqrLength;
*segmentParameter = t;
(*segmentClosest).z = 0.;
foreach_dimension()
(*segmentClosest).x = (*p0).x + t * direction.x;
}
else
{
*segmentParameter = (double)0;
*segmentClosest = *p0;
}
}
}

diff = vecdiff (*p, *segmentClosest);
return vecdot(diff, diff);
}

int PointSegmentOrientation (const coord * P,
const coord * P0,
const coord * P1)
{
coord diff = vecdiff (*P0, *P);
coord edge0 = vecdiff (*P0, *P1);
coord n = vecdotproduct (diff, edge0);
return sign(n.z);
}