Skip to content

Commit

Permalink
make smoothGaussian() robust against vertex order
Browse files Browse the repository at this point in the history
  • Loading branch information
micycle1 committed Aug 8, 2024
1 parent d1639bd commit d4cff64
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed
* `urquhartFaces()`, `relativeNeighborFaces()`, `gabrielFaces()` and `spannerFaces()` from `PGS_Meshing` now preserve holes from the input.
* The `from` and `to` arguments for `align()` were the wrong way round.
* The output of `PGS_Morphology.smoothGaussian()` is no longer (slightly) affected by the vertex ordering of the input.

### Removed
* `simplifyDCE(shape, targetNumVertices)` and `simplifyDCE(shape, vertexRemovalFraction)` in favour a single method that accepts a user-defined termination callback that is supplied with the current vertex candidate's coordinate, relevance score, and the number of vertices remaining.
Expand Down
45 changes: 45 additions & 0 deletions src/main/java/micycle/pgs/commons/GaussianLineSmoothing.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package micycle.pgs.commons;

import java.util.Arrays;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString;

import net.jafama.FastMath;

/**
Expand Down Expand Up @@ -43,6 +46,9 @@ public static LineString get(LineString line, double sigmaM, double resolution)
return (LineString) line.copy();
}

// output is sensitive to vertex order, so normalise the line (note doesn't
// normalise orientation)
line = normalise(line);
boolean isClosed = line.isClosed();
double length = line.getLength();
double densifiedResolution = sigmaM / 3;
Expand Down Expand Up @@ -165,4 +171,43 @@ public static LineString get(LineString line, double sigmaM, double resolution)
return lsOut;
}

/**
* Normalises the LineString so that it starts from the coordinate with the
* smallest x value. In case of a tie on the x value, the smallest y value is
* used.
*
* @param line The open or closed LineString to be normalised.
* @return A new LineString with coordinates ordered starting from the smallest
* x (and y, if tied).
*/
private static LineString normalise(LineString line) {
boolean isClosed = line.isClosed();

Coordinate[] originalCoords = line.getCoordinates();
if (isClosed && originalCoords[0].equals2D(originalCoords[originalCoords.length - 1])) {
// Remove last vertex if it is a duplicate of the first for closed lines
originalCoords = Arrays.copyOf(originalCoords, originalCoords.length - 1);
}

// Find index of coordinate with smallest x value, tie by y value
int minIndex = 0;
for (int i = 1; i < originalCoords.length; i++) {
if (originalCoords[i].x < originalCoords[minIndex].x
|| (originalCoords[i].x == originalCoords[minIndex].x && originalCoords[i].y < originalCoords[minIndex].y)) {
minIndex = i;
}
}

// Rotate array to start from vertex with smallest x (and y, if tied)
Coordinate[] rotatedCoords = new Coordinate[originalCoords.length + (isClosed ? 1 : 0)];
System.arraycopy(originalCoords, minIndex, rotatedCoords, 0, originalCoords.length - minIndex);
System.arraycopy(originalCoords, 0, rotatedCoords, originalCoords.length - minIndex, minIndex);

if (isClosed) {
rotatedCoords[rotatedCoords.length - 1] = rotatedCoords[0]; // Close the loop
}

return line.getFactory().createLineString(rotatedCoords);
}

}

0 comments on commit d4cff64

Please sign in to comment.