Package toxi.geom

Source Code of toxi.geom.Polygon2D

/*
*   __               .__       .__  ._____.          
* _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
* \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
*  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \
*  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
*                   \/       \/             \/     \/
*
* Copyright (c) 2006-2011 Karsten Schmidt
*
* 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 toxi.geom;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;

import toxi.geom.Line2D.LineIntersection;
import toxi.geom.Line2D.LineIntersection.Type;
import toxi.geom.mesh.Mesh3D;
import toxi.geom.mesh.TriangleMesh;
import toxi.math.MathUtils;

/**
* Container type for convex polygons. Implements {@link Shape2D}.
*/
public class Polygon2D implements Shape2D, Iterable<Vec2D> {

    /**
     * Constructs a new regular polygon from the given base line/edge.
     *
     * @param baseA
     *            left point of the base edge
     * @param baseB
     *            right point of the base edge
     * @param res
     *            number of polygon vertices
     * @return polygon
     */
    public static Polygon2D fromBaseEdge(Vec2D baseA, Vec2D baseB, int res) {
        float theta = -(MathUtils.PI - (MathUtils.PI * (res - 2) / res));
        Vec2D dir = baseB.sub(baseA);
        Vec2D prev = baseB;
        Polygon2D poly = new Polygon2D(baseA, baseB);
        for (int i = 1; i < res - 1; i++) {
            Vec2D p = prev.add(dir.getRotated(theta * i));
            poly.add(p);
            prev = p;
        }
        return poly;
    }

    /**
     * Constructs a regular polygon from the given edge length and number of
     * vertices. This automatically computes the radius of the circle the
     * polygon is inscribed in.
     *
     * <p>
     * More information: http://en.wikipedia.org/wiki/Regular_polygon#Radius
     * </p>
     *
     * @param len
     *            desired edge length
     * @param res
     *            number of vertices
     * @return polygon
     */
    public static Polygon2D fromEdgeLength(float len, int res) {
        return new Circle(getRadiusForEdgeLength(len, res)).toPolygon2D(res);
    }

    /**
     * Computes the radius of the circle the regular polygon with the desired
     * edge length is inscribed in
     *
     * @param len
     *            edge length
     * @param res
     *            number of polygon vertices
     * @return radius
     */
    public static float getRadiusForEdgeLength(float len, int res) {
        return len / (2 * MathUtils.sin(MathUtils.PI / res));
    }

    @XmlElement(name = "v")
    public List<Vec2D> vertices = new ArrayList<Vec2D>();

    public Polygon2D() {
    }

    public Polygon2D(List<Vec2D> points) {
        for (Vec2D p : points) {
            add(p.copy());
        }
    }

    public Polygon2D(Vec2D... points) {
        for (Vec2D p : points) {
            add(p.copy());
        }
    }

    /**
     * Adds a new vertex to the polygon (builder pattern).
     *
     * @param x
     * @param y
     * @return itself
     */
    public Polygon2D add(float x, float y) {
        return add(new Vec2D(x, y));
    }

    /**
     * Adds a new vertex to the polygon (builder pattern).
     *
     * @param p
     *            vertex point to add
     * @return itself
     */
    public Polygon2D add(Vec2D p) {
        if (!vertices.contains(p)) {
            vertices.add(p);
        }
        return this;
    }

    /**
     * Centers the polygon around the world origin (0,0).
     *
     * @return itself
     */
    public Polygon2D center() {
        return center(null);
    }

    /**
     * Centers the polygon so that its new centroid is at the given point.
     *
     * @param origin
     *            new centroid or null to center around (0,0)
     * @return itself
     */
    public Polygon2D center(ReadonlyVec2D origin) {
        Vec2D centroid = getCentroid();
        Vec2D delta = origin != null ? origin.sub(centroid) : centroid.invert();
        for (Vec2D v : vertices) {
            v.addSelf(delta);
        }
        return this;
    }

    public boolean containsPoint(ReadonlyVec2D p) {
        int num = vertices.size();
        int i, j = num - 1;
        boolean oddNodes = false;
        float px = p.x();
        float py = p.y();
        for (i = 0; i < num; i++) {
            Vec2D vi = vertices.get(i);
            Vec2D vj = vertices.get(j);
            if (vi.y < py && vj.y >= py || vj.y < py && vi.y >= py) {
                if (vi.x + (py - vi.y) / (vj.y - vi.y) * (vj.x - vi.x) < px) {
                    oddNodes = !oddNodes;
                }
            }
            j = i;
        }
        return oddNodes;
    }

