/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2014, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.geometry.jts;
import static java.lang.Math.atan2;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import com.vividsolutions.jts.geom.CoordinateSequence;
/**
* Utility class for the test, represents a circle in the origin with a given radius
*
* @author Andrea Aime - GeoSolutions
*/
class Circle {
double radius;
double cx, cy;
static final double EPS = 1e-6;
public Circle(double radius, double cx, double cy) {
this.radius = radius;
this.cx = cx;
this.cy = cy;
}
public Circle(double radius) {
this(radius, 0, 0);
}
public double[] samplePoints(double... angles) {
double[] result = new double[angles.length * 2];
int i = 0;
for (double angle : angles) {
result[i++] = cx + radius * Math.cos(angle);
result[i++] = cy + radius * Math.sin(angle);
}
return result;
}
public CircularArc getCircularArc(double startAngle, double midAngle, double endAngle) {
double[] controlPoints = samplePoints(startAngle, midAngle, endAngle);
return new CircularArc(controlPoints);
}
public void assertTolerance(CoordinateSequence cs, double tolerance) {
double[] ordinates = new double[cs.size() * 2];
for (int i = 0; i < cs.size(); i++) {
ordinates[i * 2] = cs.getOrdinate(i, 0);
ordinates[i * 2 + 1] = cs.getOrdinate(i, 1);
}
assertTolerance(ordinates, tolerance);
}
public void assertTolerance(double[] ordinates, double tolerance) {
// force counter-clockwise order to simplify the testing logic
if (clockwise(ordinates)) {
for (int i = 0; i < ordinates.length / 2; i++) {
double temp = ordinates[i];
ordinates[i] = ordinates[ordinates.length - i - 1];
ordinates[ordinates.length - i - 1] = temp;
}
}
double prevAngle = 0;
for (int i = 0; i < ordinates.length; i += 2) {
// check the point is on the circle
double x = ordinates[i];
double y = ordinates[i + 1];
double dx = x - cx;
double dy = y - cy;
double d = Math.sqrt(dx * dx + dy * dy);
double distanceFromCircle = Math.abs(radius - d);
if (distanceFromCircle > tolerance) {
fail("Found a point " + x + "," + y + " that's not on the circle with distance "
+ distanceFromCircle + " from it");
}
assertEquals(radius, d, tolerance);
double angle = atan2(dy, dx);
if (i > 1) {
double chordAngle = angle - prevAngle;
if (chordAngle < 0) {
chordAngle += Math.PI * 2;
} else if (chordAngle > Math.PI) {
chordAngle = Math.PI * 2 - chordAngle;
}
double halfChordLength = radius * Math.sin(chordAngle / 2);
double apothem = Math.sqrt(radius * radius - halfChordLength * halfChordLength);
double distance = radius - apothem;
if (distance > tolerance) {
fail("Max tolerance is " + tolerance + " but found a chord that is at "
+ distance + " from the circle");
}
}
prevAngle = angle;
}
}
boolean clockwise(double[] ordinates) {
double sa = atan2(ordinates[1] - cx, ordinates[0] - cy);
double ma = atan2(ordinates[3] - cx, ordinates[2] - cy);
double ea = atan2(ordinates[5] - cx, ordinates[3] - cy);
return (sa > ma && ma > ea) || (sa < ma && sa > ea) || (ma < ea && sa > ea);
}
}