All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mathiewz.slick.geom.BasicTriangulator Maven / Gradle / Ivy

Go to download

The main purpose of this libraryis to modernize and maintain the slick2D library.

The newest version!
package com.github.mathiewz.slick.geom;

import java.util.ArrayList;

import com.github.mathiewz.slick.SlickException;

/**
 * Triangulates a polygon into triangles - duh. Doesn't handle
 * holes in polys
 *
 * @author Based on Public Source from FlipCode
 */
public class BasicTriangulator implements Triangulator {
    /** The accepted error value */
    private static final float EPSILON = 0.0000000001f;
    /** The list of points to be triangulated */
    private final PointList poly = new PointList();
    /** The list of points describing the triangles */
    private final PointList tris = new PointList();
    /** True if we've tried to triangulate */
    private boolean tried;
    
    /**
     * Create a new triangulator
     */
    public BasicTriangulator() {
    }
    
    /**
     * Add a point describing the polygon to be triangulated
     *
     * @param x
     *            The x coordinate of the point
     * @param y
     *            the y coordinate of the point
     */
    @Override
    public void addPolyPoint(float x, float y) {
        Point p = new Point(x, y);
        if (!poly.contains(p)) {
            poly.add(p);
        }
    }
    
    /**
     * Get the number of points in the polygon
     *
     * @return The number of points in the polygon
     */
    public int getPolyPointCount() {
        return poly.size();
    }
    
    /**
     * Get the coordinates of the point at the specified index
     *
     * @param index
     *            The index of the point to retrieve
     * @return The oordinates of the point at the specified index
     */
    public float[] getPolyPoint(int index) {
        return new float[] { poly.get(index).x, poly.get(index).y };
    }
    
    /**
     * Cause the triangulator to split the polygon
     *
     * @return True if we managed the task
     */
    @Override
    public boolean triangulate() {
        tried = true;
        
        boolean worked = process(poly, tris);
        return worked;
    }
    
    /**
     * Get a count of the number of triangles produced
     *
     * @return The number of triangles produced
     */
    @Override
    public int getTriangleCount() {
        if (!tried) {
            throw new SlickException("Call triangulate() before accessing triangles");
        }
        return tris.size() / 3;
    }
    
    /**
     * Get a point on a specified generated triangle
     *
     * @param tri
     *            The index of the triangle to interegate
     * @param i
     *            The index of the point within the triangle to retrieve
     *            (0 - 2)
     * @return The x,y coordinate pair for the point
     */
    @Override
    public float[] getTrianglePoint(int tri, int i) {
        if (!tried) {
            throw new SlickException("Call triangulate() before accessing triangles");
        }
        
        return tris.get(tri * 3 + i).toArray();
    }
    
    /**
     * Find the area of a polygon defined by the series of points
     * in the list
     *
     * @param contour
     *            The list of points defined the contour of the polygon
     *            (Vector2f)
     * @return The area of the polygon defined
     */
    private float area(PointList contour) {
        int n = contour.size();
        
        float A = 0.0f;
        
        for (int p = n - 1, q = 0; q < n; p = q++) {
            Point contourP = contour.get(p);
            Point contourQ = contour.get(q);
            
            A += contourP.getX() * contourQ.getY() - contourQ.getX() * contourP.getY();
        }
        return A * 0.5f;
    }
    
    /**
     * Check if the point P is inside the triangle defined by
     * the points A,B,C
     *
     * @param Ax
     *            Point A x-coordinate
     * @param Ay
     *            Point A y-coordinate
     * @param Bx
     *            Point B x-coordinate
     * @param By
     *            Point B y-coordinate
     * @param Cx
     *            Point C x-coordinate
     * @param Cy
     *            Point C y-coordinate
     * @param Px
     *            Point P x-coordinate
     * @param Py
     *            Point P y-coordinate
     * @return True if the point specified is within the triangle
     */
    private boolean insideTriangle(float Ax, float Ay, float Bx, float By, float Cx, float Cy, float Px, float Py) {
        float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
        float cCROSSap, bCROSScp, aCROSSbp;
        
        ax = Cx - Bx;
        ay = Cy - By;
        bx = Ax - Cx;
        by = Ay - Cy;
        cx = Bx - Ax;
        cy = By - Ay;
        apx = Px - Ax;
        apy = Py - Ay;
        bpx = Px - Bx;
        bpy = Py - By;
        cpx = Px - Cx;
        cpy = Py - Cy;
        
        aCROSSbp = ax * bpy - ay * bpx;
        cCROSSap = cx * apy - cy * apx;
        bCROSScp = bx * cpy - by * cpx;
        
        return aCROSSbp >= 0.0f && bCROSScp >= 0.0f && cCROSSap >= 0.0f;
    }
    
