Back to project page flingbox.
The source code is released under:
GNU General Public License
If you think the Android project flingbox listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * Flingbox - An OpenSource physics sandbox for Google's Android * Copyright (C) 2009 Jon Ander Pealba & Endika Gutirrez */*from ww w . j av a 2 s. c o m*/ * 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 edu.eside.flingbox.math; import java.util.ArrayList; /** * Implements some utilities for polygons. */ public final class PolygonUtils { /** * Prevent instance creation */ private PolygonUtils() { } /** * The Douglas-Peucker algorithm is an algorithm for reducing the number * of points in a curve that is approximated by a series of points. * The end of this function is a good moment to call the GarbageCollector * * @param points Array of the polygon's points * @param epsilon Max distance to ignore a point * @return New array with optimized points */ public static Vector2D[] douglasPeuckerReducer(final Vector2D[] points, final float epsilon) { final int lastPoint = points.length -1; if (lastPoint < 3 || epsilon <= 0.0f) return points; // No reduction possible final ArrayList<Vector2D> reducedPolygon = new ArrayList<Vector2D>(); reducedPolygon.add(points[0]); // First point will not be include /* Call recursively to algorithm */ douglasPeucker(points, epsilon, 0, lastPoint, reducedPolygon); if (points[0].distanceToPoint(points[lastPoint]) > epsilon) reducedPolygon.add(points[lastPoint]); // Last point neither reducedPolygon.trimToSize(); return (Vector2D[]) reducedPolygon.toArray(new Vector2D[0]); } /** * Recursively Calculation of DouglasPeucker Algorithm */ private static void douglasPeucker(final Vector2D[] points, final float epsilon, final int first, final int last, final ArrayList<Vector2D> resultPoints) { float maxDistance = 0.0f; int maxDistanceIndex = 0; /* Find maximum distance point. */ for (int i = first + 1; i < last ; i++) { float distance = distanceFromLineToPoint(points[first], points[last], points[i]); if (distance > maxDistance) { // Store point maxDistance = distance; maxDistanceIndex = i; } } /* If point distance is more than epsilon then split points array in * two parts and iterate for each. */ if (maxDistance > epsilon) { /* Find in previous segment */ if ((maxDistanceIndex - first) > 1) douglasPeucker(points, epsilon, first, maxDistanceIndex, resultPoints); /* Put point in buffer(2 coords) */ resultPoints.add(points[maxDistanceIndex]); /* Continue searching important points */ if ((last - maxDistanceIndex) > 1) douglasPeucker(points, epsilon, maxDistanceIndex, last, resultPoints); } } /** * Computes the triangulation of a polygon(tesellation) with ear-clipping * algorithm. * @param Vector2Ds Array of polygon's points * @return Will return n-2 group of 3 points, for n sides polygon * or null if not enough points */ public static short[] triangulatePolygon(final Vector2D[] Vector2Ds) { final int Vector2DsCount = Vector2Ds.length; if (Vector2DsCount < 3) return null; // n-2 group of 3 Vector2Ds, for n sides polygon short[] triangles = new short[3 * (Vector2DsCount - 2)]; boolean[] included = new boolean[Vector2DsCount]; int topVector2DIndex; float topVector2D; for (int trianglesCount = 0; trianglesCount < (Vector2DsCount - 3); ++trianglesCount) { topVector2DIndex = 0; topVector2D = Float.NEGATIVE_INFINITY; // Find top Vector2D to find triangle for (int i = 0; i < Vector2DsCount; i++) { // Find top Vector2D if (!included[i] && (Vector2Ds[i].i > topVector2D)) { topVector2D = Vector2Ds[i].i; topVector2DIndex = i; } } // Exclude Vector2D for next iteration included[topVector2DIndex] = true; // Save triangle int prevVector2D = topVector2DIndex; // Find previous Vector2D do { if (--prevVector2D < 0) prevVector2D = Vector2DsCount - 1; } while (included[prevVector2D]); int nextVector2D = topVector2DIndex; // Find next Vector2D do { if (++nextVector2D >= Vector2DsCount) nextVector2D = 0; } while (included[nextVector2D]); // Store triangle triangles[trianglesCount * 3] = (short)prevVector2D; // Store into array triangles[trianglesCount * 3 + 1] = (short)topVector2DIndex; triangles[trianglesCount * 3 + 2] = (short)nextVector2D; } return triangles; } /** * Checks if a Vector2D is contained by a polygon. * It's based on Winding number algorithm. * More info at {@link http://en.wikipedia.org/wiki/Winding_number} * * @param polygon polygon's Vector2Ds * @param Vector2D Vector2D to be checked */ public static boolean polygonConatinsPoint(Vector2D[] polygon, Vector2D Vector2D) { final int Vector2DsCount = polygon.length; final float px = Vector2D.i, py = Vector2D.j; int c = 0; for (int i = 0; i < Vector2DsCount; i++) { Vector2D v1 = polygon[i]; Vector2D v2 = polygon[(i + 1) % Vector2DsCount] ; if ((v1.j < py) && (v2.j > py)) { if (v1.i > px || v2.i > px) c++; // Check if Vector2D is at the left or the right side of the object //final float segmentAtX = ((v2.i - v1.i) / (v2.j - v1.j)) * (py - v1.j) + v1.i; //if (segmentAtX > px) // c++; //else if (segmentAtX == px) // return true; // is over the bounder } else if ((v1.j > py) && (v2.j < py)) { if (v1.i > px || v2.i > px) c--; // Check if Vector2D is at the left or the right side of the object //final float segmentAtX = ((v2.i - v1.i) / (v2.j - v1.j)) * (py - v1.j) + v1.i; //if (segmentAtX > px) // c--; //else if (segmentAtX == px) // return true; // is over the bounder } } return c != 0; } /** * Computes area of polygon. * * @param Vector2Ds Polygon's Vector2Ds * @return Polygon's area. if Vector2Ds are counter-clockwise the * result will be positive, else it'll be negative */ public static float polygonArea(final Vector2D[] Vector2Ds) { final int lastVector2D = Vector2Ds.length - 1; float area = Vector2Ds[lastVector2D].i * Vector2Ds[0].j - Vector2Ds[0].i * Vector2Ds[lastVector2D].j; Vector2D Vector2D, nextVector2D; for (int i = 0; i < lastVector2D; i++) { Vector2D = Vector2Ds[i]; nextVector2D = Vector2Ds[i + 1]; area += Vector2D.i * nextVector2D.j - nextVector2D.i * Vector2D.j; } return area / 2f; } /** * Computes the polygon's centroid * * @param Vector2Ds Polygon's Vector2Ds * @return centroid */ public static Vector2D polygonCentroid(final Vector2D[] contour) { final int pointsCount = contour.length; float cx = 0f, cy = 0f; for (int i = 0; i < pointsCount; i++) { float p0x = contour[i].i, p1x = contour[(i + 1) % pointsCount].i; float p0y = contour[i].j, p1y = contour[(i + 1) % pointsCount].j; final float k = (p0x * p1y - p1x * p0y); cx += (p0x + p1x) * k; cy += (p0y + p1y) * k; } final float d = 6f * polygonArea(contour); cx /= d; cy /= d; return new Vector2D(cx, cy); } /** * Computes the polygon's normals. * * @param contour Counterclockwise polygon points * @return Polygon's normals */ public static Vector2D[] computePolygonNormals(final Vector2D[] contour) { final int pointsCount = contour.length; Vector2D[] normals = new Vector2D[pointsCount]; for (int i = 0; i < pointsCount; i++) { final Vector2D p0 = contour[i], p1 = contour[i == pointsCount - 1 ? 0 : i ]; normals[i] = new Vector2D((p1.j - p0.j), (p0.i - p1.i));//.normalize(); } return normals; } /** * Computes minimum distance from line to point */ public static float distanceFromLineToPoint(final Vector2D p0, final Vector2D p1, final Vector2D p) { final float area = (p0.i * p1.j + p1.i * p.j + p.i * p0.j - p1.i * p0.j - p.i * p1.j - p0.i * p.j) / 2f; final float base = (float) Math.sqrt((p1.i - p0.i) * (p1.i - p0.i) + (p1.j - p0.j) * (p1.j - p0.j)); return (float) Math.abs(2f * area / base); } }