    public boolean containsPolygon(Polygon2D poly) {
        for (Vec2D p : poly.vertices) {
            if (!containsPoint(p)) {
                return false;
            }
        }
        return true;
    }

    public Polygon2D copy() {
        return new Polygon2D(vertices);
    }

    /**
     * Flips the ordering of the polygon's vertices.
     *
     * @return itself
     */
    public Polygon2D flipVertexOrder() {
        Collections.reverse(vertices);
        return this;
    }

    /**
     * Returns the vertex at the given index. This function follows Python
     * convention, in that if the index is negative, it is considered relative
     * to the list end. Therefore the vertex at index -1 is the last vertex in
     * the list.
     *
     * @param i
     *            index
     * @return vertex
     */
    public Vec2D get(int i) {
        if (i < 0) {
            i += vertices.size();
        }
        return vertices.get(i);
    }

    /**
     * Computes the length of this polygon's apothem. This will only be valid if
     * the polygon is regular. More info: http://en.wikipedia.org/wiki/Apothem
     *
     * @return apothem length
     */
    public float getApothem() {
        return vertices.get(0).interpolateTo(vertices.get(1), 0.5f)
                .distanceTo(getCentroid());
    }

    /**
     * Computes the area of the polygon, provided it isn't self intersecting.
     * Code ported from:
     * http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
     *
     * @return polygon area
     */
    public float getArea() {
        float area = 0;
        for (int i = 0, num = vertices.size(); i < num; i++) {
            Vec2D a = vertices.get(i);
            Vec2D b = vertices.get((i + 1) % num);
            area += a.x * b.y;
            area -= a.y * b.x;
        }
        area *= 0.5f;
        return area;
    }

    public Circle getBoundingCircle() {
        return Circle.newBoundingCircle(vertices);
    }

    /**
     * Returns the polygon's bounding rect.
     *
     * @return bounding rect
     * @see toxi.geom.Shape2D#getBounds()
     */
    public Rect getBounds() {
        return Rect.getBoundingRect(vertices);
    }

    /**
     * Computes the polygon's centre of mass. Code ported from:
     * http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
     *
     * @return centroid point
     */
    public Vec2D getCentroid() {
        Vec2D res = new Vec2D();
        for (int i = 0, num = vertices.size(); i < num; i++) {
            Vec2D a = vertices.get(i);
            Vec2D b = vertices.get((i + 1) % num);
            float crossP = a.x * b.y - b.x * a.y;
            res.x += (a.x + b.x) * crossP;
            res.y += (a.y + b.y) * crossP;
        }
        return res.scale(1f / (6 * getArea()));
    }

    /**
     * Computes the polygon's circumference, the length of its perimeter.
     *
     * @return perimiter length
     *
     * @see toxi.geom.Shape2D#getCircumference()
     */
    public float getCircumference() {
        float circ = 0;
        for (int i = 0, num = vertices.size(); i < num; i++) {
            circ += vertices.get(i).distanceTo(vertices.get((i + 1) % num));
        }
        return circ;
    }

    public Vec2D getClosestPointTo(ReadonlyVec2D p) {
        float minD = Float.MAX_VALUE;
        Vec2D q = null;
        for (Line2D l : getEdges()) {
            Vec2D c = l.closestPointTo(p);
            float d = c.distanceToSquared(p);
            if (d < minD) {
                q = c;
                minD = d;
            }
        }
        return q;
    }

    public Vec2D getClosestVertexTo(ReadonlyVec2D p) {
        float minD = Float.MAX_VALUE;
        Vec2D q = null;
        for (Vec2D v : vertices) {
            float d = v.distanceToSquared(p);
            if (d < minD) {
                q = v;
                minD = d;
            }
        }
        return q;
    }

    /**
     * Returns a list of {@link Line2D} segments representing the polygon edges.
     *
     * @return list of lines
     */
    public List<Line2D> getEdges() {
        int num = vertices.size();
        List<Line2D> edges = new ArrayList<Line2D>(num);
        for (int i = 0; i < num; i++) {
            edges.add(new Line2D(vertices.get(i), vertices.get((i + 1) % num)));
        }
        return edges;
    }

    /**
     * @see #getNumVertices()
     */
    @Deprecated
    public int getNumPoints() {
        return getNumVertices();
    }

    /**
     * Returns the number of polygon vertices.
     *
     * @return vertex count
     */
    public int getNumVertices() {
        return vertices.size();
    }

