Skip to content

Commit

Permalink
Fix BufferParameters logic for quadsegs (#778)
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Davis <mtnclimb@gmail.com>
  • Loading branch information
dr-jts authored Sep 29, 2021
1 parent 3b8c337 commit 861402a
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,8 @@ public void setEndCapStyle(int endCapStyle)
}

/**
* Sets the number of segments used to approximate a angle fillet
* Sets the number of line segments in a quarter-circle
* used to approximate angle fillets for round end caps and joins.
*
* @param quadrantSegments the number of segments in a fillet for a quadrant
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public BufferParameters(int quadrantSegments,

/**
* Gets the number of quadrant segments which will be used
* to approximate angle fillets in round endcaps and joins.
*
* @return the number of quadrant segments
*/
Expand All @@ -146,60 +147,24 @@ public int getQuadrantSegments()
}

/**
* Sets the number of line segments used to approximate an angle fillet.
* <ul>
* <li>If <tt>quadSegs</tt> &gt;= 1, joins are round, and <tt>quadSegs</tt> indicates the number of
* segments to use to approximate a quarter-circle.
* <li>If <tt>quadSegs</tt> = 0, joins are bevelled (flat)
* <li>If <tt>quadSegs</tt> &lt; 0, joins are mitred, and the value of qs
* indicates the mitre ration limit as
* <pre>
* mitreLimit = |<tt>quadSegs</tt>|
* </pre>
* </ul>
* For round joins, <tt>quadSegs</tt> determines the maximum
* Sets the number of line segments in a quarter-circle
* used to approximate angle fillets in round endcaps and joins.
* The value should be at least 1.
* <p>
* This determines the
* error in the approximation to the true buffer curve.
* The default value of 8 gives less than 2% max error in the buffer distance.
* For a max error of &lt; 1%, use QS = 12.
* For a max error of &lt; 0.1%, use QS = 18.
* The default value of 8 gives less than 2% error in the buffer distance.
* For a error of &lt; 1%, use QS = 12.
* For a error of &lt; 0.1%, use QS = 18.
* The error is always less than the buffer distance
* (in other words, the computed buffer curve is always inside the true
* curve).
*
* @param quadSegs the number of segments in a fillet for a quadrant
* @param quadSegs the number of segments in a fillet for a circle quadrant
*/
public void setQuadrantSegments(int quadSegs)
{
quadrantSegments = quadSegs;

/**
* Indicates how to construct fillets.
* If qs >= 1, fillet is round, and qs indicates number of
* segments to use to approximate a quarter-circle.
* If qs = 0, fillet is bevelled flat (i.e. no filleting is performed)
* If qs < 0, fillet is mitred, and absolute value of qs
* indicates maximum length of mitre according to
*
* mitreLimit = |qs|
*/
if (quadrantSegments == 0)
joinStyle = JOIN_BEVEL;
if (quadrantSegments < 0) {
joinStyle = JOIN_MITRE;
mitreLimit = Math.abs(quadrantSegments);
}

if (quadSegs <= 0) {
quadrantSegments = 1;
}

/**
* If join style was set by the quadSegs value,
* use the default for the actual quadrantSegments value.
*/
if (joinStyle != JOIN_ROUND) {
quadrantSegments = DEFAULT_QUADRANT_SEGMENTS;
}
}

