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

src.math.geom2d.polygon.Polygons2D Maven / Gradle / Ivy

Go to download

JavaGeom - The aim of JavaGeom is to provide methods to easily perform geometric computations, such as intersections of lines, clipping of polygons, transformation shapes, perimeter and area computations... This is a binary & source redistribution of the original, unmodified JavaGeom library originating from: "http://sourceforge.net/projects/geom-java/files/javaGeom/javaGeom%200.11.1/". The purpose of this redistribution is to make the library available to other Maven projects.

The newest version!
/**
 * 
 */

package math.geom2d.polygon;

import static java.lang.Math.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import math.geom2d.Box2D;
import math.geom2d.Point2D;
import math.geom2d.circulinear.CirculinearDomain2D;
import math.geom2d.circulinear.buffer.BufferCalculator;
import math.geom2d.domain.Boundary2D;
import math.geom2d.domain.Boundaries2D;
import math.geom2d.domain.Contour2D;
import math.geom2d.domain.ContourArray2D;
import math.geom2d.point.PointSets2D;

import com.seisw.util.geom.Poly;
import com.seisw.util.geom.PolyDefault;
import com.seisw.util.geom.PolySimple;

/**
 * Several utility functions for working on polygons, including polygon
 * creation, and basic computations.  
 * I
 * @author dlegland
 */
public final class Polygons2D {

	/**
	 * Creates a new polygon representing a rectangle with edges parallel to
	 * the main directions, and having the two specified opposite corners.
	 * @since 0.10.3
	 */
	public final static SimplePolygon2D createRectangle(Point2D p1, Point2D p2) {
		// corners coordinates
    	double x1 = p1.x();
    	double y1 = p1.y();
    	double x2 = p2.x();
    	double y2 = p2.y();

    	return createRectangle(x1, y1, x2, y2);
	}

	/**
	 * Creates a new polygon representing a rectangle with edges parallel to
	 * the main directions, and having the two specified opposite corners.
	 * @since 0.10.3
	 */
	public final static SimplePolygon2D createRectangle(double x1, double y1, 
			double x2, double y2) {
		// extremes coordinates
        double xmin = min(x1, x2);
        double xmax = max(x1, x2);
        double ymin = min(y1, y2);
        double ymax = max(y1, y2);
 
		// create result polygon
		return new SimplePolygon2D(
				new Point2D(xmin, ymin),
				new Point2D(xmax, ymin),
				new Point2D(xmax, ymax),
				new Point2D(xmin, ymax)	);
	}

	/**
	 * Creates a new polygon representing a rectangle centered around a point. 
	 * Rectangle sides are parallel to the main axes. The function returns an
	 * instance of SimplePolygon2D.
	 * @since 0.9.1 
	 */
	public final static SimplePolygon2D createCenteredRectangle(Point2D center, 
			double length, double width) {
		// extract rectangle parameters
		double xc = center.x();
		double yc = center.y();
		double len = length / 2;
		double wid = width / 2;
		
		// coordinates of corners
		double x1 = xc - len;
		double y1 = yc - wid;
		double x2 = xc + len;
		double y2 = yc + wid;

		// create result polygon
		return new SimplePolygon2D(new Point2D[]{
				new Point2D(x1, y1),
				new Point2D(x2, y1),
				new Point2D(x2, y2),
				new Point2D(x1, y2),
		});
	}
	
	/**
	 * Creates a new polygon representing an oriented rectangle centered
	 * around a point. 
	 * The function returns an instance of SimplePolygon2D. 
	 * @since 0.9.1 
	 */
	public final static SimplePolygon2D createOrientedRectangle(Point2D center, 
			double length, double width, double theta) {
		// extract rectangle parameters
		double xc = center.x();
		double yc = center.y();
		double len = length / 2;
		double wid = width / 2;
		
		// Pre-compute angle quantities
		double cot = cos(theta);
		double sit = sin(theta);
		
		// Create resulting rotated rectangle
		return new SimplePolygon2D(new Point2D[]{
				new Point2D(-len*cot + wid*sit + xc, -len*sit - wid*cot + yc),
				new Point2D( len*cot + wid*sit + xc,  len*sit - wid*cot + yc),
				new Point2D( len*cot - wid*sit + xc,  len*sit + wid*cot + yc),
				new Point2D(-len*cot - wid*sit + xc, -len*sit + wid*cot + yc),
		});
	}
	
	/**
	 * Computes the centroid of the given polygon.
	 * @since 0.9.1
	 */
	public final static Point2D computeCentroid(Polygon2D polygon) {
		// process case of simple polygon 
    	if (polygon instanceof SimplePolygon2D) {
    		LinearRing2D ring = ((SimplePolygon2D) polygon).getRing();
    		return computeCentroid(ring);
    	}
    	
    	double xc = 0;
    	double yc = 0;
    	double area;
    	double cumArea = 0;
    	Point2D centroid;
    	
    	for (LinearRing2D ring : polygon.contours()) {
    		area = computeArea(ring);
    		centroid = computeCentroid(ring);
    		xc += centroid.x() * area;
    		yc += centroid.y() * area;
    		cumArea += area;
    	}
    	
    	xc /= cumArea;
    	yc /= cumArea;
    	return new Point2D(xc, yc);
	}
	
