/*
* Copyright (C) 2012 Dr. John Lindsay <jlindsay@uoguelph.ca>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package whitebox.utilities;
import com.vividsolutions.jts.geom.Coordinate;
/**
*
* @author Dr. John Lindsay email: jlindsay@uoguelph.ca
*/
public class Topology {
public static boolean isLineClosed(Coordinate[] coords) {
int lastIndex = coords.length - 1;
return (coords[0].x == coords[lastIndex].x) &
(coords[0].y == coords[lastIndex].y);
}
public static boolean isClockwisePolygon(double[][] coords) {
// Note: holes are polygons that have verticies in counter-clockwise order
// This approach is based on the method described by Paul Bourke, March 1998
// http://paulbourke.net/geometry/clockwise/index.html
double x0, y0, x1, y1, x2, y2;
int n1 = 0, n2 = 0, n3 = 0;
int numPoints = coords.length;
// are the first and last points the same? If so, remove the last point.
if ((coords[0][0] == coords[numPoints - 1][0]) && (coords[0][1] == coords[numPoints - 1][1])) {
double[][] coords2 = new double[numPoints - 1][2];
for (int p = 0; p < numPoints - 1; p++) {
coords2[p][0] = coords[p][0];
coords2[p][1] = coords[p][1];
}
numPoints--;
coords = new double[numPoints][2];
for (int p = 0; p < numPoints; p++) {
coords[p][0] = coords2[p][0];
coords[p][1] = coords2[p][1];
}
}
if (numPoints < 3) {
throw new IllegalArgumentException("Degenerate polygon with less than three points.");
}
// first see if it is a convex or concave polygon
// calculate the cross product for each adjacent edge.
double[] crossproducts = new double[numPoints];
for (int j = 0; j < numPoints; j++) {
n2 = j;
if (j == 0) {
n1 = numPoints - 1;
n3 = j + 1;
} else if (j == numPoints - 1) {
n1 = j - 1;
n3 = 0;
} else {
n1 = j - 1;
n3 = j + 1;
}
x0 = coords[n1][0];
y0 = coords[n1][1];
x1 = coords[n2][0];
y1 = coords[n2][1];
x2 = coords[n3][0];
y2 = coords[n3][1];
crossproducts[j] = (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1);
}
boolean testSign;
if (crossproducts[0] >= 0) {
testSign = true; // positive
} else {
testSign = false; // negative
}
boolean isConvex = true;
for (int j = 1; j < numPoints; j++) {
if (crossproducts[j] >= 0 && !testSign) {
isConvex = false;
break;
} else if (crossproducts[j] < 0 && testSign) {
isConvex = false;
break;
}
}
// now see if it is clockwise or counter-clockwise
if (isConvex) {
if (testSign) { // positive means counter-clockwise
return false;
} else {
return true;
}
} else {
// calculate the polygon area. If it is positive is is in clockwise order, else counter-clockwise.
double area = 0;
for (int j = 0; j < numPoints; j++) {
n1 = j;
if (j < numPoints - 1) {
n2 = j + 1;
} else {
n2 = 0;
}
x1 = coords[n1][0];
y1 = coords[n1][1];
x2 = coords[n2][0];
y2 = coords[n2][1];
area += (x1 * y2) - (x2 * y1);
}
// if this were the true area, we'd half it, but we're only interested in the sign.
if (area < 0) { // a positive area indicates counter-clockwise order
return true;
} else {
return false;
}
}
}
public static boolean isClockwisePolygon(Coordinate[] coords) {
// Note: holes are polygons that have verticies in counter-clockwise order
// This approach is based on the method described by Paul Bourke, March 1998
// http://paulbourke.net/geometry/clockwise/index.html
double x0, y0, x1, y1, x2, y2;
int n1 = 0, n2 = 0, n3 = 0;
int numPoints = coords.length;
// are the first and last points the same? If so, remove the last point.
if ((coords[0].x == coords[numPoints - 1].x) && (coords[0].y == coords[numPoints - 1].y)) {
Coordinate[] coords2 = new Coordinate[numPoints - 1];
System.arraycopy(coords, 0, coords2, 0, numPoints - 1);
coords = new Coordinate[numPoints - 1];
System.arraycopy(coords2, 0, coords, 0, numPoints - 1);
numPoints--;
}
if (numPoints < 3) {
throw new IllegalArgumentException("Degenerate polygon with less than three points.");
}
// first see if it is a convex or concave polygon
// calculate the cross product for each adjacent edge.
double[] crossproducts = new double[numPoints];
for (int j = 0; j < numPoints; j++) {
n2 = j;
if (j == 0) {
n1 = numPoints - 1;
n3 = j + 1;
} else if (j == numPoints - 1) {
n1 = j - 1;
n3 = 0;
} else {
n1 = j - 1;
n3 = j + 1;
}
x0 = coords[n1].x;
y0 = coords[n1].y;
x1 = coords[n2].x;
y1 = coords[n2].y;
x2 = coords[n3].x;
y2 = coords[n3].y;
crossproducts[j] = (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1);
}
boolean testSign;
if (crossproducts[0] >= 0) {
testSign = true; // positive
} else {
testSign = false; // negative
}
boolean isConvex = true;
for (int j = 1; j < numPoints; j++) {
if (crossproducts[j] >= 0 && !testSign) {
isConvex = false;
break;
} else if (crossproducts[j] < 0 && testSign) {
isConvex = false;
break;
}
}
// now see if it is clockwise or counter-clockwise
if (isConvex) {
if (testSign) { // positive means counter-clockwise
return false;
} else {
return true;
}
} else {
// calculate the polygon area. If it is positive is is in clockwise order, else counter-clockwise.
double area = 0;
for (int j = 0; j < numPoints; j++) {
n1 = j;
if (j < numPoints - 1) {
n2 = j + 1;
} else {
n2 = 0;
}
x1 = coords[n1].x;
y1 = coords[n1].y;
x2 = coords[n2].x;
y2 = coords[n2].y;
area += (x1 * y2) - (x2 * y1);
}
// if this were the true area, we'd half it, but we're only interested in the sign.
if (area < 0) { // a positive area indicates counter-clockwise order
return true;
} else {
return false;
}
}
}
public static boolean isPolygonConvex(Coordinate[] coords) {
// Note: holes are polygons that have verticies in counter-clockwise order
// This approach is based on the method described by Paul Bourke, March 1998
// http://paulbourke.net/geometry/clockwise/index.html
double x0, y0, x1, y1, x2, y2;
int n1 = 0, n2 = 0, n3 = 0;
int numPoints = coords.length;
// are the first and last points the same? If so, remove the last point.
if ((coords[0].x == coords[numPoints - 1].x) && (coords[0].y == coords[numPoints - 1].y)) {
Coordinate[] coords2 = new Coordinate[numPoints - 1];
System.arraycopy(coords, 0, coords2, 0, numPoints - 1);
coords = new Coordinate[numPoints - 1];
System.arraycopy(coords2, 0, coords, 0, numPoints - 1);
numPoints--;
}
if (numPoints < 3) {
throw new IllegalArgumentException("Degenerate polygon with less than three points.");
}
// first see if it is a convex or concave polygon
// calculate the cross product for each adjacent edge.
double[] crossproducts = new double[numPoints];
for (int j = 0; j < numPoints; j++) {
n2 = j;
if (j == 0) {
n1 = numPoints - 1;
n3 = j + 1;
} else if (j == numPoints - 1) {
n1 = j - 1;
n3 = 0;
} else {
n1 = j - 1;
n3 = j + 1;
}
x0 = coords[n1].x;
y0 = coords[n1].y;
x1 = coords[n2].x;
y1 = coords[n2].y;
x2 = coords[n3].x;
y2 = coords[n3].y;
crossproducts[j] = (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1);
}
boolean testSign;
if (crossproducts[0] >= 0) {
testSign = true; // positive
} else {
testSign = false; // negative
}
boolean isConvex = true;
for (int j = 1; j < numPoints; j++) {
if (crossproducts[j] >= 0 && !testSign) {
isConvex = false;
break;
} else if (crossproducts[j] < 0 && testSign) {
isConvex = false;
break;
}
}
return isConvex;
}
}