    /**
     * Creates a random point within the polygon. This is only guaranteed to
     * work with regular polygons.
     *
     * @return Vec2D
     */
    public Vec2D getRandomPoint() {
        List<Line2D> edges = getEdges();
        int numEdges = edges.size();
        Line2D ea = edges.get(MathUtils.random(numEdges));
        Line2D eb = null;
        // and another one, making sure it's different
        while (eb == null || eb.equals(ea)) {
            eb = edges.get(MathUtils.random(numEdges));
        }
        // pick a random point on edge A
        Vec2D p = ea.a.interpolateTo(ea.b, MathUtils.random(1f));
        // then randomly interpolate to another random point on edge B
        return p.interpolateToSelf(
                eb.a.interpolateTo(eb.b, MathUtils.random(1f)),
                MathUtils.random(1f));
    }

    /**
     * Repeatedly inserts vertices as mid points of the longest edges until the
     * new vertex count is reached.
     *
     * @param count
     *            new vertex count
     * @return itself
     */
    public Polygon2D increaseVertexCount(int count) {
        int num = vertices.size();
        while (num < count) {
            // find longest edge
            int longestID = 0;
            float maxD = 0;
            for (int i = 0; i < num; i++) {
                float d = vertices.get(i).distanceToSquared(
                        vertices.get((i + 1) % num));
                if (d > maxD) {
                    longestID = i;
                    maxD = d;
                }
            }
            // insert mid point of longest segment in vertex list
            Vec2D m = vertices.get(longestID)
                    .add(vertices.get((longestID + 1) % num)).scaleSelf(0.5f);
            vertices.add(longestID + 1, m);
            num++;
        }
        return this;
    }