	/**
	 * Computes the centroid of the given linear ring.
	 * @since 0.9.1
	 */
	public final static Point2D computeCentroid(LinearRing2D ring) {
        double xc = 0;
        double yc = 0;
        
        double x, y;
        double xp, yp;
        double tmp = 0;
        
        // number of vertices
        int n = ring.vertexNumber();
       
        // initialize with the last vertex
        Point2D prev = ring.vertex(n-1);
        xp = prev.x();
        yp = prev.y();

        // iterate on vertices
        for (Point2D point : ring.vertices()) {
        	x = point.x();
        	y = point.y();
        	tmp = xp * y - yp * x;
            xc += (x + xp) * tmp;
            yc += (y + yp) * tmp;
            
            prev = point;
            xp = x;
            yp = y;
        }
        
        double denom = computeArea(ring) * 6;
        return new Point2D(xc / denom, yc / denom);
	}

	
    /**
     * Computes the signed area of the polygon. Algorithm is taken from page: 
     * http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/. Signed area
     * is positive if polygon is oriented counter-clockwise, and negative
     * otherwise. Result is wrong if polygon is self-intersecting.
     * 
     * @return the signed area of the polygon.
	 * @since 0.9.1
     */
    public final static double computeArea(Polygon2D polygon) {
    	double area = 0;
    	for (LinearRing2D ring : polygon.contours()) {
    		area += computeArea(ring);
    	}
    	return area;
    }

    /**
     * Computes the signed area of the linear ring. Algorithm is taken from page: 
     * http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/. Signed area
     * is positive if linear ring is oriented counter-clockwise, and negative
     * otherwise. Result is wrong if linear ring is self-intersecting.
     * 
     * @return the signed area of the polygon.
	 * @since 0.9.1
     */
    public final static double computeArea(LinearRing2D ring) {
        double area = 0;
        
        // number of vertices
        int n = ring.vertexNumber();
       
        // initialize with the last vertex
        Point2D prev = ring.vertex(n-1);
        
        // iterate on edges
        for (Point2D point : ring.vertices()) {
            area += prev.x() * point.y() - prev.y() * point.x();
            prev = point;
        }
        
        return area /= 2;
    }


	/**
     * Computes the winding number of the polygon. Algorithm adapted from
     * http://www.geometryalgorithms.com/Archive/algorithm_0103/algorithm_0103.htm
     * http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
     * @param vertices the vertices of a polygon
     * @param point the reference point
     * @return the number of windings of the curve around the point
     */
    public final static int windingNumber(Collection vertices,
            Point2D point) {
        int wn = 0; // the winding number counter

        // Extract the last point of the collection
        Point2D previous = null;
        for (Point2D vertex : vertices)
            previous = vertex;
        double y1 = previous.y();
        double y2;

        // keep y-coordinate of test point
        double y = point.y();

        // Iterate on couple of vertices, starting from couple (last,first)
        for (Point2D current : vertices) {
            // second vertex of current edge
            y2 = current.y();
            
			if (y1 <= y) {
				if (y2 > y) // an upward crossing
					if (isLeft(previous, current, point) > 0)
						wn++;
			} else {
				if (y2 <= y) // a downward crossing
					if (isLeft(previous, current, point) < 0)
						wn--;
			}

            // for next iteration
            y1 = y2;
            previous = current;
        }

        return wn;
    }

    /**
     * Tests if a point is Left|On|Right of an infinite line.
     * Input:  three points P0, P1, and P2
     * Return: >0 for P2 left of the line through P0 and P1
     *         =0 for P2 on the line
     *         <0 for P2 right of the line
     * See: the January 2001 Algorithm "Area of 2D and 3D Triangles and Polygons"
     */
    private final static int isLeft(Point2D p1, Point2D p2, Point2D pt) {
    	double x = p1.x();
    	double y = p1.y();
    	return (int) Math.signum(
    			(p2.x() - x) * (pt.y() - y) - (pt.x() - x) * (p2.y() - y));
    }
    
    /**
     * Computes the buffer at a distance d of the input polygon. The result is
     * a domain whose boundary is composed of line segments and circle arcs.  
     * @see Polygon2D#buffer(double)
     */
    public final static CirculinearDomain2D createBuffer(Polygon2D polygon, 
    		double dist) {
    	// get current instance of buffer calculator
        BufferCalculator bc = BufferCalculator.getDefaultInstance();
        
        // compute buffer
        return bc.computeBuffer(polygon.boundary(), dist);
    }
    
    /**
     * Clips a polygon by a box. The result is a new polygon, that can be
     * multiple.
     * @see Polygon2D#clip(Box2D)
     */
    public final static Polygon2D clipPolygon(Polygon2D polygon, Box2D box) {
    	// Clip the boundary using generic method
    	Boundary2D boundary = polygon.boundary();
        ContourArray2D contours = 
            Boundaries2D.clipBoundary(boundary, box);

        // convert boundaries to linear rings
        ArrayList rings = new ArrayList();
        for(Contour2D contour : contours)
        	rings.add(convertContourToLinearRing(contour));
        
        // Create a polygon, either simple or multiple, depending on the ring
        // number
        if (rings.size() == 1)
        	return SimplePolygon2D.create(rings.get(0).vertices());
        else
        	return MultiPolygon2D.create(rings);
    }
    
