static public double segmentToSegment(Point3d s0start, Point3d s0end,
Point3d s1start, Point3d s1end,
Point3d s0int, Point3d s1int, double[] param) {
double s, t;
Vector3d diff = new Vector3d();
diff.sub(s0start, s1start);
Vector3d seg0dir = new Vector3d();
seg0dir.sub(s0end, s0start);
Vector3d seg1dir = new Vector3d();
seg1dir.sub(s1end, s1start);
double A = seg0dir.dot(seg0dir); // Dot(seg0dir,seg0dir);
double B = -seg0dir.dot(seg1dir); // -Dot(seg0dir,seg1dir);
double C = seg1dir.dot(seg1dir); // Dot(seg1dir,seg1dir);
double D = seg0dir.dot(diff); // Dot(seg0dir,diff);
double E; // -Dot(seg1dir,diff), defer until needed
double F = diff.dot(diff); // Dot(diff,diff);
double det = Math.abs(A * C - B * B); // A*C-B*B = |Cross(M0,M1)|^2 >= 0
double tmp;
if (det >= ZERO_TOL) {
// line segments are not parallel
E = -seg1dir.dot(diff); // -Dot(seg1dir,diff);
s = B * E - C * D;
t = B * D - A * E;
if (s >= 0) {
if (s <= det) {
if (t >= 0) {
if (t <= det) { // region 0 (interior)
// minimum at two interior points of 3D lines
double invDet = 1.0f / det;
s *= invDet;
t *= invDet;
if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start);
if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(s * (A * s + B * t + 2 * D) + t
* (B * s + C * t + 2 * E) + F);
}
else { // region 3 (side)
t = 1;
tmp = B + D;
if (tmp >= 0) {
s = 0;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(C + 2 * E + F);
}
else if (-tmp >= A) {
s = 1;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + C + F + 2 * (E + tmp));
}
else {
s = -tmp / A;
if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(tmp * s + C + 2 * E + F);
}
}
}
else { // region 7 (side)
t = 0;
if (D >= 0) {
s = 0;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(F);
}
else if (-D >= A) {
s = 1;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + 2 * D + F);
}
else {
s = -D / A;
if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(D * s + F);
}
}
}
else {
if (t >= 0) {
if (t <= det) { // region 1 (side)
s = 1;
tmp = B + E;
if (tmp >= 0) {
t = 0;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + 2 * D + F);
}
else if (-tmp >= C) {
t = 1;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + C + F + 2 * (D + tmp));
}
else {
t = -tmp / C;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(tmp * t + A + 2 * D + F);
}
}
else { // region 2 (corner)
tmp = B + D;
if (-tmp <= A) {
t = 1;
if (tmp >= 0) {
s = 0;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(C + 2 * E + F);
}
else {
s = -tmp / A;
if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(tmp * s + C + 2 * E + F);
}
}
else {
s = 1;
tmp = B + E;
if (tmp >= 0) {
t = 0;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + 2 * D + F);
}
else if (-tmp >= C) {
t = 1;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + C + F + 2 * (D + tmp));
}
else {
t = -tmp / C;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(tmp * t + A + 2 * D + F);
}
}
}
}
else { // region 8 (corner)
if (-D < A) {
t = 0;
if (D >= 0) {
s = 0;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(F);
}
else {
s = -D / A;
if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(D * s + F);
}
}
else {
s = 1;
tmp = B + E;
if (tmp >= 0) {
t = 0;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + 2 * D + F);
}
else if (-tmp >= C) {
t = 1;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + C + F + 2 * (D + tmp));
}
else {
t = -tmp / C;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(tmp * t + A + 2 * D + F);
}
}
}
}
}
else {
if (t >= 0) {
if (t <= det) { // region 5 (side)
s = 0;
if (E >= 0) {
t = 0;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(F);
}
else if (-E >= C) {
t = 1;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(C + 2 * E + F);
}
else {
t = -E / C;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(E * t + F);
}
}
else { // region 4 (corner)
tmp = B + D;
if (tmp < 0) {
t = 1;
if (-tmp >= A) {
s = 1;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + C + F + 2 * (E + tmp));
}
else {
s = -tmp / A;
if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(tmp * s + C + 2 * E + F);
}
}
else {
s = 0;
if (E >= 0) {
t = 0;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(F);
}
else if (-E >= C) {
t = 1;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(C + 2 * E + F);
}
else {
t = -E / C;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(E * t + F);
}
}
}
}
else { // region 6 (corner)
if (D < 0) {
t = 0;
if (-D >= A) {
s = 1;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + 2 * D + F);
}
else {
s = -D / A;
if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(D * s + F);
}
}
else {
s = 0;
if (E >= 0) {
t = 0;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(F);
}
else if (-E >= C) {
t = 1;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(C + 2 * E + F);
}
else {
t = -E / C;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(E * t + F);
}
}
}
}
}
else {
// line segments are parallel
if (B > 0) {
// direction vectors form an obtuse angle
if (D >= 0) {
s = 0;
t = 0;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(F);
}
else if (-D <= A) {
s = -D / A;
t = 0;
if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(D * s + F);
}
else {
E = -seg1dir.dot(diff); // -Dot(seg1dir,diff);
s = 1;
tmp = A + D;
if (-tmp >= B) {
t = 1;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1end);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + C + F + 2 * (B + D + E));
}
else {
t = -tmp / B;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.scaleAdd(t, seg1dir, s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + 2 * D + F + t * (C * t + 2 * (B + E)));
}
}
}
else {
// direction vectors form an acute angle
if (-D >= A) {
s = 1;
t = 0;
if (s0int != null) s0int.set(s0end);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(A + 2 * D + F);
}
else if (D <= 0) {
s = -D / A;
t = 0;
if (s0int != null) s0int.scaleAdd(s, seg0dir, s0start);
if (s1int != null) s1int.set(s1start);
if (param != null) { param[0] = s; param[1] = t; }
return Math.abs(D * s + F);
}
else {
E = -seg1dir.dot(diff); // -Dot(seg1dir,diff);
s = 0;
if (D >= -B) {
t = 1;
if (s0int != null) s0int.set(s0start);
if (s1int != null) s1int.set(s1end);