    protected boolean intersectsLine(Line2D l, List<Line2D> edges) {
        for (Line2D e : edges) {
            final Type isec = l.intersectLine(e).getType();
            if (isec == Type.INTERSECTING || isec == Type.COINCIDENT) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if the given polygon intersect this one by checking all edges for
     * line intersections.
     *
     * @param poly
     * @return true, if polygons intersect.
     */
    public boolean intersectsPolygon(Polygon2D poly) {
        List<Line2D> edgesB = poly.getEdges();
        for (Line2D ea : getEdges()) {
            if (intersectsLine(ea, edgesB)) {
                return true;
            }
        }
        return false;
    }

    public boolean intersectsRect(Rect r) {
        List<Line2D> edges = r.getEdges();
        for (Line2D ea : getEdges()) {
            if (intersectsLine(ea, edges)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if the vertices of this polygon are in clockwise ordering by
     * examining all vertices as a sequence of triangles. The test relies on the
     * fact that the {@link #getArea()} method will produce negative results for
     * ant-clockwise ordered polygons.
     *
     * @return true, if clockwise
     */
    public boolean isClockwise() {
        return getArea() > 0;
    }

    /**
     * Checks if the polygon is convex.
     *
     * @return true, if convex.
     */
    public boolean isConvex() {
        boolean isPositive = false;
        int num = vertices.size();
        for (int i = 0; i < num; i++) {
            int prev = (i == 0) ? num - 1 : i - 1;
            int next = (i == num - 1) ? 0 : i + 1;
            Vec2D d0 = vertices.get(i).sub(vertices.get(prev));
            Vec2D d1 = vertices.get(next).sub(vertices.get(i));
            boolean newIsP = (d0.cross(d1) > 0);
            if (i == 0) {
                isPositive = newIsP;
            } else if (isPositive != newIsP) {
                return false;
            }
        }
        return true;
    }

    public Iterator<Vec2D> iterator() {
        return vertices.iterator();
    }

    /**
     * Given the sequentially connected points p1, p2, p3, this function returns
     * a bevel-offset replacement for point p2.
     *
     * Note: If vectors p1->p2 and p2->p3 are exactly 180 degrees opposed, or if
     * either segment is zero then no offset will be applied.
     *
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @param x3
     * @param y3
     * @param distance
     * @param out
     *
     * @see http://alienryderflex.com/polygon_inset/
     */
    protected void offsetCorner(float x1, float y1, float x2, float y2,
            float x3, float y3, float distance, Vec2D out) {

        float c1 = x2, d1 = y2, c2 = x2, d2 = y2;
        float dx1, dy1, dist1, dx2, dy2, dist2, insetX, insetY;

        dx1 = x2 - x1;
        dy1 = y2 - y1;
        dist1 = (float) Math.sqrt(dx1 * dx1 + dy1 * dy1);
        dx2 = x3 - x2;
        dy2 = y3 - y2;
        dist2 = (float) Math.sqrt(dx2 * dx2 + dy2 * dy2);

        if (dist1 < MathUtils.EPS || dist2 < MathUtils.EPS) {
            return;
        }
        dist1 = distance / dist1;
        dist2 = distance / dist2;

        insetX = dy1 * dist1;
        insetY = -dx1 * dist1;
        x1 += insetX;
        c1 += insetX;
        y1 += insetY;
        d1 += insetY;
        insetX = dy2 * dist2;
        insetY = -dx2 * dist2;
        x3 += insetX;
        c2 += insetX;
        y3 += insetY;
        d2 += insetY;

        if (c1 == c2 && d1 == d2) {
            out.set(c1, d1);
            return;
        }

        Line2D l1 = new Line2D(new Vec2D(x1, y1), new Vec2D(c1, d1));
        Line2D l2 = new Line2D(new Vec2D(c2, d2), new Vec2D(x3, y3));
        LineIntersection isec = l1.intersectLine(l2);
        final Vec2D ipos = isec.getPos();
        if (ipos != null) {
            out.set(ipos);
        }
    }

    /**
     * Moves each line segment of the polygon in/outward perpendicular by the
     * given distance. New line segments and polygon vertices are created by
     * computing the intersection points of the displaced segments. Choosing an
     * too large displacement amount will result in deformation/undefined
     * behavior with various self intersections. Should that happen, please try
     * to clean up the shape using the {@link #toOutline()} method.
     *
     * @param distance
     *            offset/inset distance (negative for inset)
     * @return itself
     */
    public Polygon2D offsetShape(float distance) {
        int num = vertices.size() - 1;
        if (num > 1) {
            float startX = vertices.get(0).x;
            float startY = vertices.get(0).y;
            float c = vertices.get(num).x;
            float d = vertices.get(num).y;
            float e = startX;
            float f = startY;
            for (int i = 0; i < num; i++) {
                float a = c;
                float b = d;
                c = e;
                d = f;
                e = vertices.get(i + 1).x;
                f = vertices.get(i + 1).y;
                offsetCorner(a, b, c, d, e, f, distance, vertices.get(i));
            }
            offsetCorner(c, d, e, f, startX, startY, distance,
                    vertices.get(num));
        }
        return this;
    }

    /**
     * Reduces the number of vertices in the polygon based on the given minimum
     * edge length. Only vertices with at least this distance between them will
     * be kept.
     *
     * @param minEdgeLen
     * @return itself
     */
    public Polygon2D reduceVertices(float minEdgeLen) {
        minEdgeLen *= minEdgeLen;
        List<Vec2D> reduced = new ArrayList<Vec2D>();
        Vec2D prev = vertices.get(0);
        reduced.add(prev);
        int num = vertices.size() - 1;
        for (int i = 1; i < num; i++) {
            Vec2D v = vertices.get(i);
            if (prev.distanceToSquared(v) >= minEdgeLen) {
                reduced.add(v);
                prev = v;
            }
        }
        if (vertices.get(0).distanceToSquared(vertices.get(num)) >= minEdgeLen) {
            reduced.add(vertices.get(num));
        }
        vertices = reduced;
        return this;
    }

    /**
     * Removes duplicate vertices from the polygon. Only successive points are
     * recognized as duplicates.
     *
     * @param tolerance
     *            snap distance for finding duplicates
     * @return itself
     */
    public Polygon2D removeDuplicates(float tolerance) {
        Vec2D prev = null;
        for (Iterator<Vec2D> iv = vertices.iterator(); iv.hasNext();) {
            Vec2D p = iv.next();
            if (p.equalsWithTolerance(prev, tolerance)) {
                iv.remove();
            } else {
                prev = p;
            }
        }
        int num = vertices.size();
        if (num > 0) {
            Vec2D last = vertices.get(num - 1);
            if (last.equalsWithTolerance(vertices.get(0), tolerance)) {
                vertices.remove(last);
            }
        }
        return this;
    }

    public Polygon2D rotate(float theta) {
        for (Vec2D v : vertices) {
            v.rotate(theta);
        }
        return this;
    }

    public Polygon2D scale(float scale) {
        return scale(scale, scale);
    }

    public Polygon2D scale(float x, float y) {
        for (Vec2D v : vertices) {
            v.scaleSelf(x, y);
        }
        return this;
    }

    public Polygon2D scale(ReadonlyVec2D scale) {
        return scale(scale.x(), scale.y());
    }

    public Polygon2D scaleSize(float scale) {
        return scaleSize(scale, scale);
    }

    public Polygon2D scaleSize(float x, float y) {
        Vec2D centroid = getCentroid();
        for (Vec2D v : vertices) {
            v.subSelf(centroid).scaleSelf(x, y).addSelf(centroid);
        }
        return this;
    }

    public Polygon2D scaleSize(ReadonlyVec2D scale) {
        return scaleSize(scale.x(), scale.y());
    }

    /**
     * Applies a laplacian-style smooth operation to all polygon vertices,
     * causing sharp corners/angles to widen and results in a general smoother
     * shape. Let the current vertex be A and its neighbours P and Q, then A
     * will be moved by a specified amount into the direction given by
     * (P-A)+(Q-A). Additionally, and to avoid shrinking of the shape through
     * repeated iteration of this procedure, the vector A - C (Polygon centroid)
     * is added as counter force and a weight for its impact can be specified.
     * To keep the average size of the polygon stable, this weight value should
     * be ~1/2 of the smooth amount.
     *
     * @param amount
     *            smooth amount (between 0 < x < 0.5)
     * @param baseWeight
     *            counter weight (0 <= x < 1/2 * smooth amount)
     * @return itself
     */
    public Polygon2D smooth(float amount, float baseWeight) {
        Vec2D centroid = getCentroid();
        int num = vertices.size();
        List<Vec2D> filtered = new ArrayList<Vec2D>(num);
        for (int i = 0, j = num - 1, k = 1; i < num; i++) {
            Vec2D a = vertices.get(i);
            Vec2D dir = vertices.get(j).sub(a).addSelf(vertices.get(k).sub(a))
                    .addSelf(a.sub(centroid).scaleSelf(baseWeight));
            filtered.add(a.add(dir.scaleSelf(amount)));
            j++;
            if (j == num) {
                j = 0;
            }
            k++;
            if (k == num) {
                k = 0;
            }
        }
        vertices.clear();
        vertices.addAll(filtered);
        return this;
    }

    public Mesh3D toMesh(Mesh3D mesh) {
        return toMesh(mesh, null, 0);
    }

    public Mesh3D toMesh(Mesh3D mesh, Vec2D centroid2D, float extrude) {
        if (mesh == null) {
            mesh = new TriangleMesh();
        }
        final int num = vertices.size();
        if (centroid2D == null) {
            centroid2D = getCentroid();
        }
        Vec3D centroid = centroid2D.to3DXY();
        centroid.z = extrude;
        Rect bounds = getBounds();
        Vec2D boundScale = new Vec2D(1f / bounds.width, 1f / bounds.height);
        Vec2D uvC = centroid2D.sub(bounds.getTopLeft()).scaleSelf(boundScale);
        for (int i = 1; i <= num; i++) {
            Vec2D a = vertices.get(i % num);
            Vec2D b = vertices.get(i - 1);
            Vec2D uvA = a.sub(bounds.getTopLeft()).scaleSelf(boundScale);
            Vec2D uvB = b.sub(bounds.getTopLeft()).scaleSelf(boundScale);
            mesh.addFace(centroid, a.to3DXY(), b.to3DXY(), uvC, uvA, uvB);
        }
        return mesh;
    }

    /**
     * Attempts to remove all internal self-intersections and creates a new
     * polygon only consisting of perimeter vertices. Ported from:
     * http://alienryderflex.com/polygon_perimeter/
     *
     * @return true, if process completed succcessfully.
     */
    public boolean toOutline() {
        int corners = vertices.size();
        int maxSegs = corners * 3;
        List<Vec2D> newVerts = new ArrayList<Vec2D>(corners);
        Vec2D[] segments = new Vec2D[maxSegs];
        Vec2D[] segEnds = new Vec2D[maxSegs];
        float[] segAngles = new float[maxSegs];
        Vec2D start = vertices.get(0).copy();
        float lastAngle = MathUtils.PI;
        float a, b, c, d, e, f;
        double angleDif, bestAngleDif;
        int i, j = corners - 1, segs = 0;

        if (corners > maxSegs) {
            return false;
        }

        // 1,3. Reformulate the polygon as a set of line segments, and choose a
        // starting point that must be on the perimeter.
        for (i = 0; i < corners; i++) {
            Vec2D pi = vertices.get(i);
            Vec2D pj = vertices.get(j);
            if (!pi.equals(pj)) {
                segments[segs] = pi;
                segEnds[segs++] = pj;
            }
            j = i;
            if (pi.y > start.y || (pi.y == start.y && pi.x < start.x)) {
                start.set(pi);
            }
        }
        if (segs == 0) {
            return false;
        }

        // 2. Break the segments up at their intersection points.
        for (i = 0; i < segs - 1; i++) {
            for (j = i + 1; j < segs; j++) {
                Line2D li = new Line2D(segments[i], segEnds[i]);
                Line2D lj = new Line2D(segments[j], segEnds[j]);
                LineIntersection isec = li.intersectLine(lj);
                if (isec.getType() == Type.INTERSECTING) {
                    Vec2D ipos = isec.getPos();
                    if (!ipos.equals(segments[i]) && !ipos.equals(segEnds[i])) {
                        if (segs == maxSegs) {
                            return false;
                        }
                        segments[segs] = segments[i].copy();
                        segEnds[segs++] = ipos.copy();
                        segments[i] = ipos.copy();
                    }
                    if (!ipos.equals(segments[j]) && !ipos.equals(segEnds[j])) {
                        if (segs == maxSegs) {
                            return false;
                        }
                        segments[segs] = segments[j].copy();
                        segEnds[segs++] = ipos.copy();
                        segments[j] = ipos.copy();
                    }
                }
            }
        }

        // Calculate the angle of each segment.
        for (i = 0; i < segs; i++) {
            segAngles[i] = segEnds[i].sub(segments[i]).positiveHeading();
        }

        // 4. Build the perimeter polygon.
        c = start.x;
        d = start.y;
        a = c - 1;
        b = d;
        e = 0;
        f = 0;
        newVerts.add(new Vec2D(c, d));
        corners = 1;
        while (true) {
            bestAngleDif = MathUtils.TWO_PI;
            for (i = 0; i < segs; i++) {
                if (segments[i].x == c && segments[i].y == d
                        && (segEnds[i].x != a || segEnds[i].y != b)) {
                    angleDif = lastAngle - segAngles[i];
                    while (angleDif >= MathUtils.TWO_PI) {
                        angleDif -= MathUtils.TWO_PI;
                    }
                    while (angleDif < 0) {
                        angleDif += MathUtils.TWO_PI;
                    }
                    if (angleDif < bestAngleDif) {
                        bestAngleDif = angleDif;
                        e = segEnds[i].x;
                        f = segEnds[i].y;
                    }
                }
                if (segEnds[i].x == c && segEnds[i].y == d
                        && (segments[i].x != a || segments[i].y != b)) {
                    angleDif = lastAngle - segAngles[i] + MathUtils.PI;
                    while (angleDif >= MathUtils.TWO_PI) {
                        angleDif -= MathUtils.TWO_PI;
                    }
                    while (angleDif < 0) {
                        angleDif += MathUtils.TWO_PI;
                    }
                    if (angleDif < bestAngleDif) {
                        bestAngleDif = angleDif;
                        e = segments[i].x;
                        f = segments[i].y;
                    }
                }
            }
            if (corners > 1 && c == newVerts.get(0).x && d == newVerts.get(0).y
                    && e == newVerts.get(1).x && f == newVerts.get(1).y) {
                corners--;
                vertices = newVerts;
                return true;
            }
            if (bestAngleDif == MathUtils.TWO_PI || corners == maxSegs) {
                return false;
            }
            lastAngle -= bestAngleDif + MathUtils.PI;
            newVerts.add(new Vec2D(e, f));
            corners++;
            a = c;
            b = d;
            c = e;
            d = f;
        }
    }

    /**
     * Only needed for {@link Shape2D} interface. Returns itself.
     *
     * @return itself
     */
    public Polygon2D toPolygon2D() {
        return this;
    }

    public String toString() {
        StringBuilder buf = new StringBuilder();
        for (Iterator<Vec2D> i = vertices.iterator(); i.hasNext();) {
            buf.append(i.next().toString());
            if (i.hasNext()) {
                buf.append(", ");
            }
        }
        return buf.toString();
    }

    public Polygon2D translate(float x, float y) {
        for (Vec2D v : vertices) {
            v.addSelf(x, y);
        }
        return this;
    }

    public Polygon2D translate(ReadonlyVec2D offset) {
        return translate(offset.x(), offset.y());
    }
}
TOP

Related Classes of toxi.geom.Polygon2D

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.