/* * 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.geometry; import georegression.misc.GrlConstants; import georegression.struct.plane.PlaneGeneral3D_F32; import georegression.struct.plane.PlaneNormal3D_F32; import georegression.struct.plane.PlaneTangent3D_F32; import georegression.struct.point.Point2D_F32; import georegression.struct.point.Point3D_F32; import georegression.struct.point.Vector3D_F32; import georegression.struct.se.Se3_F32; import georegression.transform.se.SePointOps_F32; import org.junit.Test; import java.util.ArrayList; import java.util.List; import java.util.Random; import static org.junit.Assert.*; /** * @author Peter Abeles */ public class TestUtilPlane3D_F32 { Random rand = new Random(234); @Test public void convert_norm_general() { PlaneNormal3D_F32 original = new PlaneNormal3D_F32(); original.n.set(1,2,3); original.n.normalize(); original.p.set(-2,3,5); PlaneGeneral3D_F32 test = UtilPlane3D_F32.convert(original,null); List<Point3D_F32> points = randPointOnPlane(original,10); for( Point3D_F32 p : points ) { float found = UtilPlane3D_F32.evaluate(test,p); assertEquals(0,found, GrlConstants.FLOAT_TEST_TOL); } } @Test public void convert_general_norm() { PlaneGeneral3D_F32 general = new PlaneGeneral3D_F32(1,2,3,4); PlaneNormal3D_F32 foundPlane = UtilPlane3D_F32.convert(general,null); List<Point3D_F32> points = randPointOnPlane(foundPlane,10); for( Point3D_F32 p : points ) { float found = UtilPlane3D_F32.evaluate(general,p); assertEquals(0,found, GrlConstants.FLOAT_TEST_TOL); } } @Test public void convert_tangent_norm() { PlaneNormal3D_F32 original = new PlaneNormal3D_F32(); original.n.set(1,0,0); original.n.normalize(); original.p.set(-2,3,5); // create a bunch of points which are on the original plane List<Point3D_F32> points = randPointOnPlane(original,10); // now manually construct the plane in tangent form PlaneTangent3D_F32 tangent = new PlaneTangent3D_F32(-2,0,0); // convert this back into normal form PlaneNormal3D_F32 conv = UtilPlane3D_F32.convert(tangent,(PlaneNormal3D_F32)null); // the points should still be on the plane for( Point3D_F32 p : points ) { float found = UtilPlane3D_F32.evaluate(conv,p); assertEquals(0,found, GrlConstants.FLOAT_TEST_TOL); } } @Test public void hessianNormalForm() { PlaneGeneral3D_F32 a = new PlaneGeneral3D_F32(2,-3,4,5); float n = (float)Math.sqrt(2*2 + 3*3 + 4*4); UtilPlane3D_F32.hessianNormalForm(a); assertEquals(2/n,a.A, GrlConstants.FLOAT_TEST_TOL); assertEquals(-3/n,a.B, GrlConstants.FLOAT_TEST_TOL); assertEquals(4/n,a.C, GrlConstants.FLOAT_TEST_TOL); assertEquals(5/n,a.D, GrlConstants.FLOAT_TEST_TOL); } @Test public void evaluate_general() { PlaneNormal3D_F32 original = new PlaneNormal3D_F32(); original.n.set(1,2,3); original.n.normalize(); original.p.set(-2,3,5); PlaneGeneral3D_F32 test = UtilPlane3D_F32.convert(original,null); List<Point3D_F32> points = randPointOnPlane(original,10); for( Point3D_F32 p : points ) { float found = UtilPlane3D_F32.evaluate(test,p); assertEquals(0,found, GrlConstants.FLOAT_TEST_TOL); } } @Test public void evaluate_normal() { PlaneNormal3D_F32 input = new PlaneNormal3D_F32(); input.n.set(1,2,3); input.n.normalize(); input.p.set(-2,3,5); List<Point3D_F32> points = randPointOnPlane(input, 10); for( Point3D_F32 p : points ) { float found = UtilPlane3D_F32.evaluate(input,p); assertEquals(0,found, GrlConstants.FLOAT_TEST_TOL); } } @Test public void equals_planeNorm() { for( int i = 0; i < 100; i++ ) { PlaneNormal3D_F32 a = new PlaneNormal3D_F32( (float)rand.nextGaussian(),(float)rand.nextGaussian(),(float)rand.nextGaussian(), (float)rand.nextGaussian(),(float)rand.nextGaussian(),(float)rand.nextGaussian()); PlaneNormal3D_F32 b = new PlaneNormal3D_F32(a); b.p.x +=(rand.nextFloat()-0.5f)*GrlConstants.FLOAT_TEST_TOL; b.p.y +=(rand.nextFloat()-0.5f)*GrlConstants.FLOAT_TEST_TOL; b.p.z +=(rand.nextFloat()-0.5f)*GrlConstants.FLOAT_TEST_TOL; b.n.x +=(rand.nextFloat()-0.5f)*GrlConstants.FLOAT_TEST_TOL; b.n.y +=(rand.nextFloat()-0.5f)*GrlConstants.FLOAT_TEST_TOL; b.n.z +=(rand.nextFloat()-0.5f)*GrlConstants.FLOAT_TEST_TOL; // change scaling float scale = (float)rand.nextGaussian()*2; b.n.x *= scale; b.n.y *= scale; b.n.z *= scale; assertTrue(UtilPlane3D_F32.equals(a, b, GrlConstants.FLOAT_TEST_TOL*50)); b.p.x +=(rand.nextFloat()-0.5f); b.p.y +=(rand.nextFloat()-0.5f); b.p.z +=(rand.nextFloat()-0.5f); b.n.x +=(rand.nextFloat()-0.5f); b.n.y +=(rand.nextFloat()-0.5f); b.n.z +=(rand.nextFloat()-0.5f); assertFalse(UtilPlane3D_F32.equals(a, b, GrlConstants.FLOAT_TEST_TOL*50)); } } /** * Randomly generate points on a plane by randomly selecting two vectors on the plane using cross products */ private List<Point3D_F32> randPointOnPlane( PlaneNormal3D_F32 plane , int N ) { Vector3D_F32 v = new Vector3D_F32(-2,0,1); Vector3D_F32 a = UtilTrig_F32.cross(plane.n,v); a.normalize(); Vector3D_F32 b = UtilTrig_F32.cross(plane.n,a); b.normalize(); List<Point3D_F32> ret = new ArrayList<Point3D_F32>(); for( int i = 0; i < N; i++ ) { float v0 = (float)rand.nextGaussian(); float v1 = (float)rand.nextGaussian(); Point3D_F32 p = new Point3D_F32(); p.x = plane.p.x + v0*a.x + v1*b.x; p.y = plane.p.y + v0*a.y + v1*b.y; p.z = plane.p.z + v0*a.z + v1*b.z; ret.add(p); } return ret; } /** * Tests the planeToWorld function by randomly generating 2D points and converting them to 3D. Then it * tests to see if the points lie on the plane. */ @Test public void planeToWorld() { checkPlaneToWorld(new PlaneNormal3D_F32(1,2,3,0,0,1)); checkPlaneToWorld(new PlaneNormal3D_F32(1,2,3,0,1,0)); checkPlaneToWorld(new PlaneNormal3D_F32(1,2,3,1,0,0)); checkPlaneToWorld(new PlaneNormal3D_F32(1,2,3,-2,-4,0.5f)); } private void checkPlaneToWorld(PlaneNormal3D_F32 planeN) { planeN.getN().normalize(); PlaneGeneral3D_F32 planeG = UtilPlane3D_F32.convert(planeN, null); List<Point2D_F32> points2D = UtilPoint2D_F32.random(-2, 2, 100, rand); Se3_F32 planeToWorld = UtilPlane3D_F32.planeToWorld(planeG,null); Point3D_F32 p3 = new Point3D_F32(); Point3D_F32 l3 = new Point3D_F32(); Point3D_F32 k3 = new Point3D_F32(); for( Point2D_F32 p : points2D ) { p3.set(p.x,p.y,0); SePointOps_F32.transform(planeToWorld, p3, l3); // see if it created a valid transform SePointOps_F32.transformReverse(planeToWorld,l3,k3); assertEquals(0,k3.distance(p3), GrlConstants.FLOAT_TEST_TOL ); assertEquals(0,UtilPlane3D_F32.evaluate(planeG,l3), GrlConstants.FLOAT_TEST_TOL); } } }