1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.enclosing.euclidean.twod;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.List;
22
23 import org.apache.commons.geometry.core.GeometryTestUtils;
24 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
25 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
26 import org.apache.commons.geometry.enclosing.EnclosingBall;
27 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
28 import org.apache.commons.geometry.euclidean.twod.Vector2D;
29 import org.apache.commons.rng.UniformRandomProvider;
30 import org.apache.commons.rng.simple.RandomSource;
31 import org.junit.Assert;
32 import org.junit.Test;
33
34 public class WelzlEncloser2DTest {
35
36 private static final double TEST_EPS = 1e-10;
37
38 private static final DoublePrecisionContext TEST_PRECISION =
39 new EpsilonDoublePrecisionContext(TEST_EPS);
40
41 private final WelzlEncloser2D encloser = new WelzlEncloser2D(TEST_PRECISION);
42
43 @Test
44 public void testNoPoints() {
45
46 final String msg = "Unable to generate enclosing ball: no points given";
47
48
49 GeometryTestUtils.assertThrows(() -> {
50 encloser.enclose(null);
51 }, IllegalArgumentException.class, msg);
52
53 GeometryTestUtils.assertThrows(() -> {
54 encloser.enclose(new ArrayList<Vector2D>());
55 }, IllegalArgumentException.class, msg);
56 }
57
58 @Test
59 public void testRegularPoints() {
60
61 final List<Vector2D> list = buildList(22, 26, 30, 38, 64, 28, 8, 54, 11, 15);
62
63
64 checkDisk(list, Arrays.asList(list.get(2), list.get(3), list.get(4)));
65 }
66
67 @Test
68 public void testSolutionOnDiameter() {
69
70 final List<Vector2D> list = buildList(22, 26, 30, 38, 64, 28, 8, 54);
71
72
73 checkDisk(list, Arrays.asList(list.get(2), list.get(3)));
74 }
75
76 @Test
77 public void testReducingBall1() {
78
79 final List<Vector2D> list = buildList(0.05380958511396061, 0.57332359658700000,
80 0.99348810731127870, 0.02056421361521466,
81 0.01203950647796437, 0.99779675042261860,
82 0.00810189987706078, 0.00589246003827815,
83 0.00465180821202149, 0.99219972923046940);
84
85
86 checkDisk(list, Arrays.asList(list.get(1), list.get(3), list.get(4)));
87 }
88
89 @Test
90 public void testReducingBall2() {
91
92 final List<Vector2D> list = buildList(0.016930586154703, 0.333955448537779,
93 0.987189104892331, 0.969778855274507,
94 0.983696889599935, 0.012904580013266,
95 0.013114499572905, 0.034740156356895);
96
97
98 checkDisk(list, Arrays.asList(list.get(1), list.get(2), list.get(3)));
99 }
100
101 @Test
102 public void testLargeSamples() {
103
104 final UniformRandomProvider random = RandomSource.create(RandomSource.WELL_1024_A, 0xa2a63cad12c01fb2L);
105 for (int k = 0; k < 100; ++k) {
106 final int nbPoints = random.nextInt(10000);
107 final List<Vector2D> points = new ArrayList<>();
108 for (int i = 0; i < nbPoints; ++i) {
109 final double x = random.nextDouble();
110 final double y = random.nextDouble();
111 points.add(Vector2D.of(x, y));
112 }
113
114
115 checkDisk(points);
116 }
117 }
118
119 @Test
120 public void testEnclosingWithPrecision() {
121
122 final List<Vector2D> points = Arrays.asList(
123 Vector2D.of(271.59, 57.282),
124 Vector2D.of(269.145, 57.063),
125 Vector2D.of(309.117, 77.187),
126 Vector2D.of(316.989, 34.835),
127 Vector2D.of(323.101, 53.972)
128 );
129 final double precision = 1;
130 final DoublePrecisionContext precisionContext = new EpsilonDoublePrecisionContext(precision);
131 final WelzlEncloser2D customPrecisionEncloser = new WelzlEncloser2D(precisionContext);
132
133
134 final EnclosingBall<Vector2D> result = customPrecisionEncloser.enclose(points);
135
136
137 Assert.assertEquals(27.099954200964234, result.getRadius(), TEST_EPS);
138 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(296.0056977503686, 53.469890753441945),
139 result.getCenter(), TEST_EPS);
140 }
141
142 private List<Vector2D> buildList(final double... coordinates) {
143 final List<Vector2D> list = new ArrayList<>(coordinates.length / 2);
144 for (int i = 0; i < coordinates.length; i += 2) {
145 list.add(Vector2D.of(coordinates[i], coordinates[i + 1]));
146 }
147 return list;
148 }
149
150 private void checkDisk(final List<Vector2D> points, final List<Vector2D> refSupport) {
151
152 final EnclosingBall<Vector2D> disk = checkDisk(points);
153
154
155 final EnclosingBall<Vector2D> expected = new DiskGenerator().ballOnSupport(refSupport);
156 Assert.assertEquals(refSupport.size(), disk.getSupportSize());
157 Assert.assertEquals(expected.getRadius(), disk.getRadius(), 1.0e-10);
158 Assert.assertEquals(expected.getCenter().getX(), disk.getCenter().getX(), 1.0e-10);
159 Assert.assertEquals(expected.getCenter().getY(), disk.getCenter().getY(), 1.0e-10);
160
161 for (final Vector2D s : disk.getSupport()) {
162 boolean found = false;
163 for (final Vector2D rs : refSupport) {
164 if (s == rs) {
165 found = true;
166 break;
167 }
168 }
169 Assert.assertTrue(found);
170 }
171
172
173 for (int i = 0; i < disk.getSupportSize(); ++i) {
174 final List<Vector2D> reducedSupport = new ArrayList<>();
175 int count = 0;
176 for (final Vector2D s : disk.getSupport()) {
177 if (count++ != i) {
178 reducedSupport.add(s);
179 }
180 }
181 final EnclosingBall<Vector2D> reducedDisk = new DiskGenerator().ballOnSupport(reducedSupport);
182 boolean foundOutside = false;
183 for (int j = 0; j < points.size() && !foundOutside; ++j) {
184 if (!reducedDisk.contains(points.get(j), TEST_PRECISION)) {
185 foundOutside = true;
186 }
187 }
188 Assert.assertTrue(foundOutside);
189 }
190 }
191
192 private EnclosingBall<Vector2D> checkDisk(final List<Vector2D> points) {
193
194 final EnclosingBall<Vector2D> disk = encloser.enclose(points);
195
196
197 for (final Vector2D v : points) {
198 Assert.assertTrue(disk.contains(v, TEST_PRECISION));
199 }
200
201
202 final Vector2D center = disk.getCenter();
203 final double radius = disk.getRadius();
204
205 for (final Vector2D s : disk.getSupport()) {
206 Assert.assertTrue(TEST_PRECISION.eqZero(center.distance(s) - radius));
207 }
208
209 return disk;
210 }
211 }