/* * Copyright (C) 2011-2016, 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.metric.Intersection2D_F32; import georegression.struct.point.Point2D_F32; import georegression.struct.shapes.Polygon2D_F32; import georegression.struct.shapes.Quadrilateral_F32; import georegression.struct.shapes.Rectangle2D_F32; import georegression.struct.shapes.RectangleLength2D_I32; import org.junit.Test; import java.util.ArrayList; import java.util.List; import java.util.Random; import static georegression.misc.GrlConstants.FLOAT_TEST_TOL; import static org.junit.Assert.*; /** * @author Peter Abeles */ public class TestUtilPolygons2D_F32 { Random rand = new Random(234); @Test public void isConvex() { Polygon2D_F32 a = new Polygon2D_F32(0, 0, 5, 5, -5, 5); assertTrue(UtilPolygons2D_F32.isConvex(a)); a.flip(); assertTrue(UtilPolygons2D_F32.isConvex(a)); Polygon2D_F32 b = new Polygon2D_F32(0, 0, 0, 5, -5, 5, -5, 0); assertTrue(UtilPolygons2D_F32.isConvex(b)); b.flip(); assertTrue(UtilPolygons2D_F32.isConvex(b)); Polygon2D_F32 c = new Polygon2D_F32(0, 0, 0, 5, -5, 5, -0.1f, 4.5f); assertFalse(UtilPolygons2D_F32.isConvex(c)); c.flip(); assertFalse(UtilPolygons2D_F32.isConvex(c)); } @Test public void convert_rectcorner_quad() { Rectangle2D_F32 r = new Rectangle2D_F32(1, 2, 5, 6); Quadrilateral_F32 q = new Quadrilateral_F32(); UtilPolygons2D_F32.convert(r, q); assertEquals(1, q.a.x, FLOAT_TEST_TOL); assertEquals(2, q.a.y, FLOAT_TEST_TOL); assertEquals(5, q.b.x, FLOAT_TEST_TOL); assertEquals(2, q.b.y, FLOAT_TEST_TOL); assertEquals(5, q.c.x, FLOAT_TEST_TOL); assertEquals(6, q.c.y, FLOAT_TEST_TOL); assertEquals(1, q.d.x, FLOAT_TEST_TOL); assertEquals(6, q.d.y, FLOAT_TEST_TOL); } @Test public void convert_rect_poly() { Rectangle2D_F32 r = new Rectangle2D_F32(1,2,5,6); Polygon2D_F32 p = new Polygon2D_F32(4); UtilPolygons2D_F32.convert(r, p); assertEquals(1, p.get(0).x, FLOAT_TEST_TOL); assertEquals(2, p.get(0).y, FLOAT_TEST_TOL); assertEquals(5, p.get(1).x, FLOAT_TEST_TOL); assertEquals(2, p.get(1).y, FLOAT_TEST_TOL); assertEquals(5, p.get(2).x, FLOAT_TEST_TOL); assertEquals(6, p.get(2).y, FLOAT_TEST_TOL); assertEquals(1, p.get(3).x, FLOAT_TEST_TOL); assertEquals(6, p.get(3).y, FLOAT_TEST_TOL); } @Test public void convert_quad_poly() { Quadrilateral_F32 q = new Quadrilateral_F32(1,2,3,4,5,6,7,8); Polygon2D_F32 p = new Polygon2D_F32(4); UtilPolygons2D_F32.convert(q, p); assertTrue(p.get(0).distance(q.a) < FLOAT_TEST_TOL); assertTrue(p.get(1).distance(q.b) < FLOAT_TEST_TOL); assertTrue(p.get(2).distance(q.c) < FLOAT_TEST_TOL); assertTrue(p.get(3).distance(q.d) < FLOAT_TEST_TOL); } @Test public void convert_poly_quad() { Polygon2D_F32 r = new Polygon2D_F32(1,2, 5,2, 5,6, 1,6); Quadrilateral_F32 q = new Quadrilateral_F32(); UtilPolygons2D_F32.convert(r, q); assertTrue(r.get(0).distance(q.a)< FLOAT_TEST_TOL); assertTrue(r.get(1).distance(q.b)< FLOAT_TEST_TOL); assertTrue(r.get(2).distance(q.c)< FLOAT_TEST_TOL); assertTrue(r.get(3).distance(q.d)< FLOAT_TEST_TOL); } @Test public void convert_rectwh_quad() { RectangleLength2D_I32 rect = new RectangleLength2D_I32(1, 2, 5, 6); Quadrilateral_F32 q = new Quadrilateral_F32(); UtilPolygons2D_F32.convert(rect, q); assertEquals(1, q.a.x, FLOAT_TEST_TOL); assertEquals(2, q.a.y, FLOAT_TEST_TOL); assertEquals(5, q.b.x, FLOAT_TEST_TOL); assertEquals(2, q.b.y, FLOAT_TEST_TOL); assertEquals(5, q.c.x, FLOAT_TEST_TOL); assertEquals(7, q.c.y, FLOAT_TEST_TOL); assertEquals(1, q.d.x, FLOAT_TEST_TOL); assertEquals(7, q.d.y, FLOAT_TEST_TOL); } @Test public void bounding_quadrilateral() { Quadrilateral_F32 q = new Quadrilateral_F32(3, 0, 2, -3, -2, 3, 1, 5); Rectangle2D_F32 out = new Rectangle2D_F32(); UtilPolygons2D_F32.bounding(q, out); assertEquals(-2, out.p0.x, FLOAT_TEST_TOL); assertEquals(-3, out.p0.y, FLOAT_TEST_TOL); assertEquals(3, out.p1.x, FLOAT_TEST_TOL); assertEquals(5, out.p1.y, FLOAT_TEST_TOL); } @Test public void center_quadrilateral() { Quadrilateral_F32 q = new Quadrilateral_F32(3, 0, 2, -3, -2, 3, 1, 5); Point2D_F32 pts[] = new Point2D_F32[]{q.a, q.b, q.c, q.d}; Point2D_F32 expected = new Point2D_F32(); for (int i = 0; i < pts.length; i++) { expected.x += pts[i].x; expected.y += pts[i].y; } expected.x /= 4.0f; expected.y /= 4.0f; Point2D_F32 found = UtilPolygons2D_F32.center(q, null); assertEquals(expected.x, found.x, FLOAT_TEST_TOL); assertEquals(expected.y, found.y, FLOAT_TEST_TOL); } @Test public void isCCW() { // check convex case List<Point2D_F32> list = new ArrayList<Point2D_F32>(); list.add(new Point2D_F32(1, 1)); list.add(new Point2D_F32(2, 1)); list.add(new Point2D_F32(2, 2)); assertTrue(UtilPolygons2D_F32.isCCW(list)); assertFalse(UtilPolygons2D_F32.isCCW(reverse(list))); // check concave case list.add(new Point2D_F32(1, 2)); list.add(new Point2D_F32(1.5f, 1.5f)); assertTrue(UtilPolygons2D_F32.isCCW(list)); assertFalse(UtilPolygons2D_F32.isCCW(reverse(list))); } @Test public void vertexAverage() { Polygon2D_F32 poly = new Polygon2D_F32(1, 2, 3, 4, 5, 6); Point2D_F32 ave = new Point2D_F32(); UtilPolygons2D_F32.vertexAverage(poly, ave); assertEquals((1 + 3 + 5) / 3.0f, ave.x, FLOAT_TEST_TOL); assertEquals((2 + 4 + 6) / 3.0f, ave.y, FLOAT_TEST_TOL); } private static List<Point2D_F32> reverse(List<Point2D_F32> points) { List<Point2D_F32> reverse = new ArrayList<Point2D_F32>(); for (int i = points.size() - 1; i >= 0; i--) { reverse.add(points.get(i)); } return reverse; } @Test public void isIdentical_poly_poly() { Polygon2D_F32 poly1 = new Polygon2D_F32(1, 2, 3, 4, 5, 6); Polygon2D_F32 poly2 = new Polygon2D_F32(1, 2, 3, 4, 5, 6.1f); assertTrue(UtilPolygons2D_F32.isIdentical(poly1, poly2, 0.11f)); assertFalse(UtilPolygons2D_F32.isIdentical(poly1, poly2, 0.09f)); } @Test public void isEquivalent_poly_poly() { Polygon2D_F32 poly1 = new Polygon2D_F32(1, 2, 3, 4, 5, 6); Polygon2D_F32 poly2 = new Polygon2D_F32(1, 2, 3, 4, 5, 6); // create a shifted version of poly2 for (int i = 0; i < poly1.size(); i++) { Polygon2D_F32 poly3 = new Polygon2D_F32(poly1.size()); for (int j = 0; j < poly1.size(); j++) { poly3.vertexes.data[j] = poly2.vertexes.data[(j+i)%poly1.size()]; } assertTrue(UtilPolygons2D_F32.isEquivalent(poly1,poly3, FLOAT_TEST_TOL)); } } @Test public void flip() { // less than 3 has undrfined behavior Polygon2D_F32 poly = new Polygon2D_F32(3); List<Point2D_F32> orig = new ArrayList<Point2D_F32>(); orig.addAll(poly.vertexes.toList()); UtilPolygons2D_F32.flip(poly); assertTrue(orig.get(0)==poly.get(0)); assertTrue(orig.get(1)==poly.get(2)); assertTrue(orig.get(2)==poly.get(1)); poly = new Polygon2D_F32(4); orig.clear(); orig.addAll(poly.vertexes.toList()); UtilPolygons2D_F32.flip(poly); assertTrue(orig.get(0)==poly.get(0)); assertTrue(orig.get(1)==poly.get(3)); assertTrue(orig.get(2)==poly.get(2)); assertTrue(orig.get(3)==poly.get(1)); poly = new Polygon2D_F32(5); orig.clear(); orig.addAll(poly.vertexes.toList()); UtilPolygons2D_F32.flip(poly); assertTrue(orig.get(0)==poly.get(0)); assertTrue(orig.get(1)==poly.get(4)); assertTrue(orig.get(2)==poly.get(3)); assertTrue(orig.get(3)==poly.get(2)); assertTrue(orig.get(4)==poly.get(1)); } @Test public void shiftUp() { for (int i = 1; i <= 5; i++) { Polygon2D_F32 poly = new Polygon2D_F32(i); List<Point2D_F32> orig = new ArrayList<Point2D_F32>(); orig.addAll(poly.vertexes.toList()); UtilPolygons2D_F32.shiftUp(poly); for (int j = 0; j < i-1; j++) { assertTrue(orig.get(j+1) == poly.get(j)); } assertTrue(orig.get(0) == poly.get(i-1)); } } @Test public void shiftDown() { for (int i = 1; i <= 5; i++) { Polygon2D_F32 poly = new Polygon2D_F32(i); List<Point2D_F32> orig = new ArrayList<Point2D_F32>(); orig.addAll(poly.vertexes.toList()); UtilPolygons2D_F32.shiftDown(poly); for (int j = 1; j < i; j++) { assertTrue(orig.get(j-1) == poly.get(j)); } assertTrue(orig.get(i-1) == poly.get(0)); } } /** * Simple and not exhaustive test of convex hull. The low level algorithm class has a more * detailed implementation. */ @Test public void convexHull() { Polygon2D_F32 output = new Polygon2D_F32(); for (int numPoints = 10; numPoints < 20; numPoints++) { List<Point2D_F32> data = new ArrayList<Point2D_F32>(); for (int i = 0; i < numPoints; i++) { float x = (float)rand.nextGaussian()*5; float y = (float)rand.nextGaussian()*5; data.add( new Point2D_F32(x,y) ); } UtilPolygons2D_F32.convexHull(data, output); // check some of the properties of the convex hull assertTrue(output.size() <= numPoints); assertTrue(output.isConvex()); for (int i = 0; i < numPoints; i++) { Intersection2D_F32.containConvex(output, data.get(i)); } } } @Test public void removeAlmostParallel() { Polygon2D_F32 output = new Polygon2D_F32(5); output.get(0).set(0,0); output.get(1).set(2,0); output.get(2).set(4,0); output.get(3).set(4,5); output.get(4).set(0,5); UtilPolygons2D_F32.removeAlmostParallel(output, FLOAT_TEST_TOL); assertEquals(4, output.size()); assertEquals(0, output.get(0).x, FLOAT_TEST_TOL); assertEquals(4, output.get(1).x, FLOAT_TEST_TOL); } }