package math.geom2d.polygon;

import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import math.geom2d.AffineTransform2D;
import math.geom2d.Box2D;
import math.geom2d.GeometricObject2D;
import math.geom2d.Point2D;
import math.geom2d.circulinear.CirculinearContourArray2D;
import math.geom2d.circulinear.CirculinearDomain2D;
import math.geom2d.circulinear.GenericCirculinearDomain2D;
import math.geom2d.domain.Boundary2D;
import math.geom2d.domain.Domain2D;
import math.geom2d.line.LineSegment2D;
import math.geom2d.transform.CircleInversion2D;

/**
 * A polygonal domain whose boundary is composed of several disjoint continuous
 * LinearRing2D.
 * 
 * @author dlegland
 */
public class MultiPolygon2D implements Domain2D, Polygon2D {

    // ===================================================================
    // Static constructors
	
	public static MultiPolygon2D create(Collection rings) {
		return new MultiPolygon2D(rings);
	}

	public static MultiPolygon2D create(LinearRing2D... rings) {
		return new MultiPolygon2D(rings);
	}

	
    // ===================================================================
    // class members

    ArrayList rings = new ArrayList(1);

    
    // ===================================================================
    // Constructors

    public MultiPolygon2D() {
    }

    /**
     * Ensures the inner buffer has enough capacity for storing the required
     * number of rings.
     */
    public MultiPolygon2D(int nRings) {
    	this.rings.ensureCapacity(nRings);
    }

    public MultiPolygon2D(LinearRing2D... rings) {
        for (LinearRing2D ring : rings)
            this.rings.add(ring);
    }

    public MultiPolygon2D(Polygon2D polygon) {
    	if (polygon instanceof SimplePolygon2D) {
    		rings.add(((SimplePolygon2D) polygon).getRing());
    	} else {
    		rings.addAll(polygon.boundary().curves());
    	}
    }

    public MultiPolygon2D(Collection lines) {
        rings.addAll(lines);
    }

    // ===================================================================
    // Management of rings

    public void addRing(LinearRing2D ring) {
        rings.add(ring);
    }

    public void insertRing(int index, LinearRing2D ring) {
    	rings.add(index, ring);
    }
    
    public void removeRing(LinearRing2D ring) {
        rings.remove(ring);
    }

    public void clearRings() {
    	rings.clear();
    }
    
    public LinearRing2D getRing(int index) {
        return rings.get(index);
    }

    public void setRing(int index, LinearRing2D ring) {
        rings.set(index, ring);
    }

    public int ringNumber() {
        return rings.size();
    }

    
    // ===================================================================
    // methods implementing the Polygon2D interface

    /**
     * Computes the signed area of the polygon. 
     * @return the signed area of the polygon.
     * @since 0.9.1
     */
    public double area() {
    	return Polygons2D.computeArea(this);
    }

    /**
     * Computes the centroid (center of mass) of the polygon. 
     * @return the centroid of the polygon
     * @since 0.9.1
     */
    public Point2D centroid() {
    	return Polygons2D.computeCentroid(this);
    }

    public Collection edges() {
    	int nEdges = edgeNumber();
        ArrayList edges = new ArrayList(nEdges);
        for (LinearRing2D ring : rings)
            edges.addAll(ring.edges());
        return edges;
    }

    public int edgeNumber() {
        int count = 0;
        for (LinearRing2D ring : rings)
            count += ring.vertexNumber();
        return count;
    }

    public Collection vertices() {
    	int nv = vertexNumber();
        ArrayList points = new ArrayList(nv);
        for (LinearRing2D ring : rings)
            points.addAll(ring.vertices());
        return points;
    }

    /**
     * Returns the i-th vertex of the polygon.
     * 
     * @param i index of the vertex, between 0 and the number of vertices minus one
     */
    public Point2D vertex(int i) {
        int count = 0;
        LinearRing2D boundary = null;

        for (LinearRing2D ring : rings) {
            int nv = ring.vertexNumber();
            if (count + nv > i) {
                boundary = ring;
                break;
            }
            count += nv;
        }

        if (boundary == null)
            throw new IndexOutOfBoundsException();

        return boundary.vertex(i-count);
    }

    /**
     * Sets the position of the i-th vertex of this polygon.
     * 
     * @param i index of the vertex, between 0 and the number of vertices
     */
    public void setVertex(int i, Point2D point) {
        int count = 0;
        LinearRing2D boundary = null;

        for (LinearRing2D ring : rings) {
            int nv = ring.vertexNumber();
            if (count + nv > i) {
                boundary = ring;
                break;
            }
            count += nv;
        }

        if (boundary == null)
            throw new IndexOutOfBoundsException();

        boundary.setVertex(i-count, point);
    }

