/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* 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
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.revolsys.geometry.test.old.generator;
import java.util.ArrayList;
import java.util.List;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LinearRing;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.Polygon;
import com.revolsys.geometry.model.impl.PointDoubleXY;
import com.revolsys.geometry.operation.valid.IsValidOp;
/**
*
* This class is used to Construct a new polygon within the specified bounding box.
*
* Sucessive calls to create may or may not return the same geometry topology.
*
* @author David Zwiers, Vivid Solutions.
*/
public class PolygonGenerator extends GeometryGenerator {
/**
* Creates polygons whose points will not be rectangular when there are more than 4 points
*/
public static final int ARC = 1;
/**
* Creates rectangular polygons
*/
public static final int BOX = 0;
private static final int RUNS = 5;
private static Polygon newArc(final double x, final double dx, final double y, final double dy,
final int nholes, final int npoints, final GeometryFactory gf) {
// make outer ring first
double radius = dx < dy ? dx / 3 : dy / 3;
final double cx = x + dx / 2; // center
final double cy = y + dy / 2; // center
final LinearRing outer = newArc(cx, cy, radius, npoints, gf);
if (nholes == 0) {
return gf.polygon(outer);
}
final List<LinearRing> rings = new ArrayList<>();
rings.add(outer);
radius *= .75;
int degreesPerHole = 360 / (nholes + 1);
int degreesPerGap = degreesPerHole / nholes;
degreesPerGap = degreesPerGap < 2 ? 2 : degreesPerGap;
degreesPerHole = (360 - degreesPerGap * nholes) / nholes;
if (degreesPerHole < 2) {
throw new RuntimeException("Slices too small for poly. Use Box alg.");
}
final int start = degreesPerGap / 2;
for (int i = 0; i < nholes; i++) {
final int st = start + i * (degreesPerHole + degreesPerGap); // start
// angle
rings.add(newTri(cx, cy, st, st + degreesPerHole, radius, gf));
}
return gf.polygon(rings);
}
private static LinearRing newArc(final double cx, final double cy, final double radius,
final int npoints, final GeometryFactory gf) {
final Point[] coords = new Point[npoints + 1];
final double theta = 360 / npoints;
for (int i = 0; i < npoints; i++) {
final double angle = Math.toRadians(theta * i);
final double fx = Math.sin(angle) * radius; // may be neg.
final double fy = Math.cos(angle) * radius; // may be neg.
coords[i] = new PointDoubleXY(gf.makePrecise(0, cx + fx), gf.makePrecise(1, cy + fy));
}
coords[npoints] = coords[0];
return gf.linearRing(coords);
}
private static LinearRing newBox(final double x, final double dx, final double y, final double dy,
final int npoints, final GeometryFactory gf) {
// figure out the number of points per side
final int ptsPerSide = npoints / 4;
int rPtsPerSide = npoints % 4;
final Point[] coords = new Point[npoints + 1];
coords[0] = new PointDoubleXY(gf.makePrecise(0, x), gf.makePrecise(1, y)); // start
final int cindex = 1;
for (int i = 0; i < 4; i++) { // sides
final int npts = ptsPerSide + (rPtsPerSide-- > 0 ? 1 : 0);
// npts atleast 1
if (i % 2 == 1) { // odd vert
double cy = dy / npts;
if (i > 1) {
cy *= -1;
}
final double tx = coords[cindex - 1].getX();
final double sy = coords[cindex - 1].getY();
for (int j = 0; j < npts; j++) {
coords[cindex] = new PointDoubleXY(gf.makePrecise(0, tx),
gf.makePrecise(1, sy + (j + 1) * cy));
}
} else { // even horz
double cx = dx / npts;
if (i > 1) {
cx *= -1;
}
final double ty = coords[cindex - 1].getY();
final double sx = coords[cindex - 1].getX();
for (int j = 0; j < npts; j++) {
coords[cindex] = new PointDoubleXY(gf.makePrecise(0, sx + (j + 1) * cx),
gf.makePrecise(1, ty));
}
}
}
coords[npoints] = new PointDoubleXY(gf.makePrecise(0, x), gf.makePrecise(1, y)); // end
return gf.linearRing(coords);
}
private static Polygon newBox(final double x, final double dx, final double y, final double dy,
final int nholes, final int npoints, final GeometryFactory gf) {
// make outer ring first
final LinearRing outer = newBox(x, dx, y, dy, npoints, gf);
if (nholes == 0) {
return gf.polygon(outer);
}
final List<LinearRing> rings = new ArrayList<>();
rings.add(outer);
final int nrow = (int)Math.sqrt(nholes);
final int ncol = nholes / nrow;
final double ddx = dx / (ncol + 1);
final double ddy = dy / (nrow + 1);
// spacers
final double spx = ddx / (ncol + 1);
final double spy = ddy / (nrow + 1);
// should have more grids than required
int cindex = 0;
for (int i = 0; i < nrow; i++) {
for (int j = 0; j < ncol; j++) {
if (cindex < nholes) {
// make another box
int pts = npoints / 2;
pts = pts < 4 ? 4 : pts;
cindex++;
rings
.add(newBox(spx + x + j * (ddx + spx), ddx, spy + y + i * (ddy + spy), ddy, pts, gf));
}
}
}
return gf.polygon(rings);
}
private static LinearRing newTri(final double cx, final double cy, final int startAngle,
final int endAngle, final double radius, final GeometryFactory gf) {
final Point[] coords = new Point[4];
double fx1, fx2, fy1, fy2;
double angle = Math.toRadians(startAngle);
fx1 = Math.sin(angle) * radius; // may be neg.
fy1 = Math.cos(angle) * radius; // may be neg.
angle = Math.toRadians(endAngle);
fx2 = Math.sin(angle) * radius; // may be neg.
fy2 = Math.cos(angle) * radius; // may be neg.
final double cxp = gf.makePrecise(0, cx);
final double cyp = gf.makePrecise(1, cy);
return gf.linearRing(2, cxp, cyp, cx + fx1, cy + fy1, cx + fx2, cy + fy2, cxp, cyp);
}
protected int generationAlgorithm = 0;
protected int numberHoles = 0;
protected int numberPoints = 4;
/**
* @return Returns the generationAlgorithm.
*/
public int getGenerationAlgorithm() {
return this.generationAlgorithm;
}
/**
* @return Returns the numberHoles.
*/
public int getNumberHoles() {
return this.numberHoles;
}
/**
* @return Returns the numberPoints.
*/
public int getNumberPoints() {
return this.numberPoints;
}
/**
* As the user increases the number of points, the probability of creating a random valid polygon decreases.
* Please take not of this when selecting the generation style, and the number of points.
*
* May return null if a geometry could not be created.
*
* @see #getNumberPoints()
* @see #setNumberPoints(int)
* @see #getGenerationAlgorithm()
* @see #setGenerationAlgorithm(int)
*
* @see #BOX
* @see #ARC
*
* @see com.revolsys.geometry.testold.generator.GeometryGenerator#newIterator()
*
* @throws IllegalStateException When the alg is not valid or the number of points is invalid
* @throws NullPointerException when either the Geometry Factory, or the Bounding Box are undefined.
*/
@Override
public Geometry newGeometry() {
if (this.geometryFactory == null) {
throw new NullPointerException("GeometryFactoryI is not declared");
}
if (this.boundingBox == null || this.boundingBox.isEmpty()) {
throw new NullPointerException("Bounding Box is not declared");
}
if (this.numberPoints < 4) {
throw new IllegalStateException("Too few points");
}
final double x = this.boundingBox.getMinX(); // base x
final double dx = this.boundingBox.getMaxX() - x;
final double y = this.boundingBox.getMinY(); // base y
final double dy = this.boundingBox.getMaxY() - y;
Polygon p = null;
for (int i = 0; i < RUNS; i++) {
switch (getGenerationAlgorithm()) {
case BOX:
p = newBox(x, dx, y, dy, this.numberHoles, this.numberPoints, this.geometryFactory);
break;
case ARC:
p = newArc(x, dx, y, dy, this.numberHoles, this.numberPoints, this.geometryFactory);
break;
default:
throw new IllegalStateException("Invalid Alg. Specified");
}
final IsValidOp valid = new IsValidOp(p);
if (valid.isValid()) {
return p;
}
}
return null;
}
/**
* @param generationAlgorithm The generationAlgorithm to set.
*/
public void setGenerationAlgorithm(final int generationAlgorithm) {
this.generationAlgorithm = generationAlgorithm;
}
/**
* @param numberHoles The numberHoles to set.
*/
public void setNumberHoles(final int numberHoles) {
this.numberHoles = numberHoles;
}
/**
* @param numberPoints The numberPoints to set.
*/
public void setNumberPoints(final int numberPoints) {
this.numberPoints = numberPoints;
}
}