//The MIT License
//
//Copyright (c) 2009 nodchip
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
package tv.dyndns.kishibe.qmaclone.client.geom;
import java.util.ArrayList;
import java.util.List;
import tv.dyndns.kishibe.qmaclone.client.constant.Constant;
public class Polygon extends ArrayList<Point> implements Cloneable {
private static final double EPS = 1e-5;
public static Polygon fromString(String s) throws PolygonException {
List<Integer> values = new ArrayList<Integer>();
for (String value : s.split(" ")) {
int number;
try {
number = Integer.valueOf(value);
} catch (NumberFormatException e) {
throw new PolygonException("数字以外の文字が入力されました: " + s, e);
}
values.add(number);
if (values.size() > Constant.MAX_NUMBER_OF_POLYGON_VERTICES * 2) {
throw new PolygonException("ポリゴンの頂点数が多すぎます: " + s);
}
}
Polygon polygon = new Polygon();
for (int i = 0; i < values.size() / 2; ++i) {
polygon.add(new Point(values.get(i * 2), values.get(i * 2 + 1)));
}
if (polygon.size() < 3) {
throw new PolygonException("ポリゴンの頂点数が足りません: " + s);
}
if (polygon.hasSelfIntersecting()) {
throw new PolygonException("ポリゴンが自己交差しています: " + s);
}
return polygon;
}
public String toString() {
StringBuilder buffer = new StringBuilder();
for (Point point : this) {
if (buffer.length() != 0) {
buffer.append(" ");
}
buffer.append(point);
}
return buffer.toString();
}
private double amplitude(Point a, Point b, Point c) {
double x0 = b.x - a.x;
double y0 = b.y - a.y;
double x1 = c.x - a.x;
double y1 = c.y - a.y;
double result = Math.atan2(y1, x1) - Math.atan2(y0, x0);
while (result < -Math.PI) {
result += 2.0 * Math.PI;
}
while (result > Math.PI) {
result -= 2.0 * Math.PI;
}
return result;
}
public boolean contains(Point point) {
double argSum = 0;
for (int i = 0; i < size(); ++i) {
Point current = get(i);
Point next = get((i + 1) % size());
if (new Segment(current, next).on(point)) {
return true;
}
argSum += amplitude(point, current, next);
}
return Math.abs(argSum) > EPS;
}
public boolean isCompleted() {
return 3 <= size() && !hasSelfIntersecting();
}
private boolean hasSelfIntersecting() {
int n = size();
for (int i = 0; i < n; ++i) {
Segment currentSegment = new Segment(get(i), get((i + 1) % n));
for (int j = 0; j < n; ++j) {
if (i == j || i == (j + 1) % n || (i + 1) % n == j) {
continue;
}
Segment segment = new Segment(get(j), get((j + 1) % n));
if (segment.cross(currentSegment)) {
return true;
}
}
}
return false;
}
@Override
public Polygon clone() {
// PointはImmutableなので、Listのclone()を行うだけで良い
return (Polygon) super.clone();
}
}