/**
Expand All @@ -218,7 +183,7 @@ public static double bufferDistanceError(int quadSegs)
/**
* Gets the end cap style.
*
* @return the end cap style
* @return the end cap style code
*/
public int getEndCapStyle()
{
Expand All @@ -228,17 +193,17 @@ public int getEndCapStyle()
/**
* Specifies the end cap style of the generated buffer.
* The styles supported are {@link #CAP_ROUND}, {@link #CAP_FLAT}, and {@link #CAP_SQUARE}.
* The default is CAP_ROUND.
* The default is {@link #CAP_ROUND}.
*
* @param endCapStyle the end cap style to specify
* @param endCapStyle the code for the end cap style
*/
public void setEndCapStyle(int endCapStyle)
{
this.endCapStyle = endCapStyle;
}

/**
* Gets the join style
* Gets the join style.
*
* @return the join style code
*/
Expand All @@ -249,8 +214,9 @@ public int getJoinStyle()

/**
* Sets the join style for outside (reflex) corners between line segments.
* Allowable values are {@link #JOIN_ROUND} (which is the default),
* The styles supported are {@link #JOIN_ROUND},
* {@link #JOIN_MITRE} and {link JOIN_BEVEL}.
* The default is {@link #JOIN_ROUND}.
*
* @param joinStyle the code for the join style
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ public OffsetSegmentGenerator(PrecisionModel precisionModel,
// compute intersections in full precision, to provide accuracy
// the points are rounded as they are inserted into the curve line
li = new RobustLineIntersector();
filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments();

int quadSegs = bufParams.getQuadrantSegments();
if (quadSegs < 1) quadSegs = 1;
filletAngleQuantum = Math.PI / 2.0 / quadSegs;

/**
* Non-round joins cause issues with short closing segments, so don't use
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.locationtech.jts.operation.buffer;

import org.locationtech.jts.geom.Geometry;

import test.jts.GeometryTestCase;

/**
* Tests for the effect of buffer parameter values.
*
* @author Martin Davis
*
*/
public class BufferParameterTest extends GeometryTestCase {

public static void main(String[] args) {
junit.textui.TestRunner.run(BufferParameterTest.class);
}

public BufferParameterTest(String name) {
super(name);
}

public void testQuadSegsNeg() {
checkBuffer("LINESTRING (20 20, 80 20, 80 80)",
10.0, -99,
"POLYGON ((70 30, 70 80, 80 90, 90 80, 90 20, 80 10, 20 10, 10 20, 20 30, 70 30))");
}

public void testQuadSegs0() {
checkBuffer("LINESTRING (20 20, 80 20, 80 80)",
10.0, 0,
"POLYGON ((70 30, 70 80, 80 90, 90 80, 90 20, 80 10, 20 10, 10 20, 20 30, 70 30))");
}

public void testQuadSegs1() {
checkBuffer("LINESTRING (20 20, 80 20, 80 80)",
10.0, 1,
"POLYGON ((70 30, 70 80, 80 90, 90 80, 90 20, 80 10, 20 10, 10 20, 20 30, 70 30))");
}

public void testQuadSegs2() {
checkBuffer("LINESTRING (20 20, 80 20, 80 80)",
10.0, 2,
"POLYGON ((70 30, 70 80, 72.92893218813452 87.07106781186548, 80 90, 87.07106781186548 87.07106781186548, 90 80, 90 20, 87.07106781186548 12.928932188134524, 80 10, 20 10, 12.928932188134523 12.928932188134524, 10 20, 12.928932188134524 27.071067811865476, 20 30, 70 30))");
}

public void testQuadSegs2Bevel() {
checkBuffer("LINESTRING (20 20, 80 20, 80 80)",
10.0, 2, BufferParameters.JOIN_BEVEL,
"POLYGON ((70 30, 70 80, 72.92893218813452 87.07106781186548, 80 90, 87.07106781186548 87.07106781186548, 90 80, 90 20, 80 10, 20 10, 12.928932188134523 12.928932188134524, 10 20, 12.928932188134524 27.071067811865476, 20 30, 70 30))");
}


private void checkBuffer(String wkt, double dist, int quadSegs, String wktExpected) {
checkBuffer( wkt, dist, quadSegs, BufferParameters.JOIN_ROUND, wktExpected);
}

private void checkBuffer(String wkt, double dist, int quadSegs, int joinStyle, String wktExpected) {
BufferParameters param = new BufferParameters();
param.setQuadrantSegments(quadSegs);
param.setJoinStyle(joinStyle);
Geometry geom = read(wkt);
Geometry result = BufferOp.bufferOp(geom, dist, param);
Geometry expected = read(wktExpected);
checkEqual(expected, result);
}
}

0 comments on commit 861402a

Please sign in to comment.