    /**
     * Cut a the contour and add a triangle into V to describe the
     * location of the cut
     *
     * @param contour
     *            The list of points defining the polygon
     * @param u
     *            The index of the first point
     * @param v
     *            The index of the second point
     * @param w
     *            The index of the third point
     * @param n
     *            ?
     * @param V
     *            The array to populate with indicies of triangles
     * @return True if a triangle was found
     */
    private boolean snip(PointList contour, int u, int v, int w, int n, int[] V) {
        int p;
        float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
        
        Ax = contour.get(V[u]).getX();
        Ay = contour.get(V[u]).getY();
        
        Bx = contour.get(V[v]).getX();
        By = contour.get(V[v]).getY();
        
        Cx = contour.get(V[w]).getX();
        Cy = contour.get(V[w]).getY();
        
        if (EPSILON > (Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax)) {
            return false;
        }
        
        for (p = 0; p < n; p++) {
            if (p == u || p == v || p == w) {
                continue;
            }
            
            Px = contour.get(V[p]).getX();
            Py = contour.get(V[p]).getY();
            
            if (insideTriangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py)) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * Process a list of points defining a polygon
     *
     * @param contour
     *            The list of points describing the polygon
     * @param result
     *            The list of points describing the triangles. Groups
     *            of 3 describe each triangle
     *
     * @return True if we succeeded in completing triangulation
     */
    private boolean process(PointList contour, PointList result) {
        result.clear();
        
        /* allocate and initialize list of Vertices in polygon */
        
        int n = contour.size();
        if (n < 3) {
            return false;
        }
        
        int[] V = new int[n];
        
        /* we want a counter-clockwise polygon in V */
        
        if (0.0f < area(contour)) {
            for (int v = 0; v < n; v++) {
                V[v] = v;
            }
        } else {
            for (int v = 0; v < n; v++) {
                V[v] = n - 1 - v;
            }
        }
        
        int nv = n;
        
        /* remove nv-2 Vertices, creating 1 triangle every time */
        int count = 2 * nv; /* error detection */
        
        for (int v = nv - 1; nv > 2;) {
            /* if we loop, it is probably a non-simple polygon */
            if (0 >= count--) {
                // ** Triangulator4: ERROR - probable bad polygon!
                return false;
            }
            
            /* three consecutive vertices in current polygon,  */
            int u = v;
            if (nv <= u) {
                u = 0; /* previous */
            }
            v = u + 1;
            if (nv <= v) {
                v = 0; /* new v */
            }
            int w = v + 1;
            if (nv <= w) {
                w = 0; /* next */
            }

            if (snip(contour, u, v, w, nv, V)) {
                int a, b, c, s, t;
                
                /* true names of the vertices */
                a = V[u];
                b = V[v];
                c = V[w];
                
                /* output Triangle */
                result.add(contour.get(a));
                result.add(contour.get(b));
                result.add(contour.get(c));
                
                /* remove v from remaining polygon */
                for (s = v, t = v + 1; t < nv; s++, t++) {
                    V[s] = V[t];
                }
                nv--;
                
                /* resest error detection counter */
                count = 2 * nv;
            }
        }
        
        return true;
    }
    
    /**
     * A single point handled by the triangulator
     *
     * @author Kevin Glass
     */
    private class Point {
        /** The x coorindate of this point */
        private final float x;
        /** The y coorindate of this point */
        private final float y;
        /** The points in an array */
        private final float[] array;
        
        /**
         * Create a new point
         *
         * @param x
         *            The x coordindate of the point
         * @param y
         *            The y coordindate of the point
         */
        public Point(float x, float y) {
            this.x = x;
            this.y = y;
            array = new float[] { x, y };
        }
        
        /**
         * Get the x coordinate of the point
         *
         * @return The x coordinate of the point
         */
        public float getX() {
            return x;
        }
        
        /**
         * Get the y coordinate of the point
         *
         * @return The y coordinate of the point
         */
        public float getY() {
            return y;
        }
        
        /**
         * Convert this point into a float array
         *
         * @return The contents of this point as a float array
         */
        public float[] toArray() {
            return array;
        }
        
        /**
         * @see java.lang.Object#hashCode()
         */
        @Override
        public int hashCode() {
            return (int) (x * y * 31);
        }
        
        /**
         * @see java.lang.Object#equals(java.lang.Object)
         */
        @Override
        public boolean equals(Object other) {
            if (other instanceof Point) {
                Point p = (Point) other;
                return p.x == x && p.y == y;
            }
            
            return false;
        }
    }
    
    /**
     * A list of type Point
     *
     * @author Kevin Glass
     */
    private class PointList {
        /** The list of points */
        private final ArrayList points = new ArrayList<>();
        
        /**
         * Check if the list contains a point
         *
         * @param p
         *            The point to look for
         * @return True if the point is in the list
         */
        public boolean contains(Point p) {
            return points.contains(p);
        }
        
        /**
         * Add a point to the list
         *
         * @param point
         *            The point to add
         */
        public void add(Point point) {
            points.add(point);
        }
        
        /**
         * Get the size of the list
         *
         * @return The size of the list
         */
        public int size() {
            return points.size();
        }
        
        /**
         * Get a point a specific index in the list
         *
         * @param i
         *            The index of the point to retrieve
         * @return The point
         */
        public Point get(int i) {
            return points.get(i);
        }
        
        /**
         * Clear the list
         */
        public void clear() {
            points.clear();
        }
    }
    
    /**
     * @see com.github.mathiewz.slick.geom.Triangulator#startHole()
     */
    @Override
    public void startHole() {
        // Nothing to do here
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy