1
1
/*
2
- * Copyright (c) 2020 Martin Davis.
2
+ * Copyright (c) 2025 Martin Davis.
3
3
*
4
4
* All rights reserved. This program and the accompanying materials
5
5
* are made available under the terms of the Eclipse Public License 2.0
28
28
29
29
/**
30
30
* Constructs the Largest Empty Circle for a set
31
- * of obstacle geometries, up to a given accuracy distance tolerance.
31
+ * of obstacle geometries, up to a given accuracy distance tolerance
32
+ * (which can be specified or determined automatically).
32
33
* The obstacles may be any combination of point, linear and polygonal geometries.
33
34
* <p>
34
35
* The Largest Empty Circle (LEC) is the largest circle
60
61
*/
61
62
public class LargestEmptyCircle {
62
63
64
+ /**
65
+ * Computes the center point of the Largest Empty Circle
66
+ * interior-disjoint to a set of obstacles.
67
+ * The obstacles may be any collection of points, lines and polygons.
68
+ * The center of the LEC lies within the convex hull of the obstacles.
69
+ *
70
+ * @param obstacles a geometry representing the obstacles
71
+ * @return the center point of the Largest Empty Circle
72
+ */
73
+ public static Point getCenter (Geometry obstacles ) {
74
+ return getCenter (obstacles , null , 0.0 );
75
+ }
76
+
63
77
/**
64
78
* Computes the center point of the Largest Empty Circle
65
79
* interior-disjoint to a set of obstacles,
@@ -75,6 +89,20 @@ public static Point getCenter(Geometry obstacles, double tolerance) {
75
89
return getCenter (obstacles , null , tolerance );
76
90
}
77
91
92
+ /**
93
+ * Computes the center point of the Largest Empty Circle
94
+ * interior-disjoint to a set of obstacles and within a polygonal boundary.
95
+ * The obstacles may be any collection of points, lines and polygons.
96
+ * The center of the LEC lies within the given boundary.
97
+ *
98
+ * @param obstacles a geometry representing the obstacles
99
+ * @param boundary a polygonal geometry to contain the LEC center
100
+ * @return the center point of the Largest Empty Circle
101
+ */
102
+ public static Point getCenter (Geometry obstacles , Geometry boundary ) {
103
+ return getCenter (obstacles , boundary , 0.0 );
104
+ }
105
+
78
106
/**
79
107
* Computes the center point of the Largest Empty Circle
80
108
* interior-disjoint to a set of obstacles and within a polygonal boundary,
@@ -152,7 +180,23 @@ public static LineString getRadiusLine(Geometry obstacles, Geometry boundary, do
152
180
*
153
181
* @param obstacles a non-empty geometry representing the obstacles
154
182
* @param boundary a polygonal geometry (may be null or empty)
155
- * @param tolerance a distance tolerance for computing the circle center point (a positive value)
183
+ */
184
+ public LargestEmptyCircle (Geometry obstacles , Geometry boundary ) {
185
+ this (obstacles , boundary , 0.0 );
186
+ }
187
+
188
+ /**
189
+ * Creates a new instance of a Largest Empty Circle construction,
190
+ * interior-disjoint to a set of obstacle geometries
191
+ * and having its center within a polygonal boundary.
192
+ * The obstacles may be any collection of points, lines and polygons.
193
+ * If the boundary is null or empty the convex hull
194
+ * of the obstacles is used as the boundary.
195
+ * A zero tolerance aut0matically determines an approximation tolerance.
196
+ *
197
+ * @param obstacles a non-empty geometry representing the obstacles
198
+ * @param boundary a polygonal geometry (may be null or empty)
199
+ * @param tolerance a distance tolerance for computing the circle center point (a non-negative value)
156
200
*/
157
201
public LargestEmptyCircle (Geometry obstacles , Geometry boundary , double tolerance ) {
158
202
if (obstacles == null || obstacles .isEmpty ()) {
@@ -161,8 +205,8 @@ public LargestEmptyCircle(Geometry obstacles, Geometry boundary, double toleranc
161
205
if (boundary != null && ! (boundary instanceof Polygonal )) {
162
206
throw new IllegalArgumentException ("Boundary must be polygonal" );
163
207
}
164
- if (tolerance <= 0 ) {
165
- throw new IllegalArgumentException ("Accuracy tolerance is non-positive : " + tolerance );
208
+ if (tolerance < 0 ) {
209
+ throw new IllegalArgumentException ("Accuracy tolerance is negative : " + tolerance );
166
210
}
167
211
this .obstacles = obstacles ;
168
212
this .boundary = boundary ;
@@ -320,6 +364,9 @@ private void compute() {
320
364
radiusPoint = factory .createPoint (radiusPt );
321
365
}
322
366
367
+ //-- empirically determined to balance accuracy and speed
368
+ private static final double AUTO_TOLERANCE_FRACTION = 0.001 ;
369
+
323
370
/**
324
371
* Tests whether a cell may contain the circle center,
325
372
* and thus should be refined (split into subcells
@@ -336,14 +383,24 @@ private boolean mayContainCircleCenter(Cell cell) {
336
383
if (cell .isFullyOutside ())
337
384
return false ;
338
385
386
+ /**
387
+ * The tolerance can be automatically determined
388
+ * as a fraction of the current farthest distance.
389
+ * For a very small actual MIC distance this may cause many iterations,
390
+ * but the iter limit prevents an infinite loop
391
+ */
392
+ double requiredTol = tolerance > 0
393
+ ? tolerance
394
+ : farthestCell .getDistance () * AUTO_TOLERANCE_FRACTION ;
395
+
339
396
/**
340
397
* The cell is outside, but overlaps the boundary
341
398
* so it may contain a point which should be checked.
342
399
* This is only the case if the potential overlap distance
343
400
* is larger than the tolerance.
344
401
*/
345
402
if (cell .isOutside ()) {
346
- boolean isOverlapSignificant = cell .getMaxDistance () > tolerance ;
403
+ boolean isOverlapSignificant = cell .getMaxDistance () > requiredTol ;
347
404
return isOverlapSignificant ;
348
405
}
349
406
@@ -353,7 +410,7 @@ private boolean mayContainCircleCenter(Cell cell) {
353
410
* (up to tolerance).
354
411
*/
355
412
double potentialIncrease = cell .getMaxDistance () - farthestCell .getDistance ();
356
- return potentialIncrease > tolerance ;
413
+ return potentialIncrease > requiredTol ;
357
414
}
358
415
359
416
/**
0 commit comments