/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.visualization.hull;
import java.util.Arrays;
import java.util.Comparator;
import org.gephi.graph.api.Node;
/**
*
* @author Mathieu Bastian
*/
public class AlgoHull {
private static final Comparator<Node> LEFT_RIGHT = new Comparator<Node>() {
public int compare(Node p1, Node p2) {
if (p1.getNodeData().x() < p2.getNodeData().x()) {
return -1;
}
if (p1.getNodeData().x() > p2.getNodeData().x()) {
return +1;
}
if (p1.getNodeData().y() < p2.getNodeData().y()) {
return -1;
}
if (p1.getNodeData().y() > p2.getNodeData().y()) {
return +1;
}
return 0;
}
};
/**
* Returns true if the three points form a real right turn (i.e. they do not
* form a left turn, nor are they collinear).
* <p>
* May return an incorrect result if the points are very close to each other
* or if one point lies very close to the line described by the other two.
*/
private static boolean isRightTurn(Node a, Node b, Node c) {
double det = b.getNodeData().x() * c.getNodeData().y() + a.getNodeData().x() * b.getNodeData().y() + a.getNodeData().y() * c.getNodeData().x();
det -= b.getNodeData().y() * c.getNodeData().x() + a.getNodeData().x() * c.getNodeData().y() + a.getNodeData().y() * b.getNodeData().x();
return det < 0;
}
public static Node[] calculate(Node[] points) {
Node[] temp = new Node[points.length];
System.arraycopy(points, 0, temp, 0, points.length);
Arrays.sort(temp, LEFT_RIGHT);
if (points.length < 3) {
return temp;
}
Node[] upperHull = new Node[temp.length];
int ucount = 0;
upperHull[ucount++] = temp[0];
upperHull[ucount++] = temp[1];
for (int i = 2; i < temp.length; i++) {
while ((ucount >= 2) && !isRightTurn(upperHull[ucount - 2], upperHull[ucount - 1], temp[i])) {
ucount--;
}
upperHull[ucount++] = temp[i];
}
Node[] lowerHull = new Node[temp.length];
int lcount = 0;
lowerHull[lcount++] = temp[temp.length - 1];
lowerHull[lcount++] = temp[temp.length - 2];
for (int i = temp.length - 3; i >= 0; i--) {
while ((lcount >= 2) && !isRightTurn(lowerHull[lcount - 2], lowerHull[lcount - 1], temp[i])) {
lcount--;
}
lowerHull[lcount++] = temp[i];
}
for (int i = 1; i < lcount - 1; i++) {
upperHull[ucount++] = lowerHull[i];
}
Node[] result = new Node[ucount];
for (int i = 0; i < ucount; i++) {
result[i] = upperHull[i];
}
return result;
}
}