/*
* 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.elevation.tin.quadedge.intscale;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import com.revolsys.elevation.tin.IntArrayScaleTriangulatedIrregularNetwork;
import com.revolsys.elevation.tin.TriangleConsumer;
import com.revolsys.elevation.tin.TriangulatedIrregularNetwork;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.Point;
/**
* A utility class which creates Delaunay Trianglulations
* from collections of points and extract the resulting
* triangulation edges or triangles as geometries.
*
* @author Martin Davis
*
*/
public class IntQuadEdgeDelaunayTinBuilder {
private int[] bounds = new int[] {
Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE
};
private final List<PointIntXYZ> vertices = new ArrayList<>();
private QuadEdgeSubdivision subdivision;
private GeometryFactory geometryFactory;
private boolean sortVertices = false;
public IntQuadEdgeDelaunayTinBuilder(final GeometryFactory geometryFactory) {
if (geometryFactory == null) {
throw new NullPointerException("A geometryFactory must be specified");
} else {
this.geometryFactory = geometryFactory.convertAxisCount(3);
if (this.geometryFactory.getScaleX() == 0) {
throw new IllegalArgumentException("scaleX must not be 0");
}
if (this.geometryFactory.getScaleY() == 0) {
throw new IllegalArgumentException("scaleY must not be 0");
}
if (this.geometryFactory.getScaleZ() == 0) {
throw new IllegalArgumentException("scaleZ must not be 0");
}
}
}
public IntQuadEdgeDelaunayTinBuilder(final GeometryFactory geometryFactory, final int minX,
final int minY, final int maxX, final int maxY) {
this(geometryFactory);
this.bounds = new int[] {
minX, minY, maxX, maxY
};
}
public void addVertex(final PointIntXYZ vertex) {
this.vertices.add(vertex);
}
public void buildTin() {
if (this.subdivision == null) {
this.subdivision = new QuadEdgeSubdivision(this.bounds, this.geometryFactory);
insertVertices(this.subdivision, this.vertices);
}
}
public void forEachTriangle(final TriangleConsumer action) {
buildTin();
this.subdivision.forEachTriangle(action);
}
public void forEachTriangleInt(final TriangleConsumerInt action) {
buildTin();
this.subdivision.forEachTriangle(action);
}
public BoundingBox getBoundingBox() {
final double minX = this.geometryFactory.toDoubleX(this.bounds[0]);
final double minY = this.geometryFactory.toDoubleY(this.bounds[1]);
final double maxX = this.geometryFactory.toDoubleX(this.bounds[2]);
final double maxY = this.geometryFactory.toDoubleY(this.bounds[3]);
return this.geometryFactory.newBoundingBox(2, minX, minY, maxX, maxY);
}
public GeometryFactory getGeometryFactory() {
return this.geometryFactory;
}
/**
* Gets the {@link QuadEdgeSubdivision} which models the computed triangulation.
*
* @return the subdivision containing the triangulation
*/
public QuadEdgeSubdivision getSubdivision() {
buildTin();
return this.subdivision;
}
public void insertVertex(final int x, final int y, final int z) {
if (x < this.bounds[0]) {
this.bounds[0] = x;
}
if (x > this.bounds[2]) {
this.bounds[2] = x;
}
if (y < this.bounds[1]) {
this.bounds[1] = y;
}
if (y > this.bounds[3]) {
this.bounds[3] = y;
}
final PointIntXYZ vertex = new PointIntXYZ(x, y, z);
this.vertices.add(vertex);
if (this.subdivision != null) {
this.subdivision.insertVertex(vertex);
}
}
public void insertVertex(final Point point) {
final Point convertedPoint = point.convertPoint2d(this.geometryFactory);
final double x = convertedPoint.getX();
final double y = convertedPoint.getY();
final double z = point.getZ();
final int xInt = this.geometryFactory.toIntX(x);
final int yInt = this.geometryFactory.toIntY(y);
final int zInt = this.geometryFactory.toIntZ(z);
insertVertex(xInt, yInt, zInt);
}
protected void insertVertices(final QuadEdgeSubdivision subdivision,
final List<PointIntXYZ> vertices) {
if (this.sortVertices) {
Collections.sort(vertices);
}
subdivision.insertVertices(vertices);
}
public boolean isSortVertices() {
return this.sortVertices;
}
public TriangulatedIrregularNetwork newTriangulatedIrregularNetwork() {
buildTin();
final BoundingBox boundingBox = getBoundingBox();
final AtomicInteger triangleCounter = new AtomicInteger();
forEachTriangleInt((x1, y1, z1, x2, y2, z2, x3, y3, z3) -> {
triangleCounter.incrementAndGet();
});
final int triangleCount = triangleCounter.get();
final int[] triangleXCoordinates = new int[triangleCount * 3];
final int[] triangleYCoordinates = new int[triangleCount * 3];
final int[] triangleZCoordinates = new int[triangleCount * 3];
forEachTriangleInt(new TriangleConsumerInt() {
private int coordinateIndex = 0;
@Override
public void accept(final int x1, final int y1, final int z1, final int x2, final int y2,
final int z2, final int x3, final int y3, final int z3) {
triangleXCoordinates[this.coordinateIndex] = x1;
triangleYCoordinates[this.coordinateIndex] = y1;
triangleZCoordinates[this.coordinateIndex++] = z1;
triangleXCoordinates[this.coordinateIndex] = x2;
triangleYCoordinates[this.coordinateIndex] = y2;
triangleZCoordinates[this.coordinateIndex++] = z2;
triangleXCoordinates[this.coordinateIndex] = x3;
triangleYCoordinates[this.coordinateIndex] = y3;
triangleZCoordinates[this.coordinateIndex++] = z3;
}
});
return new IntArrayScaleTriangulatedIrregularNetwork(this.geometryFactory, boundingBox,
triangleCount, triangleXCoordinates, triangleYCoordinates, triangleZCoordinates);
}
public void setSortVertices(final boolean sortVertices) {
this.sortVertices = sortVertices;
}
}