	/**
	 * Adds a vertex at the end of the last ring of this polygon.
	 * 
	 * @throws RuntimeException
	 *             if this MultiPolygon does not contain any ring
	 */
    public void addVertex(Point2D position) {
    	// get the last ring
    	if (rings.size() == 0) {
    		throw new RuntimeException("Can not add a vertex to a multipolygon with no ring");
    	}
		LinearRing2D ring = rings.get(rings.size() - 1);
		ring.addVertex(position);
    }
    
    /**
     * Inserts a vertex at the given position
     * 
     * @throws RuntimeException if this polygon has no ring
     * @throws IllegalArgumentException if index is not smaller than vertex number
     */
    public void insertVertex(int index, Point2D point) {
    	// check number of rings
    	if (rings.size() == 0) {
    		throw new RuntimeException("Can not add a vertex to a multipolygon with no ring");
    	}
    	
    	// Check number of vertices
    	int nv = this.vertexNumber();
    	if (nv <= index) {
    		throw new IllegalArgumentException("Can not insert vertex at position " +
    				index + " (max is " + nv + ")");
    	}
    	
    	// Find the ring that correspond to index
        int count = 0;
        LinearRing2D boundary = null;

        for (LinearRing2D ring : rings) {
            nv = ring.vertexNumber();
            if (count + nv > index) {
                boundary = ring;
                break;
            }
            count += nv;
        }

        if (boundary == null)
            throw new IndexOutOfBoundsException();

        boundary.insertVertex(index-count, point);
    }

    /**
     * Returns the i-th vertex of the polygon.
     * 
     * @param i index of the vertex, between 0 and the number of vertices minus one
     */
    public void removeVertex(int i) {
        int count = 0;
        LinearRing2D boundary = null;

        for (LinearRing2D ring : rings) {
            int nv = ring.vertexNumber();
            if (count + nv > i) {
                boundary = ring;
                break;
            }
            count += nv;
        }

        if (boundary == null)
            throw new IndexOutOfBoundsException();

        boundary.removeVertex(i-count);
    }

    /**
     * Returns the total number of vertices in this polygon. 
     * The total number is computed as the sum of vertex number in each ring
     * of the polygon.
     */
    public int vertexNumber() {
        int count = 0;
        for (LinearRing2D ring : rings)
            count += ring.vertexNumber();
        return count;
    }

    /**
     * Computes the index of the closest vertex to the input point.
     */
    public int closestVertexIndex(Point2D point) {
    	double minDist = Double.POSITIVE_INFINITY;
    	int index = -1;
    	
    	int i = 0;
    	for (LinearRing2D ring : this.rings) {
    		for (Point2D vertex : ring.vertices()) {
    			double dist = vertex.distance(point);
        		if (dist < minDist) {
        			index = i;
        			minDist = dist;
        		}
        		i++;
    		}
    		
    	}
    	
    	return index;
    }
    

	// ===================================================================
    // methods implementing the Domain2D interface

	/* (non-Javadoc)
	 * @see math.geom2d.circulinear.CirculinearDomain2D#transform(math.geom2d.transform.CircleInversion2D)
	 */
	public CirculinearDomain2D transform(CircleInversion2D inv) {
		return new GenericCirculinearDomain2D(
				this.boundary().transform(inv).reverse());
	}

	/* (non-Javadoc)
	 * @see math.geom2d.circulinear.CirculinearShape2D#buffer(double)
	 */
	public CirculinearDomain2D buffer(double dist) {
		return Polygons2D.createBuffer(this, dist);
	}

	/* (non-Javadoc)
	 * @see math.geom2d.domain.Domain2D#asPolygon(int)
	 */
	public Polygon2D asPolygon(int n) {
		return this;
	}

   
	public CirculinearContourArray2D boundary() {
        return (CirculinearContourArray2D) CirculinearContourArray2D.create(rings);
    }

	/* (non-Javadoc)
	 * @see math.geom2d.domain.Domain2D#contours()
	 */
	public Collection contours() {
		return Collections.unmodifiableList(rings);
	}

