package org.osm2world.core.heightmap.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.PolygonXYZ;
import org.osm2world.core.math.PolygonXZ;
import org.osm2world.core.math.SimplePolygonXZ;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
public abstract class AbstractCellularTerrainElevation implements
CellularTerrainElevation {
final int numPointsX;
final int numPointsZ;
final Collection<TerrainPoint> terrainPoints;
final TerrainPoint[][] terrainPointGrid;
@Override
public Collection<TerrainPoint> getTerrainPoints() {
return terrainPoints;
}
@Override
public TerrainPoint[][] getTerrainPointGrid() {
return terrainPointGrid;
}
@Override
public PolygonXYZ getBoundaryPolygon() {
List<VectorXYZ> vertices = new ArrayList<VectorXYZ>();
// first row
for (int x = 0; x < numPointsX; x++) {
vertices.add(vectorXYZForPointAt(x, 0));
}
// last column
for (int z = 1; z < numPointsZ - 1; z++) {
vertices.add(vectorXYZForPointAt(numPointsX - 1, z));
}
// last row
for (int x = numPointsX - 1; x >= 0; x--) {
vertices.add(vectorXYZForPointAt(x, numPointsZ - 1));
}
// first column
for (int z = numPointsZ - 2; z >= 0 /* [0][0] will be added again*/; z--) {
vertices.add(vectorXYZForPointAt(0, z));
}
return new PolygonXYZ(vertices);
}
@Override
public PolygonXZ getBoundaryPolygonXZ() {
List<VectorXZ> vertices = new ArrayList<VectorXZ>();
// first row
for (int x = 0; x < numPointsX; x++) {
vertices.add(vectorXZForPointAt(x, 0));
}
// last column
for (int z = 1; z < numPointsZ - 1; z++) {
vertices.add(vectorXZForPointAt(numPointsX - 1, z));
}
// last row
for (int x = numPointsX - 1; x >= 0; x--) {
vertices.add(vectorXZForPointAt(x, numPointsZ - 1));
}
// first column
for (int z = numPointsZ - 2; z >= 0 /* [0][0] will be added again*/; z--) {
vertices.add(vectorXZForPointAt(0, z));
}
return new PolygonXZ(vertices);
}
private VectorXYZ vectorXYZForPointAt(int x, int z) {
TerrainPoint point = terrainPointGrid[x][z];
return point.getPos().xyz(point.getEle());
}
private VectorXZ vectorXZForPointAt(int x, int z) {
TerrainPoint point = terrainPointGrid[x][z];
return point.getPos();
}
@Override
public Iterable<? extends TerrainElevationCell> getCells() {
return new Iterable<CellImpl>() {
@Override public Iterator<CellImpl> iterator() {
return new CellIterator();
}
};
}
//TODO (duplicated code): merge with independently written version from IntersectionGrid
private final class CellIterator implements Iterator<CellImpl> {
int currX = -1, currZ = 0;
@Override
public boolean hasNext() {
return currX + 1 < numPointsX-1 || currZ + 2 < numPointsZ-1;
}
@Override
public CellImpl next() {
currX += 1;
if (currX == numPointsX-1) {
currZ += 1;
currX = 0;
if (currZ == numPointsZ-1) {
throw new NoSuchElementException();
}
}
return new CellImpl(currX, currZ);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private final class CellImpl implements TerrainElevationCell {
private int leftXIndex;
private int bottomZIndex;
public CellImpl(int leftXIndex, int bottomZIndex) {
assert leftXIndex + 1 < numPointsX
&& bottomZIndex + 1 < numPointsZ;
this.leftXIndex = leftXIndex;
this.bottomZIndex = bottomZIndex;
}
@Override public final TerrainPoint getBottomLeft() {
return terrainPointGrid[leftXIndex][bottomZIndex];
}
@Override public final TerrainPoint getTopLeft() {
return terrainPointGrid[leftXIndex][bottomZIndex+1];
}
@Override public final TerrainPoint getBottomRight() {
return terrainPointGrid[leftXIndex+1][bottomZIndex];
}
@Override public final TerrainPoint getTopRight() {
return terrainPointGrid[leftXIndex+1][bottomZIndex+1];
}
@Override
public Collection<TerrainPoint> getTerrainPoints() {
List<TerrainPoint> terrainPoints = new ArrayList<TerrainPoint>(4);
terrainPoints.add(terrainPointGrid[leftXIndex][bottomZIndex]);
terrainPoints.add(terrainPointGrid[leftXIndex+1][bottomZIndex]);
terrainPoints.add(terrainPointGrid[leftXIndex+1][bottomZIndex+1]);
terrainPoints.add(terrainPointGrid[leftXIndex][bottomZIndex+1]);
return terrainPoints;
}
@Override
public final PolygonXYZ getPolygonXYZ() {
List<VectorXYZ> vertices = new ArrayList<VectorXYZ>(5);
vertices.add(vectorXYZForPointAt(leftXIndex, bottomZIndex));
vertices.add(vectorXYZForPointAt(leftXIndex+1, bottomZIndex));
vertices.add(vectorXYZForPointAt(leftXIndex+1, bottomZIndex+1));
vertices.add(vectorXYZForPointAt(leftXIndex, bottomZIndex+1));
vertices.add(vertices.get(0));
return new PolygonXYZ(vertices);
}
@Override
public final SimplePolygonXZ getPolygonXZ() {
List<VectorXZ> vertices = new ArrayList<VectorXZ>(5);
vertices.add(vectorXZForPointAt(leftXIndex, bottomZIndex));
vertices.add(vectorXZForPointAt(leftXIndex+1, bottomZIndex));
vertices.add(vectorXZForPointAt(leftXIndex+1, bottomZIndex+1));
vertices.add(vectorXZForPointAt(leftXIndex, bottomZIndex+1));
vertices.add(vertices.get(0));
return new SimplePolygonXZ(vertices);
}
@Override
public AxisAlignedBoundingBoxXZ getAxisAlignedBoundingBoxXZ() {
return new AxisAlignedBoundingBoxXZ(
Math.min(getTopLeft().getPos().x, getBottomLeft().getPos().x),
Math.min(getBottomLeft().getPos().z, getBottomRight().getPos().z),
Math.max(getTopRight().getPos().x, getBottomRight().getPos().x),
Math.max(getTopLeft().getPos().z, getTopRight().getPos().z));
}
@Override
public String toString() {
return "cell at x: " + leftXIndex + ", z: " + bottomZIndex;
}
/* auto-generated hashCode and equals follow */
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + bottomZIndex;
result = prime * result + leftXIndex;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CellImpl other = (CellImpl) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (bottomZIndex != other.bottomZIndex)
return false;
if (leftXIndex != other.leftXIndex)
return false;
return true;
}
private AbstractCellularTerrainElevation getOuterType() {
return AbstractCellularTerrainElevation.this;
}
}
public AbstractCellularTerrainElevation(AxisAlignedBoundingBoxXZ boundary,
int numPointsX, int numPointsZ) {
if (numPointsX < 2 || numPointsZ < 2) {
throw new IllegalArgumentException("need at least 2x2 points for cell grid");
}
this.numPointsX = numPointsX;
this.numPointsZ = numPointsZ;
terrainPoints = new ArrayList<TerrainPoint>(numPointsX * numPointsZ);
terrainPointGrid = new TerrainPoint[numPointsX][numPointsZ];
for (int x = 0; x < numPointsX; x++) {
for (int z = 0; z < numPointsZ; z++) {
VectorXZ pos = new VectorXZ(boundary.minX + x * boundary.sizeX()
/ (numPointsX - 1), boundary.minZ + z * boundary.sizeZ()
/ (numPointsZ - 1));
TerrainPoint terrainPoint = new TerrainPoint(pos,
getElevation(pos));
terrainPointGrid[x][z] = terrainPoint;
terrainPoints.add(terrainPoint);
}
}
}
protected abstract Float getElevation(VectorXZ pos);
}