/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package test.jts.perf.operation.buffer;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineSegment;
import org.locationtech.jts.geom.LineString;
/**
* 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(double maxSegLen, int numPts, GeometryFactory fact)
{
RandomOffsetLineStringGenerator rlg = new RandomOffsetLineStringGenerator(maxSegLen, numPts);
return rlg.generate(fact);
}
private double maxSegLen;
private int numPts;
private int exponent2 = 5;
private Coordinate[] pts;
private Coordinate endPoint;
public RandomOffsetLineStringGenerator(double maxSegLen, int numPts)
{
this.maxSegLen = maxSegLen;
exponent2 = (int) (Math.log(numPts) / Math.log(2));
int pow2 = pow2(exponent2);
if (pow2 < numPts)
exponent2 += 1;
this.numPts = pow2(exponent2) + 1;
}
public Geometry generate(GeometryFactory fact)
{
pts = new Coordinate[numPts];
pts[0] = new Coordinate();
double ang = Math.PI * Math.random();
endPoint = new Coordinate(maxSegLen * Math.cos(ang),maxSegLen * Math.sin(ang));
pts[numPts - 1] = endPoint;
int interval = numPts / 2;
while (interval >= 1) {
createRandomOffsets(interval);
interval /= 2;
}
return fact.createLineString(pts);
}
private void createRandomOffsets(int interval)
{
// for (int i = 0; i )
int inc = pow2(exponent2);
while (inc > 1) {
computeRandomOffsets(inc);
inc /= 2;
}
}
private void computeRandomOffsets(int inc)
{
int inc2 = inc / 2;
for (int i = 0; i + inc2 < numPts; i += inc) {
int midIndex = i + inc2;
int endIndex = i + inc;
Coordinate segEndPoint;
double segFrac = 0.5 + randomFractionPerturbation();
if (endIndex >= numPts) {
segEndPoint = endPoint;
segFrac = midIndex / numPts;
}
else {
segEndPoint = pts[i + inc];
}
pts[midIndex] = computeRandomOffset(pts[i], segEndPoint, segFrac);
}
}
private Coordinate computeRandomOffset(Coordinate p0, Coordinate p1, double segFrac)
{
double len = p0.distance(p1);
double len2 = len / 2;
double offsetLen = (len * Math.random()) - len2;
LineSegment seg = new LineSegment(p0, p1);
return seg.pointAlongOffset(segFrac, offsetLen);
}
private double randomFractionPerturbation()
{
double rnd = Math.random();
double mag = rnd * rnd * rnd;
int sign = Math.random() > 0.5 ? 1 : -1;
return sign * mag;
}
private static int pow2(int exponent)
{
int pow2 = 1;
for (int i = 0; i < exponent; i++) {
pow2 *= 2;
}
return pow2;
}
}