diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferCurveSetBuilder.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferCurveSetBuilder.java index 0f491bc8cf..ff175f36a8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferCurveSetBuilder.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferCurveSetBuilder.java @@ -307,6 +307,7 @@ private void addRingSide(Coordinate[] coord, double offsetDistance, int side, in } private static final int MAX_INVERTED_RING_SIZE = 9; + private static final int INVERTED_CURVE_VERTEX_FACTOR = 4; private static final double NEARNESS_FACTOR = 0.99; /** @@ -344,10 +345,11 @@ private static boolean isRingCurveInverted(Coordinate[] inputPts, double distanc if (inputPts.length >= MAX_INVERTED_RING_SIZE) return false; /** - * An inverted curve has no more points than the input ring. - * This also eliminates concave inputs (which will produce fillet arcs) + * Don't check curves which are much larger than the input. + * This improves performance by avoiding checking some concave inputs + * (which can produce fillet arcs with many more vertices) */ - if (curvePts.length > inputPts.length) return false; + if (curvePts.length > INVERTED_CURVE_VERTEX_FACTOR * inputPts.length) return false; /** * Check if the curve vertices are all closer to the input ring diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferTest.java index 78b4c0d019..73b5509362 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferTest.java @@ -565,6 +565,17 @@ public void testMultiPolygonElementRemoved() { checkBufferNumGeometries(wkt, -18, 1); } + /** + * Checks a bug in the inverted-ring-removal heuristic. + * See https://github.com/locationtech/jts/issues/876 + */ + public void testLineClosedNoHole() { + String wkt = "LINESTRING (-20 0, 0 20, 20 0, 0 -20, -20 0)"; + checkBufferHasHole(wkt, 70, false); + } + + //=================================================== + private void checkBufferEmpty(String wkt, double dist, boolean isEmptyExpected) { Geometry a = read(wkt); Geometry result = a.buffer(dist);