/*
* Copyright (C) 2011-2015, Peter Abeles. All Rights Reserved.
*
* This file is part of Geometric Regression Library (GeoRegression).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package georegression.metric;
import georegression.geometry.UtilLine3D_F64;
import georegression.geometry.UtilPlane3D_F64;
import georegression.misc.GrlConstants;
import georegression.struct.line.LineParametric3D_F64;
import georegression.struct.line.LineSegment3D_F64;
import georegression.struct.plane.PlaneGeneral3D_F64;
import georegression.struct.plane.PlaneNormal3D_F64;
import georegression.struct.point.Point3D_F64;
import georegression.struct.point.Vector3D_F64;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* @author Peter Abeles
*/
public class TestClosestPoint3D_F64 {
/**
* Compute truth from 3 random points then see if the 3rd point is found again.
*/
@Test
public void closestPoint_line() {
Point3D_F64 a = new Point3D_F64( 1, 1, 1 );
Point3D_F64 b = new Point3D_F64( 1.5, -2.5, 9 );
Point3D_F64 c = new Point3D_F64( 10.1, 6, -3 );
Vector3D_F64 va = new Vector3D_F64( a, b );
Vector3D_F64 vc = new Vector3D_F64( c, b );
LineParametric3D_F64 lineA = new LineParametric3D_F64( a, va );
LineParametric3D_F64 lineB = new LineParametric3D_F64( c, vc );
Point3D_F64 foundB = ClosestPoint3D_F64.closestPoint(lineA, lineB, null);
assertTrue( b.isIdentical( foundB, GrlConstants.DOUBLE_TEST_TOL ) );
checkIsClosest(foundB,lineA,lineB);
// check two arbitrary lines
lineA = new LineParametric3D_F64( 2,3,-4,-9,3,6.7 );
lineB = new LineParametric3D_F64( -0.4,0,1.2,-3.4,4,-5 );
foundB = ClosestPoint3D_F64.closestPoint(lineA, lineB, null);
checkIsClosest(foundB,lineA,lineB);
}
@Test
public void closestPoints_lines() {
Point3D_F64 a = new Point3D_F64( 1, 1, 1 );
Point3D_F64 b = new Point3D_F64( 1.5, -2.5, 9 );
Point3D_F64 c = new Point3D_F64( 10.1, 6, -3 );
Vector3D_F64 va = new Vector3D_F64( a, b );
Vector3D_F64 vc = new Vector3D_F64( c, b );
// normalize the vector so that the value 't' is euclidean distance
va.normalize();
vc.normalize();
LineParametric3D_F64 lineA = new LineParametric3D_F64( a, va );
LineParametric3D_F64 lineB = new LineParametric3D_F64( c, vc );
double param[] = new double[2];
assertTrue(ClosestPoint3D_F64.closestPoints(lineA, lineB, param));
assertEquals( a.distance(b) , param[0] , GrlConstants.DOUBLE_TEST_TOL );
assertEquals(c.distance(b), param[1], GrlConstants.DOUBLE_TEST_TOL);
}
@Test
public void closestPoint_point() {
Point3D_F64 a = new Point3D_F64( 1, 1, 1 );
Point3D_F64 b = new Point3D_F64( 1.5, -2.5, 9 );
Point3D_F64 c = new Point3D_F64( 10.1, 6, -3 );
Vector3D_F64 va = new Vector3D_F64( a, b );
LineParametric3D_F64 lineA = new LineParametric3D_F64( a, va );
Point3D_F64 foundB = ClosestPoint3D_F64.closestPoint(lineA, c, null);
Vector3D_F64 p = new Vector3D_F64( foundB, c );
// see if they are perpendicular and therefor c foundB is the closest point
double d = p.dot( va );
assertEquals( 0, d, GrlConstants.DOUBLE_TEST_TOL );
}
@Test
public void closestPoint_point_d() {
Point3D_F64 a = new Point3D_F64( 1, 1, 1 );
Point3D_F64 b = new Point3D_F64( 1.5, -2.5, 9 );
Point3D_F64 c = new Point3D_F64( 10.1, 6, -3 );
Vector3D_F64 va = new Vector3D_F64( a, b );
LineParametric3D_F64 lineA = new LineParametric3D_F64( a, va );
double d = ClosestPoint3D_F64.closestPoint(lineA, c);
Point3D_F64 pt = new Point3D_F64();
pt.x = a.x + va.x*d;
pt.y = a.y + va.y*d;
pt.z = a.z + va.z*d;
Vector3D_F64 p = new Vector3D_F64( pt, c );
// see if they are perpendicular and therefor c foundB is the closest point
assertEquals( 0, p.dot(va), GrlConstants.DOUBLE_TEST_TOL );
}
@Test
public void closestPoint_planeNorm_point() {
Point3D_F64 found;
PlaneNormal3D_F64 n = new PlaneNormal3D_F64(3,4,-5,3,4,-5);
found = ClosestPoint3D_F64.closestPoint(n,new Point3D_F64(0,0,0),null);
assertEquals(3,found.x, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(4,found.y, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(-5,found.z, GrlConstants.DOUBLE_TEST_TOL);
// move it closer, but the point shouldn't change
Vector3D_F64 v = n.n;
v.normalize();
found = ClosestPoint3D_F64.closestPoint(n,new Point3D_F64(v.x,v.y,v.z),null);
assertEquals(3,found.x, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(4,found.y, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(-5,found.z, GrlConstants.DOUBLE_TEST_TOL);
// other side of normal
found = ClosestPoint3D_F64.closestPoint(n,new Point3D_F64(3+v.x,4+v.y,v.z-5),null);
assertEquals(3,found.x, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(4,found.y, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(-5,found.z, GrlConstants.DOUBLE_TEST_TOL);
}
@Test
public void closestPoint_planeGen_point() {
Point3D_F64 found;
PlaneNormal3D_F64 n = new PlaneNormal3D_F64(3,4,-5,3,4,-5);
PlaneGeneral3D_F64 g = UtilPlane3D_F64.convert(n, null);
found = ClosestPoint3D_F64.closestPoint(g,new Point3D_F64(0,0,0),null);
assertEquals(3,found.x, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(4,found.y, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(-5,found.z, GrlConstants.DOUBLE_TEST_TOL);
// move it closer, but the point shouldn't change
Vector3D_F64 v = n.n;
v.normalize();
found = ClosestPoint3D_F64.closestPoint(g,new Point3D_F64(v.x,v.y,v.z),null);
assertEquals(3,found.x, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(4,found.y, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(-5,found.z, GrlConstants.DOUBLE_TEST_TOL);
// other side of normal
found = ClosestPoint3D_F64.closestPoint(g,new Point3D_F64(3+v.x,4+v.y,v.z-5),null);
assertEquals(3,found.x, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(4,found.y, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(-5,found.z, GrlConstants.DOUBLE_TEST_TOL);
}
@Test
public void closestPointOrigin() {
PlaneGeneral3D_F64 g = new PlaneGeneral3D_F64(1,2,3,4);
Point3D_F64 expected = ClosestPoint3D_F64.closestPoint(g,new Point3D_F64(0,0,0),null);
Point3D_F64 found = ClosestPoint3D_F64.closestPointOrigin(g,null);
assertEquals(expected.x,found.x, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(expected.y,found.y, GrlConstants.DOUBLE_TEST_TOL);
assertEquals(expected.z,found.z, GrlConstants.DOUBLE_TEST_TOL);
}
@Test
public void closestPoint_lineSeg_pt() {
// closest point is on the line
LineSegment3D_F64 lineA = new LineSegment3D_F64(2,3,4,7,8,9);
checkIsClosest(lineA,new Point3D_F64(2,3.5,3.5));
// closest point is past a
checkIsClosest(lineA, new Point3D_F64(1, 1.95, 3));
// closest point is past b
checkIsClosest(lineA, new Point3D_F64(8, 9, 10.1));
// this was a bug
lineA = new LineSegment3D_F64(0,0,0,0,2,0);
checkIsClosest(lineA, new Point3D_F64(0, 1.5, 0));
}
@Test
public void closestPoint_lineSeg_lineSeg() {
Point3D_F64 found;
LineSegment3D_F64 lineA = new LineSegment3D_F64(2,3,4,7,8,9);
LineSegment3D_F64 lineB = new LineSegment3D_F64(2,3,6,-3,8,1);
// intersects in the middle
found = ClosestPoint3D_F64.closestPoint(lineA, lineB, null);
checkIsClosest(found,lineA,lineB);
lineA.set(-10,0,0,10,0,0);
// misses start of lineA
lineB.set(-100,0,20,-100,0,-20);
found = ClosestPoint3D_F64.closestPoint(lineA, lineB, null);
checkIsClosest(found, lineA, lineB);
// misses end of lineA
lineB.set(100,0,20,100,0,-20);
found = ClosestPoint3D_F64.closestPoint(lineA, lineB, null);
checkIsClosest(found, lineA, lineB);
// same but for line B
checkIsClosest(found, lineB, lineA);
lineB.set(-100,0,20,-100,0,-20);
found = ClosestPoint3D_F64.closestPoint(lineA, lineB, null);
checkIsClosest(found, lineB, lineA);
}
@Test
public void closestPoint_triangle_point() {
Point3D_F64 P0 = new Point3D_F64(0,0,0);
Point3D_F64 P1 = new Point3D_F64(0,2,0);
Point3D_F64 P2 = new Point3D_F64(1,1,0);
Point3D_F64 P = new Point3D_F64(0.2,0.5,2);
Point3D_F64 found = new Point3D_F64();
ClosestPoint3D_F64.closestPoint(P0,P1,P2,P,found);
assertEquals(0.2,found.x,GrlConstants.DOUBLE_TEST_TOL);
assertEquals(0.5,found.y,GrlConstants.DOUBLE_TEST_TOL);
assertEquals(0,found.z,GrlConstants.DOUBLE_TEST_TOL);
}
@Test
public void closestPointT_line_plane() {
LineParametric3D_F64 l = new LineParametric3D_F64(2,3,4,0,2,0);
PlaneNormal3D_F64 above = new PlaneNormal3D_F64(5,6,7,0,1,0);
PlaneNormal3D_F64 below = new PlaneNormal3D_F64(5,-1,7,0,1,0);
double t_above = ClosestPoint3D_F64.closestPointT(l,above);
double t_below = ClosestPoint3D_F64.closestPointT(l,below);
assertEquals(1.5,t_above,GrlConstants.DOUBLE_TEST_TOL);
assertEquals(-2.0,t_below,GrlConstants.DOUBLE_TEST_TOL);
}
private void checkIsClosest( LineSegment3D_F64 line , Point3D_F64 target ) {
Point3D_F64 pointOnLine = ClosestPoint3D_F64.closestPoint(line,target,null);
LineParametric3D_F64 para = UtilLine3D_F64.convert(line,null);
double t = UtilLine3D_F64.computeT(para,pointOnLine);
double t0 = t - Math.sqrt(GrlConstants.DOUBLE_TEST_TOL);
double t1 = t + Math.sqrt(GrlConstants.DOUBLE_TEST_TOL);
if( t0 < 0 ) t0 = 0;
if( t1 > 1 ) t1 = 1;
Point3D_F64 work0 = para.getPointOnLine(t0);
Point3D_F64 work1 = para.getPointOnLine(t1);
double dist = pointOnLine.distance(target);
double dist0 = work0.distance(target);
double dist1 = work1.distance(target);
assertTrue( dist <= dist0 );
assertTrue( dist <= dist1 );
}
private void checkIsClosest( Point3D_F64 pt , LineSegment3D_F64 lineA , LineSegment3D_F64 lineB ) {
double found = Distance3D_F64.distance(lineA,pt)+Distance3D_F64.distance(lineB,pt);
Point3D_F64 work = pt.copy();
for( int i = 0; i < 3; i++ ) {
double orig = work.getIndex(i);
work.setIndex(i, orig + Math.sqrt(GrlConstants.DOUBLE_TEST_TOL));
double d = Distance3D_F64.distance(lineA,work)+Distance3D_F64.distance(lineB,work);
assertTrue(found+" "+d,found < d+10*GrlConstants.DOUBLE_TEST_TOL);
work.setIndex(i, orig - Math.sqrt(GrlConstants.DOUBLE_TEST_TOL));
d = Distance3D_F64.distance(lineA,work)+Distance3D_F64.distance(lineB,work);
assertTrue(found + " " + d, found <=d+10*GrlConstants.DOUBLE_TEST_TOL);
work.setIndex(i,orig);
}
}
private void checkIsClosest( Point3D_F64 pt , LineParametric3D_F64 lineA , LineParametric3D_F64 lineB ) {
double found = Distance3D_F64.distance(lineA,pt)+Distance3D_F64.distance(lineB,pt);
Point3D_F64 work = pt.copy();
for( int i = 0; i < 3; i++ ) {
double orig = work.getIndex(i);
work.setIndex(i,orig + Math.sqrt(GrlConstants.DOUBLE_TEST_TOL));
double d = Distance3D_F64.distance(lineA,work)+Distance3D_F64.distance(lineB,work);
assertTrue(found+" "+d,found <= d+10*GrlConstants.DOUBLE_TEST_TOL);
work.setIndex(i,orig - Math.sqrt(GrlConstants.DOUBLE_TEST_TOL));
d = Distance3D_F64.distance(lineA,work)+Distance3D_F64.distance(lineB,work);
assertTrue(found+" "+d,found <= d+10*GrlConstants.DOUBLE_TEST_TOL);
work.setIndex(i,orig);
}
}
}