93 lines
3.3 KiB
Java
93 lines
3.3 KiB
Java
package com.jwetherell.algorithms.mathematics;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
|
||
/**
|
||
* The Ramer–Douglas–Peucker algorithm (RDP) is an algorithm for reducing the number of points in a
|
||
* curve that is approximated by a series of points.
|
||
*
|
||
* http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
|
||
*
|
||
* @author Justin Wetherell <phishman3579@gmail.com>
|
||
*/
|
||
public class RamerDouglasPeucker {
|
||
|
||
private RamerDouglasPeucker() { }
|
||
|
||
private static final double sqr(double x) {
|
||
return Math.pow(x, 2);
|
||
}
|
||
|
||
private static final double distanceBetweenPoints(double vx, double vy, double wx, double wy) {
|
||
return sqr(vx - wx) + sqr(vy - wy);
|
||
}
|
||
|
||
private static final double distanceToSegmentSquared(double px, double py, double vx, double vy, double wx, double wy) {
|
||
final double l2 = distanceBetweenPoints(vx, vy, wx, wy);
|
||
if (l2 == 0)
|
||
return distanceBetweenPoints(px, py, vx, vy);
|
||
final double t = ((px - vx) * (wx - vx) + (py - vy) * (wy - vy)) / l2;
|
||
if (t < 0)
|
||
return distanceBetweenPoints(px, py, vx, vy);
|
||
if (t > 1)
|
||
return distanceBetweenPoints(px, py, wx, wy);
|
||
return distanceBetweenPoints(px, py, (vx + t * (wx - vx)), (vy + t * (wy - vy)));
|
||
}
|
||
|
||
private static final double perpendicularDistance(double px, double py, double vx, double vy, double wx, double wy) {
|
||
return Math.sqrt(distanceToSegmentSquared(px, py, vx, vy, wx, wy));
|
||
}
|
||
|
||
private static final void douglasPeucker(List<Double[]> list, int s, int e, double epsilon, List<Double[]> resultList) {
|
||
// Find the point with the maximum distance
|
||
double dmax = 0;
|
||
int index = 0;
|
||
|
||
final int start = s;
|
||
final int end = e-1;
|
||
for (int i=start+1; i<end; i++) {
|
||
// Point
|
||
final double px = list.get(i)[0];
|
||
final double py = list.get(i)[1];
|
||
// Start
|
||
final double vx = list.get(start)[0];
|
||
final double vy = list.get(start)[1];
|
||
// End
|
||
final double wx = list.get(end)[0];
|
||
final double wy = list.get(end)[1];
|
||
final double d = perpendicularDistance(px, py, vx, vy, wx, wy);
|
||
if (d > dmax) {
|
||
index = i;
|
||
dmax = d;
|
||
}
|
||
}
|
||
// If max distance is greater than epsilon, recursively simplify
|
||
if (dmax > epsilon) {
|
||
// Recursive call
|
||
douglasPeucker(list, s, index, epsilon, resultList);
|
||
douglasPeucker(list, index, e, epsilon, resultList);
|
||
} else {
|
||
if ((end-start)>0) {
|
||
resultList.add(list.get(start));
|
||
resultList.add(list.get(end));
|
||
} else {
|
||
resultList.add(list.get(start));
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Given a curve composed of line segments find a similar curve with fewer points.
|
||
*
|
||
* @param list List of Double[] points (x,y)
|
||
* @param epsilon Distance dimension
|
||
* @return Similar curve with fewer points
|
||
*/
|
||
public static final List<Double[]> douglasPeucker(List<Double[]> list, double epsilon) {
|
||
final List<Double[]> resultList = new ArrayList<Double[]>();
|
||
douglasPeucker(list, 0, list.size(), epsilon, resultList);
|
||
return resultList;
|
||
}
|
||
}
|