/*
* Copyright (C) 2014 Alfons Wirtz
* website www.freerouting.net
*
* 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 at <http://www.gnu.org/licenses/>
* for more details.
*/
package geometry.planar;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
/**
*
* A Polygon is a list of points in the plane, where no 2 consecutive
* points may be equal and no 3 consecutive points collinear.
*
* @author Alfons Wirtz#
*/
public class Polygon implements java.io.Serializable
{
/**
* Creates a polygon from p_point_arr.
* Multiple points and points, which are collinear with its previous
* and next point will be removed.
*/
public Polygon(Point [] p_point_arr)
{
corners = new LinkedList<Point>();
if (p_point_arr.length == 0)
{
return;
}
for (int i = 0; i < p_point_arr.length; ++i)
{
corners.add(p_point_arr[i]);
}
boolean corner_removed = true;
while (corner_removed)
{
corner_removed = false;
// remove multiple points
if (corners.isEmpty())
{
return;
}
Iterator<Point> i = corners.iterator();
Point curr_ob = i.next();
while(i.hasNext())
{
Point next_ob = i.next();
if(next_ob.equals(curr_ob))
{
i.remove();
corner_removed = true;
}
else
{
curr_ob = next_ob;
}
}
// remove points which are collinear with the previous
// and next point.
i = corners.iterator();
Point prev = i.next();
Iterator<Point> prev_i = corners.iterator();
if (!i.hasNext())
{
continue;
}
Point curr = i.next();
prev_i.next();
while(i.hasNext())
{
Point next = i.next();
prev_i.next();
if(curr.side_of(prev, next) == Side.COLLINEAR)
{
prev_i.remove();
corner_removed = true;
break;
}
prev = curr;
curr = next;
}
}
}
/**
* returns the array of corners of this polygon
*/
public Point[] corner_array()
{
int corner_count = corners.size();
Point[] result = new Point[corner_count];
Iterator<Point> it = corners.iterator();
for (int i = 0; i < corner_count; ++i)
{
result[i] = it.next();
}
return result;
}
/**
* Reverts the order of the corners of this polygon.
*/
public Polygon revert_corners()
{
Point [] corner_arr = corner_array();
Point [] reverse_corner_arr = new Point[corner_arr.length];
for (int i = 0; i < corner_arr.length; ++i)
{
reverse_corner_arr[i] = corner_arr [corner_arr.length - i - 1];
}
return new Polygon(reverse_corner_arr);
}
/**
* Returns the winding number of this polygon, treated as closed.
* It will be > 0, if the corners are in countercock sense,
* and < 0, if the corners are in clockwise sense.
*/
public int winding_number_after_closing()
{
Point [] corner_arr = corner_array();
if (corner_arr.length < 2)
{
return 0;
}
Vector first_side_vector = corner_arr[1].difference_by(corner_arr[0]);
Vector prev_side_vector = first_side_vector;
int corner_count = corner_arr.length;
// Skip the last corner, if it is equal to the first corner.
if (corner_arr[0].equals(corner_arr[corner_count - 1]))
{
--corner_count;
}
double angle_sum = 0;
for (int i = 1; i <= corner_count; ++i)
{
Vector next_side_vector;
if (i == corner_count - 1)
{
next_side_vector = corner_arr[0].difference_by(corner_arr[i]);
}
else if (i == corner_count)
{
next_side_vector = first_side_vector;
}
else
{
next_side_vector = corner_arr[i + 1].difference_by(corner_arr[i]);
}
angle_sum += prev_side_vector.angle_approx(next_side_vector);
prev_side_vector = next_side_vector;
}
angle_sum /= 2.0 * Math.PI;
if (Math.abs(angle_sum) < 0.5)
{
System.out.println ("Polygon.winding_number_after_closing: winding number != 0 expected");
}
return (int) Math.round(angle_sum);
}
private final Collection<Point> corners;
}