    private final static LinearRing2D convertContourToLinearRing(
    		Contour2D contour) {
    	// process the basic case of simple class cast
    	if (contour instanceof LinearRing2D)
    		return (LinearRing2D) contour;
    	
    	// extract all vertices of the contour
    	List vertices = new ArrayList();
    	for(Point2D v : contour.singularPoints())
    		vertices.add(v);

    	// remove adjacent multiple vertices
    	vertices = PointSets2D.filterMultipleVertices(vertices, true);
 	
    	// Create new ring with vertices
    	return LinearRing2D.create(vertices);
    }
    
    /**
     * Computes the union of the two polygons. Uses the GPCJ library, developed by
     * Solution Engineering, Inc.
     */
    public final static Polygon2D union(Polygon2D polygon1, 
            Polygon2D polygon2) {
    	// convert to GPCJ data structures
    	Poly poly1 = convertToGpcjPolygon(polygon1);
    	Poly poly2 = convertToGpcjPolygon(polygon2);
    	
    	// compute union
    	Poly result = poly1.union(poly2);
    	
    	// convert result to javaGeom structure
    	return convertFromGpcjPolygon(result);
    }
    
    /**
     * Computes the intersection of the two polygons. Uses the GPCJ library, 
     * developed by Solution Engineering, Inc.
     */
    public final static Polygon2D intersection(Polygon2D polygon1, 
            Polygon2D polygon2) {
    	// convert to GPCJ data structures
    	Poly poly1 = convertToGpcjPolygon(polygon1);
    	Poly poly2 = convertToGpcjPolygon(polygon2);
    	
    	// compute union
    	Poly result = poly1.intersection(poly2);
    	
    	// convert result to javaGeom structure
    	return convertFromGpcjPolygon(result);
    }
    
    /**
     * Computes the exclusive XOR of the two polygons. Uses the GPCJ library, 
     * developed by Solution Engineering, Inc.
     */
    public final static Polygon2D exclusiveOr(Polygon2D polygon1, 
    		Polygon2D polygon2) {
    	// convert to GPCJ data structures
    	Poly poly1 = convertToGpcjPolygon(polygon1);
    	Poly poly2 = convertToGpcjPolygon(polygon2);
    	
    	// compute union
    	Poly result = poly1.xor(poly2);
    	
    	// convert result to javaGeom structure
    	return convertFromGpcjPolygon(result);
    }
    
    /**
     * Computes the Difference of the two polygons. Uses the modified GPCJ library, 
     * developed by Solution Engineering, Inc.
     * @since 0.9.1
     */
    public final static Polygon2D difference(Polygon2D polygon1, 
    		Polygon2D polygon2) {
    	// convert to GPCJ data structures
    	Poly poly1 = convertToGpcjPolygon(polygon1);
    	Poly poly2 = convertToGpcjPolygon(polygon2);
    	
    	// compute union
    	Poly result = poly1.difference(poly2);
    	
    	// convert result to javaGeom structure
    	return convertFromGpcjPolygon(result);
    }
    
    private final static Poly convertToGpcjPolygon(Polygon2D polygon) {
    	PolyDefault result = new PolyDefault();
    	for (LinearRing2D ring : polygon.contours())
    		result.add(convertToGpcjSimplePolygon(ring));
    	return result;
    }
    
    private final static PolySimple convertToGpcjSimplePolygon(
    		LinearRing2D ring) {
    	PolySimple poly = new PolySimple();
    	for (Point2D point : ring.vertices())
    		poly.add(new com.seisw.util.geom.Point2D(point.x(), point.y()));
    	return poly;
    }
    
    
    private final static Polygon2D convertFromGpcjPolygon(Poly poly) {
    	int n = poly.getNumInnerPoly();
    	
    	// if the result is single, create a SimplePolygon
    	if (n == 1) {
    		Point2D[] points = extractPolyVertices(poly.getInnerPoly(0));
    		return SimplePolygon2D.create(points);
    	}
    	
    	// extract the different rings of the resulting polygon
    	LinearRing2D[] rings = new LinearRing2D[n];
    	for (int i = 0; i < n; i++) 
    		rings[i] = convertFromGpcjSimplePolygon(poly.getInnerPoly(i));
    	
    	// create a multiple polygon
    	return MultiPolygon2D.create(rings);
    }
    
    private final static LinearRing2D convertFromGpcjSimplePolygon(
    		Poly poly) {
    	return LinearRing2D.create(extractPolyVertices(poly));
    }
    
    private final static Point2D[] extractPolyVertices(Poly poly) {
    	int n = poly.getNumPoints();
    	Point2D[] points = new Point2D[n];
    	for (int i = 0; i < n; i++)
    		points[i] = new Point2D(poly.getX(i), poly.getY(i));
    	return points;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy