| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.commons.geometry.spherical.twod; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.stream.Collectors; |
| |
| import org.apache.commons.numbers.angle.PlaneAngleRadians; |
| import org.apache.commons.geometry.core.GeometryTestUtils; |
| import org.apache.commons.geometry.core.RegionLocation; |
| import org.apache.commons.geometry.core.partitioning.Split; |
| import org.apache.commons.geometry.core.partitioning.SplitLocation; |
| import org.apache.commons.geometry.core.precision.DoublePrecisionContext; |
| import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext; |
| import org.apache.commons.geometry.euclidean.threed.Vector3D; |
| import org.apache.commons.geometry.spherical.SphericalTestUtils; |
| import org.apache.commons.geometry.spherical.oned.Point1S; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| public class ConvexArea2STest { |
| |
| private static final double TEST_EPS = 1e-10; |
| |
| private static final DoublePrecisionContext TEST_PRECISION = |
| new EpsilonDoublePrecisionContext(TEST_EPS); |
| |
| // epsilon value for use when comparing computed barycenter locations; |
| // this must currently be set much higher than the other epsilon |
| private static final double BARYCENTER_EPS = 1e-2; |
| |
| @Test |
| public void testFull() { |
| // act |
| ConvexArea2S area = ConvexArea2S.full(); |
| |
| // assert |
| Assert.assertTrue(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(0, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(4 * PlaneAngleRadians.PI, area.getSize(), TEST_EPS); |
| Assert.assertNull(area.getBarycenter()); |
| |
| Assert.assertEquals(0, area.getBoundaries().size()); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.PLUS_I, Point2S.MINUS_I, |
| Point2S.PLUS_J, Point2S.MINUS_J, |
| Point2S.PLUS_K, Point2S.MINUS_K); |
| } |
| |
| @Test |
| public void testFromBounds_empty() { |
| // act |
| ConvexArea2S area = ConvexArea2S.fromBounds(); |
| |
| // assert |
| Assert.assertTrue(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(0, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(4 * PlaneAngleRadians.PI, area.getSize(), TEST_EPS); |
| Assert.assertNull(area.getBarycenter()); |
| |
| Assert.assertEquals(0, area.getBoundaries().size()); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.PLUS_I, Point2S.MINUS_I, |
| Point2S.PLUS_J, Point2S.MINUS_J, |
| Point2S.PLUS_K, Point2S.MINUS_K); |
| } |
| |
| @Test |
| public void testFromBounds_singleBound() { |
| // arrange |
| GreatCircle circle = GreatCircle.fromPoints(Point2S.PLUS_K, Point2S.PLUS_I, TEST_PRECISION); |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromBounds(circle); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(2 * PlaneAngleRadians.PI, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(2 * PlaneAngleRadians.PI, area.getSize(), TEST_EPS); |
| SphericalTestUtils.assertPointsEq(Point2S.PLUS_J, area.getBarycenter(), TEST_EPS); |
| checkBarycenterConsistency(area); |
| |
| Assert.assertEquals(1, area.getBoundaries().size()); |
| GreatArc arc = area.getBoundaries().get(0); |
| Assert.assertTrue(arc.isFull()); |
| SphericalTestUtils.assertPointsEq(Point2S.PLUS_J, arc.getCircle().getPolePoint(), TEST_EPS); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, Point2S.PLUS_J); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| Point2S.PLUS_I, Point2S.MINUS_I, |
| Point2S.PLUS_K, Point2S.MINUS_K); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, Point2S.MINUS_J); |
| } |
| |
| @Test |
| public void testFromBounds_lune_intersectionAtPoles() { |
| // arrange |
| GreatCircle a = GreatCircle.fromPoints(Point2S.PLUS_K, Point2S.PLUS_I, TEST_PRECISION); |
| GreatCircle b = GreatCircle.fromPoints( |
| Point2S.of(0.25 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO), Point2S.PLUS_K, TEST_PRECISION); |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromBounds(a, b); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(2 * PlaneAngleRadians.PI, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, area.getSize(), TEST_EPS); |
| SphericalTestUtils.assertPointsEq(Point2S.of(0.125 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO), area.getBarycenter(), TEST_EPS); |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(2, arcs.size()); |
| checkArc(arcs.get(0), Point2S.PLUS_K, Point2S.MINUS_K); |
| checkArc(arcs.get(1), Point2S.MINUS_K, Point2S.PLUS_K); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.of(0.125 * PlaneAngleRadians.PI, 0.1), |
| Point2S.of(0.125 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO), |
| Point2S.of(0.125 * PlaneAngleRadians.PI, PlaneAngleRadians.PI - 0.1)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| Point2S.PLUS_I, Point2S.of(0.25 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO), |
| Point2S.PLUS_K, Point2S.MINUS_K); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.PLUS_J, Point2S.MINUS_J); |
| } |
| |
| @Test |
| public void testFromBounds_lune_intersectionAtEquator() { |
| // arrange |
| GreatCircle a = GreatCircle.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION); |
| GreatCircle b = GreatCircle.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION); |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromBounds(a, b); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(2 * PlaneAngleRadians.PI, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI, area.getSize(), TEST_EPS); |
| SphericalTestUtils.assertPointsEq(Point2S.of(0, 0.25 * PlaneAngleRadians.PI), area.getBarycenter(), TEST_EPS); |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(2, arcs.size()); |
| checkArc(arcs.get(0), Point2S.PLUS_J, Point2S.MINUS_J); |
| checkArc(arcs.get(1), Point2S.MINUS_J, Point2S.PLUS_J); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.of(0, 0.25 * PlaneAngleRadians.PI), |
| Point2S.of(0.25, 0.4 * PlaneAngleRadians.PI), |
| Point2S.of(-0.25, 0.4 * PlaneAngleRadians.PI)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| Point2S.PLUS_I, Point2S.PLUS_K, |
| Point2S.PLUS_J, Point2S.MINUS_J); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.MINUS_I, Point2S.MINUS_K, |
| Point2S.of(PlaneAngleRadians.PI, 0.25 * PlaneAngleRadians.PI), |
| Point2S.of(PlaneAngleRadians.PI, 0.75 * PlaneAngleRadians.PI)); |
| } |
| |
| @Test |
| public void testFromBounds_triangle_large() { |
| // arrange |
| GreatCircle a = GreatCircle.fromPole(Vector3D.Unit.PLUS_X, TEST_PRECISION); |
| GreatCircle b = GreatCircle.fromPole(Vector3D.Unit.PLUS_Y, TEST_PRECISION); |
| GreatCircle c = GreatCircle.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION); |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromBounds(Arrays.asList(a, b, c)); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(1.5 * PlaneAngleRadians.PI, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, area.getSize(), TEST_EPS); |
| |
| Point2S expectedBarycenter = triangleBarycenter(Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K); |
| SphericalTestUtils.assertPointsEq(expectedBarycenter, area.getBarycenter(), TEST_EPS); |
| |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(3, arcs.size()); |
| checkArc(arcs.get(0), Point2S.PLUS_K, Point2S.PLUS_I); |
| checkArc(arcs.get(1), Point2S.PLUS_I, Point2S.PLUS_J); |
| checkArc(arcs.get(2), Point2S.PLUS_J, Point2S.PLUS_K); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.of(0.25 * PlaneAngleRadians.PI, 0.25 * PlaneAngleRadians.PI)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K, |
| Point2S.of(0, 0.25 * PlaneAngleRadians.PI), Point2S.of(PlaneAngleRadians.PI_OVER_TWO, 0.304 * PlaneAngleRadians.PI), |
| Point2S.of(0.25 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.MINUS_I, Point2S.MINUS_J, Point2S.MINUS_K); |
| } |
| |
| @Test |
| public void testFromBounds_triangle_small() { |
| // arrange |
| double azMin = 1.125 * PlaneAngleRadians.PI; |
| double azMax = 1.375 * PlaneAngleRadians.PI; |
| double azMid = 0.5 * (azMin + azMax); |
| double polarTop = 0.1; |
| double polarBottom = 0.25 * PlaneAngleRadians.PI; |
| |
| Point2S p1 = Point2S.of(azMin, polarBottom); |
| Point2S p2 = Point2S.of(azMax, polarBottom); |
| Point2S p3 = Point2S.of(azMid, polarTop); |
| |
| GreatCircle a = GreatCircle.fromPoints(p1, p2, TEST_PRECISION); |
| GreatCircle b = GreatCircle.fromPoints(p2, p3, TEST_PRECISION); |
| GreatCircle c = GreatCircle.fromPoints(p3, p1, TEST_PRECISION); |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromBounds(Arrays.asList(a, b, c)); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(p1.distance(p2) + p2.distance(p3) + p3.distance(p1), |
| area.getBoundarySize(), TEST_EPS); |
| double size = PlaneAngleRadians.TWO_PI - a.angle(b) - b.angle(c) - c.angle(a); |
| Assert.assertEquals(size, area.getSize(), TEST_EPS); |
| |
| Point2S expectedBarycenter = triangleBarycenter(p1, p2, p3); |
| SphericalTestUtils.assertPointsEq(expectedBarycenter, area.getBarycenter(), BARYCENTER_EPS); |
| |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(3, arcs.size()); |
| |
| checkArc(arcs.get(0), p3, p1); |
| checkArc(arcs.get(1), p1, p2); |
| checkArc(arcs.get(2), p2, p3); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, Point2S.of(azMid, 0.11)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| p1, p2, p3, p1.slerp(p2, 0.2)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K, |
| Point2S.MINUS_I, Point2S.MINUS_J, Point2S.MINUS_K); |
| } |
| |
| @Test |
| public void testFromBounds_quad() { |
| // arrange |
| Point2S p1 = Point2S.of(0.2, 0.1); |
| Point2S p2 = Point2S.of(0.1, 0.2); |
| Point2S p3 = Point2S.of(0.2, 0.5); |
| Point2S p4 = Point2S.of(0.3, 0.2); |
| |
| GreatCircle c1 = GreatCircle.fromPoints(p1, p2, TEST_PRECISION); |
| GreatCircle c2 = GreatCircle.fromPoints(p2, p3, TEST_PRECISION); |
| GreatCircle c3 = GreatCircle.fromPoints(p3, p4, TEST_PRECISION); |
| GreatCircle c4 = GreatCircle.fromPoints(p4, p1, TEST_PRECISION); |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromBounds(c1, c2, c3, c4); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(p1.distance(p2) + p2.distance(p3) + p3.distance(p4) + p4.distance(p1), |
| area.getBoundarySize(), TEST_EPS); |
| |
| double size = 2 * PlaneAngleRadians.PI - c1.angle(c2) - c2.angle(c3) - c3.angle(c4) - c4.angle(c1); |
| Assert.assertEquals(size, area.getSize(), TEST_EPS); |
| |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(4, arcs.size()); |
| |
| checkArc(arcs.get(0), p1, p2); |
| checkArc(arcs.get(1), p2, p3); |
| checkArc(arcs.get(2), p4, p1); |
| checkArc(arcs.get(3), p3, p4); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, Point2S.of(0.2, 0.11)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| p1, p2, p3, p4, p1.slerp(p2, 0.2)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K, |
| Point2S.MINUS_I, Point2S.MINUS_J, Point2S.MINUS_K); |
| } |
| |
| @Test |
| public void testFromPath_empty() { |
| // act |
| ConvexArea2S area = ConvexArea2S.fromPath(GreatArcPath.empty()); |
| |
| // assert |
| Assert.assertSame(ConvexArea2S.full(), area); |
| } |
| |
| @Test |
| public void testFromPath() { |
| // arrange |
| GreatArcPath path = GreatArcPath.builder(TEST_PRECISION) |
| .append(Point2S.MINUS_I) |
| .append(Point2S.MINUS_K) |
| .append(Point2S.MINUS_J) |
| .close(); |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromPath(path); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(1.5 * PlaneAngleRadians.PI, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, area.getSize(), TEST_EPS); |
| |
| Point2S expectedBarycenter = triangleBarycenter(Point2S.MINUS_I, Point2S.MINUS_K, Point2S.MINUS_J); |
| SphericalTestUtils.assertPointsEq(expectedBarycenter, area.getBarycenter(), TEST_EPS); |
| |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(3, arcs.size()); |
| checkArc(arcs.get(0), Point2S.MINUS_I, Point2S.MINUS_K); |
| checkArc(arcs.get(1), Point2S.MINUS_J, Point2S.MINUS_I); |
| checkArc(arcs.get(2), Point2S.MINUS_K, Point2S.MINUS_J); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.of(1.25 * PlaneAngleRadians.PI, 0.75 * PlaneAngleRadians.PI)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| Point2S.MINUS_I, Point2S.MINUS_J, Point2S.MINUS_K); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K); |
| } |
| |
| @Test |
| public void testFromVertices_empty() { |
| // act |
| ConvexArea2S area = ConvexArea2S.fromVertices(Collections.emptyList(), TEST_PRECISION); |
| |
| // assert |
| Assert.assertSame(ConvexArea2S.full(), area); |
| } |
| |
| @Test |
| public void testFromVertices() { |
| // arrange |
| Point2S p1 = Point2S.PLUS_I; |
| Point2S p2 = Point2S.PLUS_J; |
| Point2S p3 = Point2S.PLUS_K; |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromVertices(Arrays.asList(p1, p2, p3), TEST_PRECISION); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(2 * PlaneAngleRadians.PI, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI, area.getSize(), TEST_EPS); |
| SphericalTestUtils.assertPointsEq(Point2S.of(0, 0.25 * PlaneAngleRadians.PI), area.getBarycenter(), TEST_EPS); |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(2, arcs.size()); |
| checkArc(arcs.get(0), Point2S.PLUS_J, Point2S.MINUS_J); |
| checkArc(arcs.get(1), Point2S.MINUS_J, Point2S.PLUS_J); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.of(-0.25 * PlaneAngleRadians.PI, 0.25 * PlaneAngleRadians.PI), |
| Point2S.of(0, 0.25 * PlaneAngleRadians.PI), |
| Point2S.of(0.25 * PlaneAngleRadians.PI, 0.25 * PlaneAngleRadians.PI)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| Point2S.PLUS_I, Point2S.PLUS_J, |
| Point2S.PLUS_K, Point2S.MINUS_J); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.MINUS_I, Point2S.MINUS_K); |
| } |
| |
| @Test |
| public void testFromVertices_lastVertexRepeated() { |
| // arrange |
| Point2S p1 = Point2S.PLUS_I; |
| Point2S p2 = Point2S.PLUS_J; |
| Point2S p3 = Point2S.PLUS_K; |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromVertices(Arrays.asList(p1, p2, p3, p1), TEST_PRECISION); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(1.5 * PlaneAngleRadians.PI, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, area.getSize(), TEST_EPS); |
| |
| Point2S expectedBarycenter = triangleBarycenter(Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K); |
| SphericalTestUtils.assertPointsEq(expectedBarycenter, area.getBarycenter(), TEST_EPS); |
| |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(3, arcs.size()); |
| checkArc(arcs.get(0), Point2S.PLUS_K, Point2S.PLUS_I); |
| checkArc(arcs.get(1), Point2S.PLUS_I, Point2S.PLUS_J); |
| checkArc(arcs.get(2), Point2S.PLUS_J, Point2S.PLUS_K); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.of(0.25 * PlaneAngleRadians.PI, 0.25 * PlaneAngleRadians.PI)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K, |
| Point2S.of(0, 0.25 * PlaneAngleRadians.PI), Point2S.of(PlaneAngleRadians.PI_OVER_TWO, 0.304 * PlaneAngleRadians.PI), |
| Point2S.of(0.25 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.MINUS_I, Point2S.MINUS_J, Point2S.MINUS_K); |
| } |
| |
| @Test |
| public void testFromVertices_verticesRepeated() { |
| // arrange |
| Point2S p1 = Point2S.PLUS_I; |
| Point2S p2 = Point2S.PLUS_J; |
| Point2S p3 = Point2S.PLUS_K; |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromVertices(Arrays.asList( |
| p1, Point2S.of(1e-17, PlaneAngleRadians.PI_OVER_TWO), p2, p3, p3, p1), true, TEST_PRECISION); |
| |
| // assert |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, area.getSize(), TEST_EPS); |
| |
| Point2S expectedBarycenter = triangleBarycenter(Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K); |
| SphericalTestUtils.assertPointsEq(expectedBarycenter, area.getBarycenter(), TEST_EPS); |
| |
| List<Point2S> vertices = area.getBoundaryPath().getVertices(); |
| Assert.assertEquals(4, vertices.size()); |
| SphericalTestUtils.assertPointsEq(Point2S.PLUS_K, vertices.get(0), TEST_EPS); |
| SphericalTestUtils.assertPointsEq(Point2S.PLUS_I, vertices.get(1), TEST_EPS); |
| SphericalTestUtils.assertPointsEq(Point2S.PLUS_J, vertices.get(2), TEST_EPS); |
| SphericalTestUtils.assertPointsEq(Point2S.PLUS_K, vertices.get(3), TEST_EPS); |
| } |
| |
| @Test |
| public void testFromVertices_invalidArguments() { |
| // act/assert |
| GeometryTestUtils.assertThrows(() -> { |
| ConvexArea2S.fromVertices(Arrays.asList(Point2S.PLUS_I), TEST_PRECISION); |
| }, IllegalStateException.class); |
| |
| GeometryTestUtils.assertThrows(() -> { |
| ConvexArea2S.fromVertices(Arrays.asList(Point2S.PLUS_I, Point2S.of(1e-16, PlaneAngleRadians.PI_OVER_TWO)), TEST_PRECISION); |
| }, IllegalStateException.class); |
| } |
| |
| @Test |
| public void testFromVertexLoop() { |
| // arrange |
| Point2S p1 = Point2S.PLUS_I; |
| Point2S p2 = Point2S.PLUS_J; |
| Point2S p3 = Point2S.PLUS_K; |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromVertexLoop(Arrays.asList(p1, p2, p3), TEST_PRECISION); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(1.5 * PlaneAngleRadians.PI, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, area.getSize(), TEST_EPS); |
| |
| Point2S expectedBarycenter = triangleBarycenter(Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K); |
| SphericalTestUtils.assertPointsEq(expectedBarycenter, area.getBarycenter(), TEST_EPS); |
| |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(3, arcs.size()); |
| checkArc(arcs.get(0), Point2S.PLUS_K, Point2S.PLUS_I); |
| checkArc(arcs.get(1), Point2S.PLUS_I, Point2S.PLUS_J); |
| checkArc(arcs.get(2), Point2S.PLUS_J, Point2S.PLUS_K); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.of(0.25 * PlaneAngleRadians.PI, 0.25 * PlaneAngleRadians.PI)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K, |
| Point2S.of(0, 0.25 * PlaneAngleRadians.PI), Point2S.of(PlaneAngleRadians.PI_OVER_TWO, 0.304 * PlaneAngleRadians.PI), |
| Point2S.of(0.25 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.MINUS_I, Point2S.MINUS_J, Point2S.MINUS_K); |
| } |
| |
| @Test |
| public void testFromVertexLoop_empty() { |
| // act |
| ConvexArea2S area = ConvexArea2S.fromVertexLoop(Collections.emptyList(), TEST_PRECISION); |
| |
| // assert |
| Assert.assertSame(ConvexArea2S.full(), area); |
| } |
| |
| @Test |
| public void testBoundaryStream() { |
| // arrange |
| GreatCircle circle = GreatCircle.fromPole(Vector3D.Unit.PLUS_X, TEST_PRECISION); |
| ConvexArea2S area = ConvexArea2S.fromBounds(circle); |
| |
| // act |
| List<GreatArc> arcs = area.boundaryStream().collect(Collectors.toList()); |
| |
| // assert |
| Assert.assertEquals(1, arcs.size()); |
| Assert.assertSame(circle, arcs.get(0).getCircle()); |
| } |
| |
| @Test |
| public void testBoundaryStream_noBoundaries() { |
| // arrange |
| ConvexArea2S area = ConvexArea2S.full(); |
| |
| // act |
| List<GreatArc> arcs = area.boundaryStream().collect(Collectors.toList()); |
| |
| // assert |
| Assert.assertEquals(0, arcs.size()); |
| } |
| |
| @Test |
| public void testGetInteriorAngles_noAngles() { |
| // act/assert |
| Assert.assertEquals(0, ConvexArea2S.full().getInteriorAngles().length); |
| Assert.assertEquals(0, ConvexArea2S.fromBounds(GreatCircle.fromPole(Vector3D.Unit.PLUS_X, TEST_PRECISION)) |
| .getInteriorAngles().length); |
| } |
| |
| @Test |
| public void testGetInteriorAngles() { |
| // arrange |
| Point2S p1 = Point2S.PLUS_K; |
| Point2S p2 = Point2S.PLUS_I; |
| Point2S p4 = Point2S.PLUS_J; |
| |
| GreatCircle base = GreatCircle.fromPoints(p2, p4, TEST_PRECISION); |
| GreatCircle c1 = base.transform(Transform2S.createRotation(p2, -0.2)); |
| GreatCircle c2 = base.transform(Transform2S.createRotation(p4, 0.1)); |
| |
| Point2S p3 = c1.intersection(c2); |
| |
| // act |
| ConvexArea2S area = ConvexArea2S.fromVertexLoop(Arrays.asList(p1, p2, p3, p4), TEST_PRECISION); |
| |
| // assert |
| double[] angles = area.getInteriorAngles(); |
| Assert.assertEquals(4, angles.length); |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO + 0.2, angles[0], TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI - c1.angle(c2), angles[1], TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO + 0.1, angles[2], TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, angles[3], TEST_EPS); |
| } |
| |
| @Test |
| public void testTransform() { |
| // arrange |
| Transform2S t = Transform2S.createReflection(Point2S.PLUS_J); |
| ConvexArea2S input = ConvexArea2S.fromVertexLoop( |
| Arrays.asList(Point2S.PLUS_I, Point2S.PLUS_J, Point2S.PLUS_K), TEST_PRECISION); |
| |
| // act |
| ConvexArea2S area = input.transform(t); |
| |
| // assert |
| Assert.assertFalse(area.isFull()); |
| Assert.assertFalse(area.isEmpty()); |
| Assert.assertEquals(1.5 * PlaneAngleRadians.PI, area.getBoundarySize(), TEST_EPS); |
| Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, area.getSize(), TEST_EPS); |
| |
| Point2S expectedBarycenter = triangleBarycenter(Point2S.MINUS_J, Point2S.PLUS_I, Point2S.PLUS_K); |
| SphericalTestUtils.assertPointsEq(expectedBarycenter, area.getBarycenter(), TEST_EPS); |
| |
| checkBarycenterConsistency(area); |
| |
| List<GreatArc> arcs = sortArcs(area.getBoundaries()); |
| Assert.assertEquals(3, arcs.size()); |
| checkArc(arcs.get(0), Point2S.PLUS_K, Point2S.MINUS_J); |
| checkArc(arcs.get(1), Point2S.PLUS_I, Point2S.PLUS_K); |
| checkArc(arcs.get(2), Point2S.MINUS_J, Point2S.PLUS_I); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, |
| Point2S.of(-0.25 * PlaneAngleRadians.PI, 0.25 * PlaneAngleRadians.PI)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.BOUNDARY, |
| Point2S.PLUS_I, Point2S.MINUS_J, Point2S.PLUS_K, |
| Point2S.of(0, 0.25 * PlaneAngleRadians.PI), Point2S.of(-PlaneAngleRadians.PI_OVER_TWO, 0.304 * PlaneAngleRadians.PI), |
| Point2S.of(-0.25 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO)); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.OUTSIDE, |
| Point2S.PLUS_J, Point2S.MINUS_I, Point2S.MINUS_K); |
| } |
| |
| @Test |
| public void testTrim() { |
| // arrange |
| GreatCircle c1 = GreatCircle.fromPole(Vector3D.Unit.MINUS_X, TEST_PRECISION); |
| GreatCircle c2 = GreatCircle.fromPole(Vector3D.of(1, 1, 0), TEST_PRECISION); |
| |
| GreatCircle slanted = GreatCircle.fromPole(Vector3D.of(-1, 0, 1), TEST_PRECISION); |
| |
| ConvexArea2S area = ConvexArea2S.fromBounds(c1, c2); |
| |
| // act/assert |
| checkArc(area.trim(GreatArc.fromPoints(Point2S.of(0.1, PlaneAngleRadians.PI_OVER_TWO), Point2S.MINUS_I, TEST_PRECISION)), |
| Point2S.PLUS_J, Point2S.of(0.75 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO)); |
| |
| checkArc(area.trim(GreatArc.fromPoints(Point2S.MINUS_I, Point2S.of(0.2, PlaneAngleRadians.PI_OVER_TWO), TEST_PRECISION)), |
| Point2S.of(0.75 * PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO), Point2S.PLUS_J); |
| |
| checkArc(area.trim(GreatArc.fromPoints(Point2S.of(0.6 * PlaneAngleRadians.PI, 0.1), Point2S.of(0.7 * PlaneAngleRadians.PI, 0.8), TEST_PRECISION)), |
| Point2S.of(0.6 * PlaneAngleRadians.PI, 0.1), Point2S.of(0.7 * PlaneAngleRadians.PI, 0.8)); |
| |
| Assert.assertNull(area.trim(GreatArc.fromPoints(Point2S.MINUS_I, Point2S.MINUS_J, TEST_PRECISION))); |
| |
| checkArc(area.trim(slanted.span()), c1.intersection(slanted), slanted.intersection(c2)); |
| } |
| |
| @Test |
| public void testSplit_both() { |
| // arrange |
| GreatCircle c1 = GreatCircle.fromPole(Vector3D.Unit.MINUS_X, TEST_PRECISION); |
| GreatCircle c2 = GreatCircle.fromPole(Vector3D.of(1, 1, 0), TEST_PRECISION); |
| |
| ConvexArea2S area = ConvexArea2S.fromBounds(c1, c2); |
| |
| GreatCircle splitter = GreatCircle.fromPole(Vector3D.of(-1, 0, 1), TEST_PRECISION); |
| |
| // act |
| Split<ConvexArea2S> split = area.split(splitter); |
| |
| // assert |
| Assert.assertEquals(SplitLocation.BOTH, split.getLocation()); |
| |
| Point2S p1 = c1.intersection(splitter); |
| Point2S p2 = splitter.intersection(c2); |
| |
| ConvexArea2S minus = split.getMinus(); |
| assertPath(minus.getBoundaryPath(), Point2S.PLUS_K, p1, p2, Point2S.PLUS_K); |
| |
| ConvexArea2S plus = split.getPlus(); |
| assertPath(plus.getBoundaryPath(), p1, Point2S.MINUS_K, p2, p1); |
| |
| Assert.assertEquals(area.getSize(), minus.getSize() + plus.getSize(), TEST_EPS); |
| } |
| |
| @Test |
| public void testSplit_minus() { |
| // arrange |
| ConvexArea2S area = ConvexArea2S.fromVertexLoop(Arrays.asList( |
| Point2S.PLUS_I, Point2S.PLUS_K, Point2S.MINUS_J |
| ), TEST_PRECISION); |
| |
| GreatCircle splitter = GreatCircle.fromPole(Vector3D.of(0, -1, 1), TEST_PRECISION); |
| |
| // act |
| Split<ConvexArea2S> split = area.split(splitter); |
| |
| // assert |
| Assert.assertEquals(SplitLocation.MINUS, split.getLocation()); |
| |
| Assert.assertSame(area, split.getMinus()); |
| Assert.assertNull(split.getPlus()); |
| } |
| |
| @Test |
| public void testSplit_plus() { |
| // arrange |
| ConvexArea2S area = ConvexArea2S.fromVertexLoop(Arrays.asList( |
| Point2S.PLUS_I, Point2S.PLUS_K, Point2S.MINUS_J |
| ), TEST_PRECISION); |
| |
| GreatCircle splitter = GreatCircle.fromPole(Vector3D.of(0, 1, -1), TEST_PRECISION); |
| |
| // act |
| Split<ConvexArea2S> split = area.split(splitter); |
| |
| // assert |
| Assert.assertEquals(SplitLocation.PLUS, split.getLocation()); |
| |
| Assert.assertNull(split.getMinus()); |
| Assert.assertSame(area, split.getPlus()); |
| } |
| |
| @Test |
| public void testToTree() { |
| // arrange |
| ConvexArea2S area = ConvexArea2S.fromVertexLoop(Arrays.asList( |
| Point2S.of(0.1, 0.1), Point2S.of(-0.4, 1), |
| Point2S.of(0.15, 1.5), Point2S.of(0.3, 1.2), |
| Point2S.of(0.1, 0.1) |
| ), TEST_PRECISION); |
| |
| // act |
| RegionBSPTree2S tree = area.toTree(); |
| |
| // assert |
| Assert.assertFalse(tree.isFull()); |
| Assert.assertFalse(tree.isEmpty()); |
| |
| Assert.assertEquals(area.getSize(), tree.getSize(), TEST_EPS); |
| SphericalTestUtils.assertPointsEq(area.getBarycenter(), tree.getBarycenter(), TEST_EPS); |
| } |
| |
| private static List<GreatArc> sortArcs(List<GreatArc> arcs) { |
| List<GreatArc> result = new ArrayList<>(arcs); |
| |
| Collections.sort(result, (a, b) -> |
| Point2S.POLAR_AZIMUTH_ASCENDING_ORDER.compare(a.getStartPoint(), b.getStartPoint())); |
| |
| return result; |
| } |
| |
| private static Point2S triangleBarycenter(Point2S p1, Point2S p2, Point2S p3) { |
| // compute the barycenter using intersection mid point arcs |
| GreatCircle c1 = GreatCircle.fromPoints(p1, p2.slerp(p3, 0.5), TEST_PRECISION); |
| GreatCircle c2 = GreatCircle.fromPoints(p2, p1.slerp(p3, 0.5), TEST_PRECISION); |
| |
| return c1.intersection(c2); |
| } |
| |
| private static void checkArc(GreatArc arc, Point2S start, Point2S end) { |
| SphericalTestUtils.assertPointsEq(start, arc.getStartPoint(), TEST_EPS); |
| SphericalTestUtils.assertPointsEq(end, arc.getEndPoint(), TEST_EPS); |
| } |
| |
| private static void assertPath(GreatArcPath path, Point2S... expectedVertices) { |
| List<Point2S> vertices = path.getVertices(); |
| |
| Assert.assertEquals(expectedVertices.length, vertices.size()); |
| for (int i = 0; i < expectedVertices.length; ++i) { |
| |
| if (!expectedVertices[i].eq(vertices.get(i), TEST_PRECISION)) { |
| String msg = "Unexpected point in path at index " + i + ". Expected " + |
| Arrays.toString(expectedVertices) + " but received " + vertices; |
| Assert.fail(msg); |
| } |
| } |
| } |
| |
| private static void checkBarycenterConsistency(ConvexArea2S area) { |
| Point2S barycenter = area.getBarycenter(); |
| double size = area.getSize(); |
| |
| SphericalTestUtils.checkClassify(area, RegionLocation.INSIDE, barycenter); |
| |
| GreatCircle circle = GreatCircle.fromPole(barycenter.getVector(), TEST_PRECISION); |
| for (double az = 0; az <= PlaneAngleRadians.TWO_PI; az += 0.2) { |
| Point2S pt = circle.toSpace(Point1S.of(az)); |
| GreatCircle splitter = GreatCircle.fromPoints(barycenter, pt, TEST_PRECISION); |
| |
| Split<ConvexArea2S> split = area.split(splitter); |
| |
| Assert.assertEquals(SplitLocation.BOTH, split.getLocation()); |
| |
| ConvexArea2S minus = split.getMinus(); |
| double minusSize = minus.getSize(); |
| Point2S minusBc = minus.getBarycenter(); |
| |
| Vector3D weightedMinus = minusBc.getVector() |
| .multiply(minus.getSize()); |
| |
| ConvexArea2S plus = split.getPlus(); |
| double plusSize = plus.getSize(); |
| Point2S plusBc = plus.getBarycenter(); |
| |
| Vector3D weightedPlus = plusBc.getVector() |
| .multiply(plus.getSize()); |
| Point2S computedBarycenter = Point2S.from(weightedMinus.add(weightedPlus)); |
| |
| Assert.assertEquals(size, minusSize + plusSize, TEST_EPS); |
| SphericalTestUtils.assertPointsEq(barycenter, computedBarycenter, BARYCENTER_EPS); |
| } |
| } |
| } |