/* file : LinearRing2D.java
 * 
 * Project : geometry
 *
 * ===========================================
 * 
 * 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.
 *
 * 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., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 * 
 * Created on 16 avr. 2007
 *
 */

package math.geom2d.polygon;

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

import math.geom2d.AffineTransform2D;
import math.geom2d.GeometricObject2D;
import math.geom2d.Point2D;
import math.geom2d.Shape2D;
import math.geom2d.circulinear.*;
import math.geom2d.circulinear.buffer.BufferCalculator;
import math.geom2d.line.LineSegment2D;
import math.geom2d.transform.CircleInversion2D;

/**
 * 

* A LinearRing2D is a Polyline2D whose last point is connected to the first one. * This is typically the boundary of a SimplePolygon2D. *

*

* The name 'LinearRing2D' was used for 2 reasons: *

*

* @author dlegland */ public class LinearRing2D extends LinearCurve2D implements CirculinearRing2D { // =================================================================== // Static methods /** * Static factory for creating a new LinearRing2D from a collection of * points. * @since 0.8.1 */ public static LinearRing2D create(Collection points) { return new LinearRing2D(points); } /** * Static factory for creating a new LinearRing2D from an array of * points. * @since 0.8.1 */ public static LinearRing2D create(Point2D... vertices) { return new LinearRing2D(vertices); } // =================================================================== // Constructors public LinearRing2D() { super(); } public LinearRing2D(int n) { super(n); } public LinearRing2D(Point2D... vertices) { super(vertices); } public LinearRing2D(double[] xcoords, double[] ycoords) { super(xcoords, ycoords); } public LinearRing2D(Collection points) { super(points); } public LinearRing2D(LinearCurve2D lineString) { super(lineString.vertices); } // =================================================================== // Methods specific to ClosedPolyline2D /** * Computes the signed area of the linear ring. Algorithm is taken from page: * * http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/. Signed are * is positive if polyline is oriented counter-clockwise, and negative * otherwise. Result is wrong if polyline is self-intersecting. * * @return the signed area of the polyline. */ public double area() { // start from edge joining last and first vertices Point2D prev = this.vertices.get(this.vertices.size() - 1); // Iterate over all couples of adjacent vertices double area = 0; for (Point2D point : this.vertices) { // add area of elementary parallelogram area += prev.x() * point.y() - prev.y() * point.x(); prev = point; } // divides by 2 to consider only elementary triangles return area /= 2; } // =================================================================== // Methods specific to LinearCurve2D /** * Returns a simplified version of this linear ring, by using * Douglas-Peucker algorithm. */ public LinearRing2D simplify(double distMax) { return new LinearRing2D(Polylines2D.simplifyClosedPolyline(this.vertices, distMax)); } /** * Returns an array of LineSegment2D. The number of edges is the same as * the number of vertices. * * @return the edges of the polyline */ @Override public Collection edges() { // create resulting array int n = vertices.size(); ArrayList edges = new ArrayList(n); // do not process empty polylines if (n < 2) return edges; // create one edge for each couple of vertices for (int i = 0; i < n - 1; i++) edges.add(new LineSegment2D(vertices.get(i), vertices.get(i + 1))); // add a supplementary edge at the end, but only if vertices differ Point2D p0 = vertices.get(0); Point2D pn = vertices.get(n - 1); // TODO: should not make the test... if (pn.distance(p0) > Shape2D.ACCURACY) edges.add(new LineSegment2D(pn, p0)); // return resulting array return edges; } public int edgeNumber() { int n = vertices.size(); if (n > 1) return n; return 0; } public LineSegment2D edge(int index) { int i2 = (index + 1) % vertices.size(); return new LineSegment2D(vertices.get(index), vertices.get(i2)); } /** * Returns the last edge of this linear ring. The last edge connects the * last vertex with the first one. */ public LineSegment2D lastEdge() { int n = vertices.size(); if (n < 2) return null; return new LineSegment2D(vertices.get(n-1), vertices.get(0)); } // =================================================================== // Methods inherited from CirculinearCurve2D public CirculinearRing2D parallel(double dist) { BufferCalculator bc = BufferCalculator.getDefaultInstance(); return (CirculinearRing2D) GenericCirculinearRing2D.create( bc.createContinuousParallel(this, dist).smoothPieces()); } /* (non-Javadoc) * @see math.geom2d.circulinear.CirculinearCurve2D#transform(math.geom2d.transform.CircleInversion2D) */ public CirculinearRing2D transform(CircleInversion2D inv) { // Create array for storing transformed arcs Collection edges = this.edges(); ArrayList arcs = new ArrayList(edges.size()); // Transform each arc for(LineSegment2D edge : edges) { arcs.add(edge.transform(inv)); } // create the transformed shape return new GenericCirculinearRing2D(arcs); } // =================================================================== // Methods inherited from Boundary2D public CirculinearDomain2D domain() { return new GenericCirculinearDomain2D(this); } public void fill(Graphics2D g2) { g2.fill(this.asGeneralPath()); } // =================================================================== // Methods inherited from interface OrientedCurve2D /* * (non-Javadoc) * * @see math.geom2d.OrientedCurve2D#windingAngle(Point2D) */ public double windingAngle(Point2D point) { int wn = Polygons2D.windingNumber(this.vertices, point); return wn * 2 * Math.PI; } public boolean isInside(double x, double y) { return this.isInside(new Point2D(x, y)); } /* * (non-Javadoc) * * @see math.geom2d.OrientedCurve2D#isInside(Point2D) */ public boolean isInside(Point2D point) { // TODO: choose convention for points on the boundary if (this.contains(point)) return true; double area = this.area(); int winding = Polygons2D.windingNumber(this.vertices, point); if (area > 0) { return winding == 1; } else { return winding == 0; } } // =================================================================== // Methods inherited from interface ContinuousCurve2D /** * Returns true, by definition of linear ring. */ public boolean isClosed() { return true; } // =================================================================== // Methods inherited from interface Curve2D /** * Returns point from position as double. Position t can be from 0 to n, * with n equal to the number of vertices of the linear ring. */ public math.geom2d.Point2D point(double t) { // format position to stay between limits double t0 = this.t0(); double t1 = this.t1(); t = Math.max(Math.min(t, t1), t0); int n = vertices.size(); // index of vertex before point int ind0 = (int) Math.floor(t + Shape2D.ACCURACY); double tl = t - ind0; if (ind0 == n) ind0 = 0; Point2D p0 = vertices.get(ind0); // check if equal to a vertex if (Math.abs(t - ind0) < Shape2D.ACCURACY) return p0; // index of vertex after point int ind1 = ind0 + 1; if (ind1 == n) ind1 = 0; Point2D p1 = vertices.get(ind1); // position on line; double x0 = p0.x(); double y0 = p0.y(); double dx = p1.x() - x0; double dy = p1.y() - y0; return new Point2D(x0 + tl * dx, y0 + tl *dy); } /** * Returns the number of points in the linear ring. */ public double t1() { return vertices.size(); } /** * @deprecated replaced by t1() (since 0.11.1). */ @Deprecated public double getT1() { return this.t1(); } /** * Returns the first point, as this is the same as the last point. */ @Override public Point2D lastPoint() { if (vertices.size() == 0) return null; return vertices.get(0); } @Override public Collection continuousCurves() { return wrapCurve(this); } /** * Returns the linear ring with same points taken in reverse order. The * first points is still the same. Points of reverse curve are the same as * the original curve (same references). */ public LinearRing2D reverse() { Point2D[] points2 = new Point2D[vertices.size()]; int n = vertices.size(); if (n > 0) points2[0] = vertices.get(0); for (int i = 1; i < n; i++) points2[i] = vertices.get(n - i); return new LinearRing2D(points2); } /** * Return an instance of Polyline2D. If t1 is lower than t0, the returned * Polyline contains the origin of the curve. */ public Polyline2D subCurve(double t0, double t1) { // code adapted from CurveSet2D Polyline2D res = new Polyline2D(); // number of points in the polyline int indMax = this.vertexNumber(); // format to ensure t is between T0 and T1 t0 = Math.min(Math.max(t0, 0), indMax); t1 = Math.min(Math.max(t1, 0), indMax); // find curves index int ind0 = (int) Math.floor(t0 + Shape2D.ACCURACY); int ind1 = (int) Math.floor(t1 + Shape2D.ACCURACY); // need to subdivide only one line segment if (ind0 == ind1 && t0 < t1) { // extract limit points res.addVertex(this.point(t0)); res.addVertex(this.point(t1)); // return result return res; } // add the point corresponding to t0 res.addVertex(this.point(t0)); if (ind1 > ind0) { // add all the whole points between the 2 cuts for (int n = ind0 + 1; n <= ind1; n++) res.addVertex(vertices.get(n)); } else { // add all points until the end of the set for (int n = ind0 + 1; n < indMax; n++) res.addVertex(vertices.get(n)); // add all points from the beginning of the set for (int n = 0; n <= ind1; n++) res.addVertex(vertices.get(n)); } // add the last point res.addVertex(this.point(t1)); // return the curve set return res; } // =================================================================== // Methods inherited from interface Shape2D /** * Returns the transformed shape, as a LinerRing2D. */ public LinearRing2D transform(AffineTransform2D trans) { Point2D[] pts = new Point2D[vertices.size()]; for (int i = 0; i < vertices.size(); i++) pts[i] = trans.transform(vertices.get(i)); return new LinearRing2D(pts); } /* * (non-Javadoc) * * @see math.geom2d.ContinuousCurve2D#appendPath(java.awt.geom.GeneralPath) */ public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) { if (vertices.size()<2) return path; // move to last first point of the curve (then a line will be drawn to // the first point) Point2D p0 = this.lastPoint(); path.moveTo((float) p0.x(), (float) p0.y()); // process each point for(Point2D point : vertices) path.lineTo((float) point.x(), (float) point.y()); // close the path, even if the path is already at the right position path.closePath(); return path; } // =================================================================== // 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 LinearRing2D)) return false; LinearRing2D ring = (LinearRing2D) obj; if (vertices.size() != ring.vertices.size()) return false; for (int i = 0; i < vertices.size(); i++) if (!(vertices.get(i)).almostEquals(ring.vertices.get(i), eps)) return false; return true; } @Override public boolean equals(Object object) { if (!(object instanceof LinearRing2D)) return false; LinearRing2D ring = (LinearRing2D) object; if (vertices.size() != ring.vertices.size()) return false; for (int i = 0; i < vertices.size(); i++) if (!(vertices.get(i)).equals(ring.vertices.get(i))) return false; return true; } // =================================================================== // methods implementing the Object interface /** * @deprecated use copy constructor instead (0.11.2) */ @Deprecated @Override public LinearRing2D clone() { ArrayList array = new ArrayList(vertices.size()); for (Point2D point : vertices) array.add(point); return new LinearRing2D(array); } }

Ads help maintain this website.