/*
* Copyright (c) 2010, Frederik Vanhoutte 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; either version
* 2.1 of the License, or (at your option) any later version.
* http://creativecommons.org/licenses/LGPL/2.1/ 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. You should have
* received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
* Fifth Floor, Boston, MA 02110-1301 USA
*/
package wblut.geom;
import java.util.List;
import wblut.WB_Epsilon;
import javolution.util.FastList;
// TODO: Auto-generated Javadoc
/**
* Planar polygon class.
*/
public class WB_IndexedPolygon implements WB_Polygon {
/** Ordered array of WB_Point. */
private WB_Point3d[] allpoints;
/** The indices. */
private int[] indices;
/** Number of points. */
public int n;
/** Stored plane of polygon. */
private WB_Plane P;
/** Status of stored plane. */
private boolean updated;
/**
* Instantiates a new WB_Polygon.
*/
public WB_IndexedPolygon() {
allpoints = new WB_Point3d[0];
indices = new int[0];
n = 0;
updated = false;
}
/**
* Instantiates a new WB_Polygon.
*
* @param points array of WB_Point, no copies are made
* @param indices the indices
* @param n number of points
*/
public WB_IndexedPolygon(final WB_Point3d[] points, final int[] indices,
final int n) {
allpoints = points;
this.indices = indices;
this.n = n;
P = getPlane();
updated = true;
}
/**
* Set polygon.
*
* @param points array of WB_Point, no copies are made
* @param indices the indices
* @param n number of points
*/
public void set(final WB_Point3d[] points, final int[] indices, final int n) {
allpoints = points;
this.indices = indices;
this.n = n;
P = getPlane();
updated = true;
}
/**
* Set polygon.
*
* @param poly source polygon, no copies are made
*/
public void set(final WB_Polygon poly) {
allpoints = new WB_Point3d[n];
indices = new int[n];
for (int i = 0; i < n; i++) {
allpoints[i] = poly.getPoint(i);
indices[i] = poly.getIndex(i);
}
P = getPlane();
}
/**
* Get copy.
*
* @return copy
*/
public WB_IndexedPolygon get() {
final int[] cindices = new int[n];
for (int i = 0; i < n; i++) {
cindices[i] = indices[i];
}
return new WB_IndexedPolygon(allpoints, cindices, n);
}
/**
* Closest point on polygon to given point.
*
* @param p point
* @return closest point of polygon
*/
public WB_Point3d closestPoint(final WB_Point3d p) {
double d = Double.POSITIVE_INFINITY;
int id = -1;
for (int i = 0; i < n; i++) {
final double cd = WB_Distance.sqDistance(p, allpoints[indices[i]]);
if (cd < d) {
id = indices[i];
d = cd;
}
}
return allpoints[id];
}
/**
* Index of closest point on polygon to given point.
*
* @param p point
* @return index of closest point of polygon
*/
public int closestIndex(final WB_Point3d p) {
double d = Double.POSITIVE_INFINITY;
int id = -1;
for (int i = 0; i < n; i++) {
final double cd = WB_Distance.sqDistance(p, allpoints[indices[i]]);
if (cd < d) {
id = indices[i];
d = cd;
}
}
return id;
}
/**
* Plane of polygon.
*
* @return plane
*/
public WB_Plane getPlane() {
if (updated) {
return P;
}
final WB_Normal3d normal = new WB_Normal3d();
final WB_Point3d center = new WB_Point3d();
WB_Point3d p0;
WB_Point3d p1;
for (int i = 0, j = n - 1; i < n; j = i, i++) {
p0 = allpoints[indices[j]];
p1 = allpoints[indices[i]];
normal.x += (p0.y - p1.y) * (p0.z + p1.z);
normal.y += (p0.z - p1.z) * (p0.x + p1.x);
normal.z += (p0.x - p1.x) * (p0.y + p1.y);
center.add(p1);
}
normal.normalize();
center.div(n);
P = new WB_Plane(center, normal);
updated = true;
return P;
}
/**
* Checks if point at index is convex.
*
* @param i index
* @return WB.VertexType.FLAT,WB.VertexType.CONVEX,WB.VertexType.CONCAVE
*/
public WB_VertexType2D isConvex(final int i) {
final WB_Vector3d vp = allpoints[(i == 0) ? indices[n - 1]
: indices[i - 1]].subToVector(allpoints[indices[i]]);
vp.normalize();
final WB_Vector3d vn = allpoints[(i == n - 1) ? indices[0]
: indices[i + 1]].subToVector(allpoints[indices[i]]);
vn.normalize();
final double cross = vp.cross(vn).mag2();
if (WB_Epsilon.isZeroSq(cross)) {
return WB_VertexType2D.FLAT;
} else if (Math.acos(vp.dot(vn)) < Math.PI) {
return WB_VertexType2D.CONVEX;
} else {
return WB_VertexType2D.CONCAVE;
}
}
/**
* Triangulate polygon.
*
* @return arrayList of WB_IndexedTriangle, points are not copied
*/
public List<WB_IndexedTriangle> triangulate() {
final List<WB_IndexedTriangle> tris = new FastList<WB_IndexedTriangle>();
final WB_Polygon2D tmp = toPolygon2D();
final List<WB_IndexedTriangle2D> tris2d = tmp.indexedTriangulate();
WB_IndexedTriangle2D tri2d;
for (int i = 0; i < tris2d.size(); i++) {
tri2d = tris2d.get(i);
tris.add(new WB_IndexedTriangle(tri2d.i1, tri2d.i2, tri2d.i3,
allpoints));
}
return tris;
}
/**
* Removes point.
*
* @param i index of point to remove
* @return new WB_Polygon with point removed
*/
public WB_IndexedPolygon removePoint(final int i) {
final int[] newindices = new int[n - 1];
for (int j = 0; j < i; j++) {
newindices[j] = indices[j];
}
for (int j = i; j < n - 1; j++) {
newindices[j] = indices[j + 1];
}
return new WB_IndexedPolygon(allpoints, newindices, n - 1);
}
/**
* Removes the point self.
*
* @param i the i
*/
public void removePointSelf(final int i) {
final int[] newindices = new int[n - 1];
for (int j = 0; j < i; j++) {
newindices[j] = indices[j];
}
for (int j = i; j < n - 1; j++) {
newindices[j] = indices[j + 1];
}
set(allpoints, newindices, n - 1);
}
/**
* Adds point.
*
* @param i index to put point
* @param id the id
* @return new WB_Polygon with point added
*/
public WB_IndexedPolygon addPoint(final int i, final int id) {
final int[] newindices = new int[n + 1];
for (int j = 0; j < i; j++) {
newindices[j] = indices[j];
}
newindices[i] = id;
for (int j = i + 1; j < n + 1; j++) {
newindices[j] = indices[j - 1];
}
return new WB_IndexedPolygon(allpoints, newindices, n + 1);
}
/**
* Adds the point self.
*
* @param i the i
* @param id the id
*/
public void addPointSelf(final int i, final int id) {
final int[] newindices = new int[n + 1];
for (int j = 0; j < i; j++) {
newindices[j] = indices[j];
}
newindices[i] = id;
for (int j = i + 1; j < n + 1; j++) {
newindices[j] = indices[j - 1];
}
set(allpoints, newindices, n + 1);
}
/* (non-Javadoc)
* @see wblut.geom.WB_Polygon#getSegments()
*/
public List<WB_IndexedSegment> getSegments() {
final List<WB_IndexedSegment> segments = new FastList<WB_IndexedSegment>(
n);
for (int i = 0, j = n - 1; i < n; j = i, i++) {
segments.add(new WB_IndexedSegment(i, j, allpoints));
}
return segments;
}
/**
* Extract polygons.
*
* @param segs the segs
* @param points the points
* @return the list
*/
public static List<WB_IndexedPolygon> extractPolygons(
final List<WB_IndexedSegment> segs, final WB_Point3d[] points) {
final List<WB_IndexedPolygon> result = new FastList<WB_IndexedPolygon>();
final List<WB_IndexedSegment> leftovers = new FastList<WB_IndexedSegment>();
final List<WB_IndexedSegment> cleanedsegs = clean(segs, points);
leftovers.addAll(cleanedsegs);
while (leftovers.size() > 0) {
final List<WB_IndexedSegment> currentPolygon = new FastList<WB_IndexedSegment>();
final boolean loopFound = tryToFindLoop(leftovers, currentPolygon,
points);
if (loopFound) {
final int[] indices = new int[currentPolygon.size()];
for (int i = 0; i < currentPolygon.size(); i++) {
indices[i] = currentPolygon.get(i).i1();
}
if (currentPolygon.size() > 2) {
final WB_IndexedPolygon poly = new WB_IndexedPolygon(
points, indices, currentPolygon.size());
result.add(poly);
}
}
leftovers.removeAll(currentPolygon);
}
return result;
}
/**
* Clean.
*
* @param segs the segs
* @param points the points
* @return the list
*/
public static List<WB_IndexedSegment> clean(
final List<WB_IndexedSegment> segs, final WB_Point3d[] points) {
final List<WB_IndexedSegment> cleanedsegs = new FastList<WB_IndexedSegment>();
final WB_KDTree3Dold<Integer> tree = new WB_KDTree3Dold<Integer>();
int i = 0;
for (i = 0; i < segs.size(); i++) {
if (!WB_Epsilon.isZeroSq(WB_Distance.sqDistance(segs.get(i)
.getOrigin(), segs.get(i).getEnd()))) {
tree.put(segs.get(i).getOrigin(), segs.get(i).i1());
tree.put(segs.get(i).getEnd(), segs.get(i).i2());
cleanedsegs.add(new WB_IndexedSegment(segs.get(i).i1(), segs
.get(i).i2(), points));
break;
}
}
for (; i < segs.size(); i++) {
if (!WB_Epsilon.isZeroSq(WB_Distance.sqDistance(segs.get(i)
.getOrigin(), segs.get(i).getEnd()))) {
WB_Point3d origin = segs.get(i).getOrigin();
WB_Point3d end = segs.get(i).getEnd();
int i1 = segs.get(i).i1();
int i2 = segs.get(i).i2();
WB_KDNeighbor<Integer>[] nn = tree.getNearestNeighbors(origin,
1);
if (WB_Epsilon.isZeroSq(nn[0].sqDistance())) {
origin = nn[0].point();
i1 = nn[0].value();
} else {
tree.put(segs.get(i).getOrigin(), segs.get(i).i1());
}
nn = tree.getNearestNeighbors(end, 1);
if (WB_Epsilon.isZeroSq(nn[0].sqDistance())) {
end = nn[0].point();
i2 = nn[0].value();
} else {
tree.put(segs.get(i).getEnd(), segs.get(i).i2());
}
cleanedsegs.add(new WB_IndexedSegment(i1, i2, points));
}
}
return cleanedsegs;
}
/**
* Try to find loop.
*
* @param segs the segs
* @param loop the loop
* @param points the points
* @return true, if successful
*/
private static boolean tryToFindLoop(final List<WB_IndexedSegment> segs,
final List<WB_IndexedSegment> loop, final WB_Point3d[] points) {
final List<WB_IndexedSegment> localSegs = new FastList<WB_IndexedSegment>();
localSegs.addAll(segs);
WB_IndexedSegment start = localSegs.get(0);
loop.add(localSegs.get(0));
boolean found = false;
do {
found = false;
for (int i = 0; i < localSegs.size(); i++) {
if (localSegs.get(i).i1() == start.i2()) {
start = localSegs.get(i);
loop.add(localSegs.get(i));
found = true;
break;
}
}
if (found) {
localSegs.remove(start);
}
} while ((start != segs.get(0)) && found);
if ((loop.size() > 0) && (start == segs.get(0))) {
return true;
}
return false;
}
/* (non-Javadoc)
* @see wblut.geom.WB_Polygon#toPolygon2D()
*/
public WB_Polygon2D toPolygon2D() {
final WB_Point2d[] lpoints = new WB_Point2d[n];
for (int i = 0; i < n; i++) {
lpoints[i] = P.localPoint2D(getPoint(i));
}
return new WB_Polygon2D(lpoints, n);
}
/*
* (non-Javadoc)
* @see wblut.geom.WB_Polygon#getN()
*/
public int getN() {
return n;
}
/*
* (non-Javadoc)
* @see wblut.geom.WB_Polygon#getPoint(int)
*/
public WB_Point3d getPoint(final int i) {
return allpoints[indices[i]];
}
/* (non-Javadoc)
* @see wblut.geom.WB_Polygon#getIndex(int)
*/
public int getIndex(final int i) {
return indices[i];
}
/*
* (non-Javadoc)
* @see wblut.geom.WB_Polygon#getPoints()
*/
public WB_Point3d[] getPoints() {
return allpoints;
}
/**
* Gets the indices.
*
* @return the indices
*/
public int[] getIndices() {
return indices;
}
}