package com.revolsys.geometry.test.old.perf.operation.buffer;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.impl.PointDoubleXY;
import com.revolsys.geometry.model.segment.LineSegment;
import com.revolsys.geometry.model.segment.LineSegmentDouble;
/**
* Generates random {@link LineString}s, which are somewhat coherent
* in terms of how far they deviate from a given line segment,
* and how much they twist around.
* <p>
* The method is to recursively perturb line segment midpoints by a random offset.
*
* @author mbdavis
*
*/
public class RandomOffsetLineStringGenerator {
public static Geometry generate(final double maxSegLen, final int numPts,
final GeometryFactory fact) {
final RandomOffsetLineStringGenerator rlg = new RandomOffsetLineStringGenerator(maxSegLen,
numPts);
return rlg.generate(fact);
}
private static int pow2(final int exponent) {
int pow2 = 1;
for (int i = 0; i < exponent; i++) {
pow2 *= 2;
}
return pow2;
}
private Point endPoint;
private int exponent2 = 5;
private final double maxSegLen;
private final int numPts;
private Point[] pts;
public RandomOffsetLineStringGenerator(final double maxSegLen, final int numPts) {
this.maxSegLen = maxSegLen;
this.exponent2 = (int)(Math.log(numPts) / Math.log(2));
final int pow2 = pow2(this.exponent2);
if (pow2 < numPts) {
this.exponent2 += 1;
}
this.numPts = pow2(this.exponent2) + 1;
}
private Point computeRandomOffset(final Point p0, final Point p1, final double segFrac) {
final double len = p0.distancePoint(p1);
final double len2 = len / 2;
final double offsetLen = len * Math.random() - len2;
final LineSegment seg = new LineSegmentDouble(p0, p1);
return seg.pointAlongOffset(segFrac, offsetLen);
}
private void computeRandomOffsets(final int inc) {
final int inc2 = inc / 2;
for (int i = 0; i + inc2 < this.numPts; i += inc) {
final int midIndex = i + inc2;
final int endIndex = i + inc;
Point segEndPoint;
double segFrac = 0.5 + randomFractionPerturbation();
if (endIndex >= this.numPts) {
segEndPoint = this.endPoint;
segFrac = midIndex / this.numPts;
} else {
segEndPoint = this.pts[i + inc];
}
this.pts[midIndex] = computeRandomOffset(this.pts[i], segEndPoint, segFrac);
}
}
public Geometry generate(final GeometryFactory fact) {
this.pts = new Point[this.numPts];
this.pts[0] = GeometryFactory.DEFAULT_3D.point();
final double ang = Math.PI * Math.random();
this.endPoint = new PointDoubleXY(this.maxSegLen * Math.cos(ang),
this.maxSegLen * Math.sin(ang));
this.pts[this.numPts - 1] = this.endPoint;
int interval = this.numPts / 2;
while (interval >= 1) {
newRandomOffsets(interval);
interval /= 2;
}
return fact.lineString(this.pts);
}
private void newRandomOffsets(final int interval) {
// for (int i = 0; i )
int inc = pow2(this.exponent2);
while (inc > 1) {
computeRandomOffsets(inc);
inc /= 2;
}
}
private double randomFractionPerturbation() {
final double rnd = Math.random();
final double mag = rnd * rnd * rnd;
final int sign = Math.random() > 0.5 ? 1 : -1;
return sign * mag;
}
}