    public Polygon2D complement() {
        // allocate memory for array of reversed rings
        ArrayList reverseLines = 
        	new ArrayList(rings.size());
        
        // reverse each ring
        for (LinearRing2D ring : rings)
            reverseLines.add(ring.reverse());
        
        // create the new MultiMpolygon2D with set of reversed rings
        return new MultiPolygon2D(reverseLines);
    }

    // ===================================================================
    // methods inherited from interface Shape2D

    public Box2D boundingBox() {
        // start with empty bounding box
        Box2D box = new Box2D(
        		Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 
                Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
        
        // compute union of all bounding boxes
        for (LinearRing2D ring : this.rings)
            box = box.union(ring.boundingBox());
        
        // return result
        return box;
    }

    /**
     * Clips the polygon with the specified box.
     */
    public Polygon2D clip(Box2D box) {
    	return Polygons2D.clipPolygon(this, box);
    }

    public double distance(Point2D p) {
        return Math.max(this.boundary().signedDistance(p), 0);
    }

    public double distance(double x, double y) {
        return Math.max(this.boundary().signedDistance(x, y), 0);
    }

    public boolean isBounded() {
        // If boundary is not bounded, the polygon is not
        Boundary2D boundary = this.boundary();
        if (!boundary.isBounded())
            return false;

        // Computes the signed area
        double area = 0;
        for (LinearRing2D ring : rings)
            area += ring.area();

        // bounded if positive area
        return area>0;
    }

    /**
     * The MultiPolygon2D is empty either if it contains no ring, or if all
     * rings are empty.
     */
    public boolean isEmpty() {
        // return true if at least one ring is not empty
        for (LinearRing2D ring : rings)
            if (!ring.isEmpty())
                return false;
        return true;
    }

    public MultiPolygon2D transform(AffineTransform2D trans) {
        // allocate memory for transformed rings
        ArrayList transformed = 
            new ArrayList(rings.size());
        
        // transform each ring
        for (LinearRing2D ring : rings)
            transformed.add(ring.transform(trans));
        
        // creates a new MultiPolygon2D with the set of trasnformed rings
        return new MultiPolygon2D(transformed);
    }

    public boolean contains(Point2D point) {
        double angle = 0;
        for (LinearRing2D ring : this.rings)
            angle += ring.windingAngle(point);
      
        double area = this.area();
    	if (area > 0) {
    		return angle > Math.PI;
    	} else {
    		return angle > -Math.PI;
    	}
    }

    public boolean contains(double x, double y) {
        return this.contains(new math.geom2d.Point2D(x, y));
    }

    public void draw(Graphics2D g2) {
        g2.draw(this.boundary().getGeneralPath());
    }

    public void fill(Graphics2D g) {
        g.fill(this.boundary().getGeneralPath());
    }
    

	// ===================================================================
	// methods implementing the GeometricObject2D interface

	/* (non-Javadoc)
	 * @see math.geom2d.GeometricObject2D#almostEquals(math.geom2d.GeometricObject2D, double)
	 */
    public boolean almostEquals(GeometricObject2D obj, double eps) {
    	if (this == obj)
    		return true;
    	
        if (!(obj instanceof MultiPolygon2D))
            return false;
        MultiPolygon2D polygon = (MultiPolygon2D) obj;

        // check if the two objects have same number of rings
        if (polygon.rings.size() != this.rings.size()) 
            return false;
        
        // check each couple of ring
        for (int i = 0; i < rings.size(); i++)
            if(!this.rings.get(i).almostEquals(polygon.rings.get(i), eps))
                return false;
        
        return true;
    }

	// ===================================================================
	// methods overriding the Object class

	@Override
    public boolean equals(Object obj) {
    	if (this == obj)
    		return true;

    	if (!(obj instanceof MultiPolygon2D))
            return false;
        
        // check if the two objects have same number of rings
        MultiPolygon2D polygon = (MultiPolygon2D) obj;
        if (polygon.rings.size() != this.rings.size()) 
            return false;
        
        // check each couple of ring
        for (int i = 0; i < rings.size(); i++)
            if (!this.rings.get(i).equals(polygon.rings.get(i)))
                return false;
        
        return true;
    }
   
	/**
	 * @deprecated use copy constructor instead (0.11.2)
	 */
	@Deprecated
    public MultiPolygon2D clone() {
        // allocate memory for new ring array
        ArrayList array = new ArrayList(rings.size());
        
        // clone each ring
        for(LinearRing2D ring : rings)
            array.add(new LinearRing2D(ring));
        
        // create a new polygon with cloned rings
        return new MultiPolygon2D(array);
    }
}

 

Ads help maintain this website.